From c72ba1935ee943656c42f72e394f26d5a9e4d578 Mon Sep 17 00:00:00 2001
From: Emanuele Dall'Ara <71103219+LeleDallas@users.noreply.github.com>
Date: Fri, 28 Feb 2025 12:02:08 +0100
Subject: [PATCH 1/4] chore: [PE-996] Accessibility for CGN loading component
(#6754)
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
## Short description
This pull request is improving accessibility for
`CgnActivationLoadingScreen` to its loading state
## List of changes proposed in this pull request
- Added a `useRef` hook and `useOnFirstRender` to set accessibility
focus on the `SafeAreaView` component when the `LoadingComponent` is
first rendered
## How to test
- Using a real device 📱, start the `CGN` card onboarding
- Turn on the TalkBack accessibility tool
- Press on `Attiva Carta Giovani Nazionale`
- Ensure that, when the loading state is appearing, the screen reader is
now reading the loading state
---
.../activation/CgnActivationLoadingScreen.tsx | 61 +++++++++++--------
.../CgnActivationLoadingScreen.test.tsx | 36 +++++++++++
2 files changed, 72 insertions(+), 25 deletions(-)
create mode 100644 ts/features/bonus/cgn/screens/activation/__tests__/CgnActivationLoadingScreen.test.tsx
diff --git a/ts/features/bonus/cgn/screens/activation/CgnActivationLoadingScreen.tsx b/ts/features/bonus/cgn/screens/activation/CgnActivationLoadingScreen.tsx
index 4e93b7da99c..fe396c96bef 100644
--- a/ts/features/bonus/cgn/screens/activation/CgnActivationLoadingScreen.tsx
+++ b/ts/features/bonus/cgn/screens/activation/CgnActivationLoadingScreen.tsx
@@ -1,4 +1,3 @@
-import { StyleSheet, View } from "react-native";
import {
Body,
ContentWrapper,
@@ -6,13 +5,17 @@ import {
IOStyles,
VSpacer
} from "@pagopa/io-app-design-system";
+import { useRef } from "react";
+import { StyleSheet, View } from "react-native";
import { SafeAreaView } from "react-native-safe-area-context";
-import { isCgnActivationLoading } from "../../store/reducers/activation";
-import { cgnActivationCancel } from "../../store/actions/activation";
+import { OperationResultScreenContent } from "../../../../../components/screens/OperationResultScreenContent";
+import { LoadingIndicator } from "../../../../../components/ui/LoadingIndicator";
import I18n from "../../../../../i18n";
import { useIODispatch, useIOSelector } from "../../../../../store/hooks";
-import { LoadingIndicator } from "../../../../../components/ui/LoadingIndicator";
-import { OperationResultScreenContent } from "../../../../../components/screens/OperationResultScreenContent";
+import { setAccessibilityFocus } from "../../../../../utils/accessibility";
+import { useOnFirstRender } from "../../../../../utils/hooks/useOnFirstRender";
+import { cgnActivationCancel } from "../../store/actions/activation";
+import { isCgnActivationLoading } from "../../store/reducers/activation";
const styles = StyleSheet.create({
container: {
@@ -28,27 +31,35 @@ const styles = StyleSheet.create({
}
});
-const LoadingComponent = () => (
-
-
-
-
-
+const LoadingComponent = () => {
+ const ref = useRef(null);
+
+ useOnFirstRender(() => {
+ setAccessibilityFocus(ref);
+ });
+
+ return (
+
+
+
+
+
+
+
+
+ {I18n.t("bonus.cgn.activation.loading.caption")}
+
+
+ {I18n.t("bonus.cgn.activation.loading.subCaption")}
-
-
- {I18n.t("bonus.cgn.activation.loading.caption")}
-
-
- {I18n.t("bonus.cgn.activation.loading.subCaption")}
-
-
-
-);
+
+
+ );
+};
const ErrorComponent = () => {
const dispatch = useIODispatch();
diff --git a/ts/features/bonus/cgn/screens/activation/__tests__/CgnActivationLoadingScreen.test.tsx b/ts/features/bonus/cgn/screens/activation/__tests__/CgnActivationLoadingScreen.test.tsx
new file mode 100644
index 00000000000..57216f2bd96
--- /dev/null
+++ b/ts/features/bonus/cgn/screens/activation/__tests__/CgnActivationLoadingScreen.test.tsx
@@ -0,0 +1,36 @@
+import { createStore } from "redux";
+import I18n from "../../../../../../i18n";
+import { applicationChangeState } from "../../../../../../store/actions/application";
+import { Store } from "../../../../../../store/actions/types";
+import { appReducer } from "../../../../../../store/reducers";
+import { GlobalState } from "../../../../../../store/reducers/types";
+import { renderScreenWithNavigationStoreContext } from "../../../../../../utils/testWrapper";
+import CGN_ROUTES from "../../../navigation/routes";
+import { cgnRequestActivation } from "../../../store/actions/activation";
+import CgnActivationLoadingScreen from "../CgnActivationLoadingScreen";
+
+const renderComponent = (store: Store) =>
+ renderScreenWithNavigationStoreContext(
+ () => ,
+ CGN_ROUTES.ACTIVATION.LOADING,
+ {},
+ store
+ );
+
+describe("CgnActivationLoadingScreen", () => {
+ it("renders LoadingComponent when isLoading is true", () => {
+ const globalState = appReducer(undefined, applicationChangeState("active"));
+ const store = createStore(appReducer, globalState as any);
+
+ const { getByText, getByA11yRole } = renderComponent(store);
+
+ store.dispatch(cgnRequestActivation());
+ expect(getByA11yRole("header")).toBeTruthy();
+ expect(
+ getByText(I18n.t("bonus.cgn.activation.loading.caption"))
+ ).toBeTruthy();
+ expect(
+ getByText(I18n.t("bonus.cgn.activation.loading.subCaption"))
+ ).toBeTruthy();
+ });
+});
From a49b20e1e5315c826dda74f9be8746a3e7247e16 Mon Sep 17 00:00:00 2001
From: Emanuele Dall'Ara <71103219+LeleDallas@users.noreply.github.com>
Date: Fri, 28 Feb 2025 15:32:06 +0100
Subject: [PATCH 2/4] feat: [PE-988,PE-919,IOBP-1193] CGN merchants swipe back,
`IOListViewWithLargeHeader ` (#6764)
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
## Short description
This pull request introduces a new `IOListViewWithLargeHeader` component
and applies it to the CGN merchants screen. The primary goal is to
resolve a swipe-back issue affecting this screen.
> [!NOTE]
> This PR depends on
https://github.com/pagopa/io-app-design-system/pull/417
> Otherwise you will see the Tabs not aligned...
> [!TIP]
> Design System has been updated to `v5.0.3`, addressing the
accessibility (`a11y`) issue with `IOScroll` header visibility
Key Features of
IOListViewWithLargeHeader
## Key Features of `IOListViewWithLargeHeader`
This component provides a structured flat list with a large header,
supporting lists and additional UI elements. It is designed to be
reusable across different screens that require a list-based layout with
a header.
### Props and Their Functionality (List & Data Handling - Refresh &
Loading Behavior)
- `data (Required)`: The array of items that will be displayed in the
scrollable list. When empty, the `listEmptyComponent` is displayed as a
fallback;
- `renderItem (Required)`: A function that takes an item and its index
as arguments and returns a `JSX` element to render each list item;
- `keyExtractor (Required)`: Used to extract a unique key for a given
item at the specified index. Key is used for caching and as the react
key to track item re-ordering;
- `ListEmptyComponent (Optional)`: Defines a fallback component shown
when data is empty. Useful for displaying "No items available" messages
or placeholders;
- `ListHeaderComponent (Optional)`: Allows adding a custom component
before the list items (e.g., filters, introductory text);
- `ListFooterComponent (Optional)`: Allows adding a custom component at
the end of the list (e.g., buttons, summary info);
- `refreshControlProps (Required)`: Manages pull-to-refresh behavior,
allowing users to refresh the list contents. Uses React Native’s
`RefreshControl` props;
`skeleton (Optional)`: A loading skeleton displayed while
`refreshControlProps.refreshing` is `true`. Helps improve the user
experience by showing a placeholder instead of a blank screen.
___
## List of changes proposed in this pull request
- Introduced a new `IOListViewWithLargeHeader` component to handle large
headers with lists, including props for customization and rendering
items
- Refactored the categories list screen to use the new
`IOListViewWithLargeHeader` component
- Updated the categories selection screen to use state management for
tab selection
### Minor Changes:
- Added a `count` prop to customize the number of skeleton items
displayed, providing better flexibility for loading states
## How to test
Describe the tests that you ran to verify your changes. Provide
instructions so we can reproduce.
## Preview
| Design System | CGN |CGN Swipe action|
|--------|--------|--------|
| | | |
---
package.json | 2 +-
.../LoadingSpinnerOverlay.test.tsx.snap | 3 +-
...OperationResultScreenContent.test.tsx.snap | 3 +-
ts/components/ui/IOListView.tsx | 278 +++
.../ui/IOListViewWithLargeHeader.tsx | 159 ++
ts/components/ui/IOScrollView.tsx | 2 +-
ts/components/ui/__test__/IOListView.test.tsx | 47 +
.../IOScrollViewCentredContent.test.tsx.snap | 112 +-
.../IOScrollViewWithListItems.test.tsx.snap | 21 +-
.../merchants/CgnMerchantListSkeleton.tsx | 5 +-
.../CgnMerchantListSkeleton.test.tsx | 24 +
.../ModuleCgnDiscount.test.tsx | 0
.../CGNDiscountExpiredScreen.test.tsx.snap | 3 +-
.../CgnDiscountCodeScreen.test.tsx.snap | 3 +-
.../CgnMerchantCategoriesListScreen.tsx | 77 +-
.../CgnMerchantsCategoriesSelectionScreen.tsx | 160 +-
.../merchants/CgnMerchantsListScreen.tsx | 158 +-
...erchantsCategoriesSelectionScreen.test.tsx | 53 +-
...ntsCategoriesSelectionScreen.test.tsx.snap | 2137 +++++++++--------
.../CieIdErrorScreen.test.tsx.snap | 6 +-
.../CieIdNotInstalled.test.tsx.snap | 3 +-
.../CieIdNotInstalledScreen.test.tsx.snap | 3 +-
.../__snapshots__/wizards.test.tsx.snap | 28 +-
.../CieIdAuthUrlError.test.tsx.snap | 3 +-
.../CieIdLoginScreen.test.tsx.snap | 3 +-
ts/features/design-system/DesignSystem.tsx | 6 +-
.../core/DSIOListViewWithLargeHeader.tsx | 109 +
.../DSIOListViewWithLargeHeader.test.tsx | 24 +
.../DSIOListViewWithLargeHeader.test.tsx.snap | 1808 ++++++++++++++
.../design-system/navigation/navigator.tsx | 9 +-
.../design-system/navigation/params.ts | 1 +
.../design-system/navigation/routes.ts | 4 +
.../FimsUpdateAppAlert.test.tsx.snap | 3 +-
.../FimsHistoryKoScreen.test.tsx.snap | 3 +-
.../FimsHistoryNonEmptyContent.test.tsx.snap | 42 +-
...ntialAsyncContinuationScreen.test.tsx.snap | 12 +-
.../__snapshots__/EmptyList.test.tsx.snap | 6 +-
.../PreconditionsFooter.test.tsx.snap | 14 +-
.../TabNavigationContainer.test.tsx.snap | 4 +-
.../MessageDetailsPaymentButton.test.tsx.snap | 14 +-
.../MessageDetailsStickyFooter.test.tsx.snap | 49 +-
.../MessageAttachment.test.tsx.snap | 11 +-
.../MessageGreenPassScreen.test.tsx.snap | 3 +-
.../MessageRouterScreen.test.tsx.snap | 13 +-
.../MessagesHomeScreen.test.tsx.snap | 8 +-
...PaymentInputFiscalCodeScreen.test.tsx.snap | 7 +-
...ymentInputNoticeNumberScreen.test.tsx.snap | 7 +-
.../WalletPaymentOutcomeScreen.test.tsx.snap | 135 +-
.../__snapshots__/MessageFooter.test.tsx.snap | 14 +-
.../MessageDetailsScreen.test.tsx.snap | 4 +-
.../PaidPaymentScreen.test.tsx.snap | 5 +-
...tificationsInfoScreenConsent.test.tsx.snap | 7 +-
...tificationsPreferencesScreen.test.tsx.snap | 45 +-
...otificationPermissionsScreen.test.tsx.snap | 7 +-
.../ServiceDetailsScreenCgn.test.tsx.snap | 22 +-
.../ServiceDetailsScreenDefault.test.tsx.snap | 20 +-
.../ServiceDetailsScreenPn.test.tsx.snap | 31 +-
.../WalletEmptyScreenContent.test.tsx.snap | 7 +-
ts/hooks/useScrollHeaderAnimation.tsx | 122 +
.../__snapshots__/LandingScreen.test.tsx.snap | 16 +-
.../ActivateNfcScreen.test.tsx.snap | 7 +-
yarn.lock | 10 +-
62 files changed, 4427 insertions(+), 1475 deletions(-)
create mode 100644 ts/components/ui/IOListView.tsx
create mode 100644 ts/components/ui/IOListViewWithLargeHeader.tsx
create mode 100644 ts/components/ui/__test__/IOListView.test.tsx
create mode 100644 ts/features/bonus/cgn/components/merchants/__tests__/CgnMerchantListSkeleton.test.tsx
rename ts/features/bonus/cgn/components/merchants/{___tests___ => __tests__}/ModuleCgnDiscount.test.tsx (100%)
create mode 100644 ts/features/design-system/core/DSIOListViewWithLargeHeader.tsx
create mode 100644 ts/features/design-system/core/__tests__/DSIOListViewWithLargeHeader.test.tsx
create mode 100644 ts/features/design-system/core/__tests__/__snapshots__/DSIOListViewWithLargeHeader.test.tsx.snap
create mode 100644 ts/hooks/useScrollHeaderAnimation.tsx
diff --git a/package.json b/package.json
index e3c7aaa711a..8543d98b3ab 100644
--- a/package.json
+++ b/package.json
@@ -57,7 +57,7 @@
"@babel/plugin-transform-private-property-in-object": "^7.25.9",
"@babel/plugin-transform-react-jsx": "^7.25.9",
"@gorhom/bottom-sheet": "^4.1.5",
- "@pagopa/io-app-design-system": "5.0.2",
+ "@pagopa/io-app-design-system": "5.0.3",
"@pagopa/io-pagopa-commons": "^3.1.0",
"@pagopa/io-react-native-cieid": "^0.3.5",
"@pagopa/io-react-native-crypto": "^1.0.1",
diff --git a/ts/components/__tests__/__snapshots__/LoadingSpinnerOverlay.test.tsx.snap b/ts/components/__tests__/__snapshots__/LoadingSpinnerOverlay.test.tsx.snap
index fb9a32e0766..965d4744f6d 100644
--- a/ts/components/__tests__/__snapshots__/LoadingSpinnerOverlay.test.tsx.snap
+++ b/ts/components/__tests__/__snapshots__/LoadingSpinnerOverlay.test.tsx.snap
@@ -291,7 +291,8 @@ exports[`LoadingSpinnerOverlay Should match all-properties and loading snapshot
onStartShouldSetResponder={[Function]}
style={
{
- "alignSelf": "flex-start",
+ "alignSelf": "auto",
+ "flexShrink": 1,
}
}
testID="loadingSpinnerOverlayCancelButton"
diff --git a/ts/components/screens/__tests__/__snapshots__/OperationResultScreenContent.test.tsx.snap b/ts/components/screens/__tests__/__snapshots__/OperationResultScreenContent.test.tsx.snap
index 0a7cf081e17..edc5221f3ab 100644
--- a/ts/components/screens/__tests__/__snapshots__/OperationResultScreenContent.test.tsx.snap
+++ b/ts/components/screens/__tests__/__snapshots__/OperationResultScreenContent.test.tsx.snap
@@ -479,7 +479,8 @@ exports[`OperationResultScreenContent should match the snapshot with default pro
onStartShouldSetResponder={[Function]}
style={
{
- "alignSelf": "flex-start",
+ "alignSelf": "auto",
+ "flexShrink": 1,
}
}
>
diff --git a/ts/components/ui/IOListView.tsx b/ts/components/ui/IOListView.tsx
new file mode 100644
index 00000000000..255107f0e36
--- /dev/null
+++ b/ts/components/ui/IOListView.tsx
@@ -0,0 +1,278 @@
+/* eslint-disable sonarjs/cognitive-complexity */
+
+import {
+ IOColors,
+ IOSpacingScale,
+ IOVisualCostants,
+ hexToRgba,
+ useIOTheme
+} from "@pagopa/io-app-design-system";
+
+import { ComponentProps, ReactElement, useState } from "react";
+
+import {
+ ColorValue,
+ LayoutChangeEvent,
+ LayoutRectangle,
+ ListRenderItemInfo,
+ RefreshControl,
+ StyleSheet,
+ View
+} from "react-native";
+import LinearGradient from "react-native-linear-gradient";
+import Animated, { AnimatedRef } from "react-native-reanimated";
+import { useSafeAreaInsets } from "react-native-safe-area-context";
+
+import { useScrollHeaderAnimation } from "../../hooks/useScrollHeaderAnimation";
+import {
+ IOScrollView,
+ IOScrollViewActions,
+ renderActionButtons
+} from "./IOScrollView";
+
+export type IOListViewActions = IOScrollViewActions;
+
+type IOListView = ComponentProps &
+ ComponentProps> & {
+ data: Array;
+ renderItem: (item: ListRenderItemInfo) => ReactElement | null;
+ keyExtractor: ((item: T, index: number) => string) | undefined;
+ animatedRef?: AnimatedRef>;
+ skeleton?: ReactElement;
+ loading?: boolean;
+ };
+
+/* Extended gradient area above the actions */
+const gradientSafeAreaHeight: IOSpacingScale = 96;
+/* End content margin before the actions */
+const contentEndMargin: IOSpacingScale = 32;
+/* Extra bottom margin for iPhone bottom handle because
+ ButtonLink doesn't have a fixed height */
+const extraSafeAreaMargin: IOSpacingScale = 8;
+
+const styles = StyleSheet.create({
+ gradientBottomActions: {
+ width: "100%",
+ position: "absolute",
+ bottom: 0,
+ justifyContent: "flex-end"
+ },
+ gradientContainer: {
+ ...StyleSheet.absoluteFillObject
+ },
+ buttonContainer: {
+ paddingHorizontal: IOVisualCostants.appMarginDefault,
+ width: "100%",
+ flexShrink: 0
+ },
+ centerContentWrapper: {
+ flexGrow: 1,
+ alignItems: "stretch",
+ justifyContent: "center",
+ alignContent: "center"
+ }
+});
+
+/**
+ * The main scrollable container component.
+ * It includes full support for custom headers and actions.
+ *
+ * @param [headerConfig] Configuration for the header component. Use this only if you need to configure a custom header from scratch.
+ * If you need the predefined configuration with default `Back (<)` and `Help (?)` buttons, use `useHeaderSecondLevel`
+ * @param {IOListViewActions} [actions] Actions to be rendered at the bottom of the `FlatList`
+ * @param [animatedRef] Ref generated through `useAnimatedRef` (used by `useFlatListOffset` to get the scroll position)
+ * @param {number} [snapOffset] Offset when you need to add a snap point
+ * @param {boolean} [excludeSafeAreaMargins=false] Exclude safe area margins at the bottom of the `FlatList`
+ * This is useful if you have a screen with a tab bar at the bottom, or if the bottom margin is already being managed
+ * @param {boolean} [excludeEndContentMargin=false] Exclude the end content margin
+ * @param {boolean} [includeContentMargins=true] Include horizontal screen margins
+ * @param {boolean} [debugMode=false] Enable debug mode. Only for testing purposes
+ */
+export const IOListView = ({
+ headerConfig,
+ data,
+ renderItem,
+ keyExtractor,
+ actions,
+ snapOffset,
+ excludeSafeAreaMargins = false,
+ excludeEndContentMargin = false,
+ includeContentMargins = true,
+ debugMode = false,
+ animatedRef,
+ centerContent,
+ refreshControlProps,
+ contentContainerStyle,
+ ListHeaderComponent,
+ ListFooterComponent,
+ ListEmptyComponent,
+ skeleton,
+ ItemSeparatorComponent,
+ testID,
+ loading
+}: IOListView) => {
+ const theme = useIOTheme();
+
+ /* Total height of actions */
+ const [actionBlockHeight, setActionBlockHeight] =
+ useState(0);
+
+ const getActionBlockHeight = (event: LayoutChangeEvent) => {
+ setActionBlockHeight(event.nativeEvent.layout.height);
+ };
+
+ const insets = useSafeAreaInsets();
+ const needSafeAreaMargin = insets.bottom !== 0;
+
+ /* Check if the iPhone bottom handle is present.
+ If not, or if you don't need safe area insets,
+ add a default margin to prevent the button
+ from sticking to the bottom. */
+ const bottomMargin =
+ !needSafeAreaMargin || excludeSafeAreaMargins
+ ? IOVisualCostants.appMarginDefault
+ : insets.bottom;
+
+ /* GENERATE EASING GRADIENT
+ Background color should be app main background
+ (both light and dark themes) */
+ const HEADER_BG_COLOR: ColorValue = IOColors[theme["appBackground-primary"]];
+
+ /* When the secondary action is visible, add extra margin
+ to avoid little space from iPhone bottom handle */
+ const extraBottomMargin =
+ actions?.secondary && needSafeAreaMargin ? extraSafeAreaMargin : 0;
+
+ /* Safe background block. Cover at least 85% of the space
+ to avoid glitchy elements underneath */
+ const safeBackgroundBlockHeight = (bottomMargin + actionBlockHeight) * 0.85;
+
+ /* Total height of "Actions + Gradient" area */
+ const gradientAreaHeight =
+ bottomMargin + actionBlockHeight + gradientSafeAreaHeight;
+
+ /* Height of the safe bottom area, applied to the ScrollView:
+ Actions + Content end margin */
+ const safeBottomAreaHeight =
+ bottomMargin + actionBlockHeight + contentEndMargin;
+
+ const { colors, handleScroll, locations, opacityTransition } =
+ useScrollHeaderAnimation({ snapOffset, headerConfig });
+
+ const RefreshControlComponent = refreshControlProps && (
+
+ );
+
+ return (
+
+ ListHeaderComponent={ListHeaderComponent}
+ ItemSeparatorComponent={ItemSeparatorComponent}
+ ref={animatedRef}
+ keyExtractor={keyExtractor}
+ data={data}
+ renderItem={item =>
+ // If the refresh control is active, show the skeleton (if present) instead of the content
+ loading || refreshControlProps?.refreshing
+ ? skeleton ?? null
+ : renderItem(item)
+ }
+ testID={testID}
+ onScroll={handleScroll}
+ ListEmptyComponent={
+ (loading || refreshControlProps?.refreshing) && skeleton
+ ? skeleton
+ : ListEmptyComponent
+ }
+ refreshing={refreshControlProps?.refreshing}
+ scrollEventThrottle={8}
+ snapToOffsets={
+ // If there is a refresh control, don't snap to offsets
+ // This is a react-native bug: https://github.com/facebook/react-native/issues/27324
+ RefreshControlComponent ? undefined : [0, snapOffset ?? 0]
+ }
+ snapToEnd={false}
+ decelerationRate="normal"
+ refreshControl={RefreshControlComponent}
+ centerContent={centerContent}
+ ListFooterComponent={
+ <>
+ {ListFooterComponent}
+ {actions && (
+
+
+
+
+
+
+ {/* Safe background block. It's added because when you swipe up
+ quickly, the content below is visible for about 100ms. Without this
+ block, the content appears glitchy. */}
+
+
+
+ {renderActionButtons(actions, extraBottomMargin)}
+
+
+ )}
+ >
+ }
+ contentContainerStyle={[
+ {
+ paddingBottom: excludeEndContentMargin
+ ? 0
+ : actions
+ ? safeBottomAreaHeight
+ : bottomMargin + contentEndMargin,
+ paddingHorizontal: includeContentMargins
+ ? IOVisualCostants.appMarginDefault
+ : 0,
+ ...(contentContainerStyle || {})
+ },
+ centerContent ? styles.centerContentWrapper : {}
+ ]}
+ />
+ );
+};
diff --git a/ts/components/ui/IOListViewWithLargeHeader.tsx b/ts/components/ui/IOListViewWithLargeHeader.tsx
new file mode 100644
index 00000000000..1c8c114bbd1
--- /dev/null
+++ b/ts/components/ui/IOListViewWithLargeHeader.tsx
@@ -0,0 +1,159 @@
+import {
+ Body,
+ ComposedBodyFromArray,
+ H2,
+ HeaderSecondLevel,
+ BodySmall,
+ VSpacer,
+ useIOTheme,
+ Divider,
+ BodyProps
+} from "@pagopa/io-app-design-system";
+import { useNavigation } from "@react-navigation/native";
+import { ComponentProps, useState, forwardRef } from "react";
+
+import { LayoutChangeEvent, View } from "react-native";
+import { useHeaderProps } from "../../hooks/useHeaderProps";
+import I18n from "../../i18n";
+import { IOScrollViewWithLargeHeader } from "./IOScrollViewWithLargeHeader";
+import { IOListView } from "./IOListView";
+
+type Props = ComponentProps> &
+ ComponentProps & {
+ subtitle?: string | Array;
+ };
+
+/**
+ * Special `IOListView` screen with a large title that is hidden by a transition when
+ * the user scrolls. It also handles the contextual help and the FAQ.
+ * Use of LargeHeader naming is due to similar behavior offered by the native iOS API.
+ */
+export const IOListViewWithLargeHeader = forwardRef(
+ (
+ {
+ renderItem,
+ data,
+ keyExtractor,
+ title,
+ description,
+ subtitle,
+ actions,
+ goBack,
+ canGoback = true,
+ contextualHelp,
+ contextualHelpMarkdown,
+ faqCategories,
+ ignoreSafeAreaMargin = false,
+ refreshControlProps,
+ includeContentMargins = true,
+ headerActionsProp = {},
+ excludeEndContentMargin,
+ skeleton,
+ ListHeaderComponent,
+ ListFooterComponent,
+ ListEmptyComponent,
+ testID,
+ ignoreAccessibilityCheck = false,
+ loading
+ }: Props,
+ ref: React.Ref
+ ) => {
+ const [titleHeight, setTitleHeight] = useState(0);
+
+ const navigation = useNavigation();
+ const theme = useIOTheme();
+
+ const getTitleHeight = (event: LayoutChangeEvent) => {
+ const { height } = event.nativeEvent.layout;
+ setTitleHeight(height);
+ };
+
+ const headerPropsWithoutGoBack = {
+ title: title.label,
+ contextualHelp,
+ contextualHelpMarkdown,
+ faqCategories,
+ ...headerActionsProp
+ };
+
+ const headerProps: ComponentProps = {
+ ignoreSafeAreaMargin,
+ ignoreAccessibilityCheck,
+ ...useHeaderProps(
+ canGoback
+ ? {
+ ...headerPropsWithoutGoBack,
+ backAccessibilityLabel: I18n.t("global.buttons.back"),
+ goBack: goBack ?? navigation.goBack
+ }
+ : headerPropsWithoutGoBack
+ )
+ };
+
+ return (
+
+ ListHeaderComponent={
+ <>
+
+ {title.section && (
+
+ {title.section}
+
+ )}
+
+ {title.label}
+
+
+
+ {description && (
+ <>
+
+ {typeof description === "string" ? (
+ {description}
+ ) : (
+
+ )}
+ >
+ )}
+ {subtitle && (
+ <>
+
+ {typeof subtitle === "string" ? (
+ {subtitle}
+ ) : (
+
+ )}
+ >
+ )}
+ {ListHeaderComponent && (
+ <>
+
+ {ListHeaderComponent}
+ >
+ )}
+ >
+ }
+ loading={loading}
+ skeleton={skeleton}
+ ListFooterComponent={ListFooterComponent}
+ ListEmptyComponent={ListEmptyComponent}
+ ItemSeparatorComponent={Divider}
+ refreshControlProps={refreshControlProps}
+ renderItem={renderItem}
+ data={data}
+ keyExtractor={keyExtractor}
+ actions={actions}
+ headerConfig={headerProps}
+ snapOffset={titleHeight}
+ includeContentMargins={includeContentMargins}
+ excludeEndContentMargin={excludeEndContentMargin}
+ testID={testID}
+ />
+ );
+ }
+) as (props: Props & { ref?: React.Ref }) => JSX.Element;
diff --git a/ts/components/ui/IOScrollView.tsx b/ts/components/ui/IOScrollView.tsx
index 1d2c3c22fde..e72121bce0e 100644
--- a/ts/components/ui/IOScrollView.tsx
+++ b/ts/components/ui/IOScrollView.tsx
@@ -387,7 +387,7 @@ export const IOScrollView = ({
);
};
-const renderActionButtons = (
+export const renderActionButtons = (
actions: IOScrollViewActions,
extraBottomMargin: number
) => {
diff --git a/ts/components/ui/__test__/IOListView.test.tsx b/ts/components/ui/__test__/IOListView.test.tsx
new file mode 100644
index 00000000000..556e7e67642
--- /dev/null
+++ b/ts/components/ui/__test__/IOListView.test.tsx
@@ -0,0 +1,47 @@
+import { Body } from "@pagopa/io-app-design-system";
+import { ComponentProps } from "react";
+import { createStore } from "redux";
+import ROUTES from "../../../navigation/routes";
+import { applicationChangeState } from "../../../store/actions/application";
+import { appReducer } from "../../../store/reducers";
+import { GlobalState } from "../../../store/reducers/types";
+import { renderScreenWithNavigationStoreContext } from "../../../utils/testWrapper";
+import { IOListView } from "../IOListView";
+
+type Data = { id: string; name: string };
+const renderComponent = (props: ComponentProps>) => {
+ const globalState = appReducer(undefined, applicationChangeState("active"));
+ return renderScreenWithNavigationStoreContext(
+ () => ,
+ ROUTES.WALLET_HOME,
+ {},
+ createStore(appReducer, globalState as any)
+ );
+};
+
+const defaultProps: ComponentProps> = {
+ data: [
+ { id: "1", name: "Item 1" },
+ { id: "2", name: "Item 2" }
+ ],
+ keyExtractor: item => item.id,
+ renderItem: ({ item }) => {item.name}
+};
+
+describe("IOListView", () => {
+ it("should render correctly", () => {
+ const { getByText } = renderComponent(defaultProps);
+ expect(getByText("Item 1")).toBeTruthy();
+ expect(getByText("Item 2")).toBeTruthy();
+ });
+
+ it("should render loading state", () => {
+ const props = {
+ ...defaultProps,
+ skeleton: Skeleton,
+ loading: true
+ };
+ const { getAllByText } = renderComponent(props);
+ expect(getAllByText("Skeleton")).toHaveLength(2);
+ });
+});
diff --git a/ts/components/ui/__test__/__snapshots__/IOScrollViewCentredContent.test.tsx.snap b/ts/components/ui/__test__/__snapshots__/IOScrollViewCentredContent.test.tsx.snap
index c77b43cffe2..563c039973b 100644
--- a/ts/components/ui/__test__/__snapshots__/IOScrollViewCentredContent.test.tsx.snap
+++ b/ts/components/ui/__test__/__snapshots__/IOScrollViewCentredContent.test.tsx.snap
@@ -839,7 +839,12 @@ exports[`IOScrollViewCentredContent should match snapshot, with description,
onResponderTerminate={[Function]}
onResponderTerminationRequest={[Function]}
onStartShouldSetResponder={[Function]}
- style={{}}
+ style={
+ {
+ "alignSelf": "stretch",
+ "flexShrink": 0,
+ }
+ }
>
- {new Array(6).fill(null).map((_, index) => (
+ {new Array(count).fill(null).map((_, index) => (
{
+ it("should render the correct number of skeleton items", () => {
+ const { getAllByTestId } = render(
+
+ );
+ const items = getAllByTestId(/CgnMerchantListSkeleton-Item-/);
+ expect(items.length).toBe(3);
+ });
+
+ it("should render the default number of skeleton items", () => {
+ const { getAllByTestId } = render();
+ const items = getAllByTestId(/CgnMerchantListSkeleton-Item-/);
+ expect(items.length).toBe(6);
+ });
+
+ it("should render placeholders with correct testID", () => {
+ const { getByTestId } = render();
+ const placeholder = getByTestId("CgnMerchantListSkeleton-Placeholder-0");
+ expect(placeholder).toBeTruthy();
+ });
+});
diff --git a/ts/features/bonus/cgn/components/merchants/___tests___/ModuleCgnDiscount.test.tsx b/ts/features/bonus/cgn/components/merchants/__tests__/ModuleCgnDiscount.test.tsx
similarity index 100%
rename from ts/features/bonus/cgn/components/merchants/___tests___/ModuleCgnDiscount.test.tsx
rename to ts/features/bonus/cgn/components/merchants/__tests__/ModuleCgnDiscount.test.tsx
diff --git a/ts/features/bonus/cgn/screens/discount/___tests___/__snapshots__/CGNDiscountExpiredScreen.test.tsx.snap b/ts/features/bonus/cgn/screens/discount/___tests___/__snapshots__/CGNDiscountExpiredScreen.test.tsx.snap
index f0315d0c683..ded2151bfcb 100644
--- a/ts/features/bonus/cgn/screens/discount/___tests___/__snapshots__/CGNDiscountExpiredScreen.test.tsx.snap
+++ b/ts/features/bonus/cgn/screens/discount/___tests___/__snapshots__/CGNDiscountExpiredScreen.test.tsx.snap
@@ -632,7 +632,8 @@ exports[`CGNDiscountExpiredScreen should render correctly 1`] = `
onStartShouldSetResponder={[Function]}
style={
{
- "alignSelf": "flex-start",
+ "alignSelf": "auto",
+ "flexShrink": 1,
}
}
testID="close-button"
diff --git a/ts/features/bonus/cgn/screens/discount/___tests___/__snapshots__/CgnDiscountCodeScreen.test.tsx.snap b/ts/features/bonus/cgn/screens/discount/___tests___/__snapshots__/CgnDiscountCodeScreen.test.tsx.snap
index a6e20b4b066..7428f82a393 100644
--- a/ts/features/bonus/cgn/screens/discount/___tests___/__snapshots__/CgnDiscountCodeScreen.test.tsx.snap
+++ b/ts/features/bonus/cgn/screens/discount/___tests___/__snapshots__/CgnDiscountCodeScreen.test.tsx.snap
@@ -613,7 +613,8 @@ exports[`CgnDiscountCodeScreen should render correctly 1`] = `
onStartShouldSetResponder={[Function]}
style={
{
- "alignSelf": "flex-start",
+ "alignSelf": "auto",
+ "flexShrink": 1,
}
}
testID="generate-button"
diff --git a/ts/features/bonus/cgn/screens/merchants/CgnMerchantCategoriesListScreen.tsx b/ts/features/bonus/cgn/screens/merchants/CgnMerchantCategoriesListScreen.tsx
index fc74ac96343..c1319d5cb18 100644
--- a/ts/features/bonus/cgn/screens/merchants/CgnMerchantCategoriesListScreen.tsx
+++ b/ts/features/bonus/cgn/screens/merchants/CgnMerchantCategoriesListScreen.tsx
@@ -3,7 +3,6 @@ import {
Body,
Divider,
H6,
- IOStyles,
IOToast,
ListItemAction,
ListItemNav
@@ -13,7 +12,7 @@ import { useNavigation } from "@react-navigation/native";
import { pipe } from "fp-ts/lib/function";
import * as O from "fp-ts/lib/Option";
import { useEffect, useMemo, useState } from "react";
-import { FlatList, RefreshControl, View } from "react-native";
+import { View } from "react-native";
import { useSafeAreaInsets } from "react-native-safe-area-context";
import { ProductCategoryWithNewDiscountsCount } from "../../../../../../definitions/cgn/merchants/ProductCategoryWithNewDiscountsCount";
import I18n from "../../../../../i18n";
@@ -75,19 +74,17 @@ export const CgnMerchantCategoriesListScreen = () => {
}
}, [potCategories]);
- const renderCategoryElement = (
- category: ProductCategoryWithNewDiscountsCount,
- i: number
- ) => {
+ const renderItem = (category: ProductCategoryWithNewDiscountsCount) => {
const specs = getCategorySpecs(category.productCategory);
const countAvailable = category.newDiscounts > 0;
+
return pipe(
specs,
O.fold(
() => null,
s => (
{
}}
>
{I18n.t(s.nameKey)}
-
+
) : (
I18n.t(s.nameKey)
@@ -127,42 +124,30 @@ export const CgnMerchantCategoriesListScreen = () => {
[potCategories]
);
- return (
- <>
- {bottomSheet}
- }
- data={pot.isNone(potCategories) ? [] : categoriesToArray}
- style={[
- IOStyles.horizontalContentPadding,
- IOStyles.flex,
- { paddingBottom: insets.bottom }
- ]}
- keyExtractor={pc => pc.productCategory}
- renderItem={({ item, index }) => renderCategoryElement(item, index)}
- refreshControl={
-
- }
- ItemSeparatorComponent={() => }
- ListFooterComponent={
- <>
-
-
- >
- }
- />
- >
- );
+ return {
+ data: categoriesToArray,
+ renderItem,
+ refreshControlProps: {
+ refreshing: isPullRefresh,
+ onRefresh: onPullRefresh
+ },
+ ListFooterComponent: (
+ <>
+
+
+ {bottomSheet}
+ >
+ ),
+ ListEmptyComponent: undefined,
+ skeleton:
+ };
};
diff --git a/ts/features/bonus/cgn/screens/merchants/CgnMerchantsCategoriesSelectionScreen.tsx b/ts/features/bonus/cgn/screens/merchants/CgnMerchantsCategoriesSelectionScreen.tsx
index 19920ce3bc9..374d85a9d60 100644
--- a/ts/features/bonus/cgn/screens/merchants/CgnMerchantsCategoriesSelectionScreen.tsx
+++ b/ts/features/bonus/cgn/screens/merchants/CgnMerchantsCategoriesSelectionScreen.tsx
@@ -1,24 +1,17 @@
-import { useCallback } from "react";
-import { View } from "react-native";
import {
- H2,
- TabNavigation,
+ IOIcons,
TabItem,
- VSpacer,
- IOIcons
+ TabNavigation,
+ VSpacer
} from "@pagopa/io-app-design-system";
-import {
- MaterialTopTabBarProps,
- createMaterialTopTabNavigator
-} from "@react-navigation/material-top-tabs";
-import { IOStyles } from "../../../../../components/core/variables/IOStyles";
+import { useState } from "react";
+import { IOListViewWithLargeHeader } from "../../../../../components/ui/IOListViewWithLargeHeader";
import I18n from "../../../../../i18n";
-import { useHeaderSecondLevel } from "../../../../../hooks/useHeaderSecondLevel";
import { useIONavigation } from "../../../../../navigation/params/AppParamsList";
-import CGN_ROUTES from "../../navigation/routes";
import { useDisableRootNavigatorGesture } from "../../hooks/useDisableRootNavigatorGesture";
-import CgnMerchantsListScreen from "./CgnMerchantsListScreen";
+import CGN_ROUTES from "../../navigation/routes";
import { CgnMerchantCategoriesListScreen } from "./CgnMerchantCategoriesListScreen";
+import { CgnMerchantsListScreen } from "./CgnMerchantsListScreen";
export const CgnMerchantsHomeTabRoutes = {
CGN_CATEGORIES: "CGN_CATEGORIES",
@@ -30,8 +23,6 @@ export type CgnMerchantsHomeTabParamsList = {
[CgnMerchantsHomeTabRoutes.CGN_MERCHANTS_ALL]: undefined;
};
-const Tab = createMaterialTopTabNavigator();
-
type TabOption = {
title: string;
icon: IOIcons;
@@ -48,93 +39,88 @@ const tabOptions: Record = {
}
};
-const CgnTabBar = ({ state, navigation }: MaterialTopTabBarProps) => {
- const isFocused = useCallback((i: number) => state.index === i, [state]);
+const CgnMerchantsCategoriesSelectionScreen = () => {
+ const { navigate } = useIONavigation();
+ useDisableRootNavigatorGesture();
+ const [selectedTab, setSelectedTab] = useState<
+ keyof CgnMerchantsHomeTabParamsList
+ >(CgnMerchantsHomeTabRoutes.CGN_CATEGORIES);
- return (
-
-
- {state.routes.map((route, index) => {
- const onPress = () => {
- const event = navigation.emit({
- type: "tabPress",
- target: route.key,
- canPreventDefault: true
- });
+ const categoriesScreen = CgnMerchantCategoriesListScreen();
+ const merchantsScreen = CgnMerchantsListScreen();
- if (!isFocused(index) && !event.defaultPrevented) {
- navigation.navigate(route.name);
- }
- };
+ const {
+ data,
+ renderItem,
+ refreshControlProps,
+ ListFooterComponent,
+ ListEmptyComponent,
+ skeleton
+ } =
+ selectedTab === CgnMerchantsHomeTabRoutes.CGN_CATEGORIES
+ ? categoriesScreen
+ : merchantsScreen;
- const label =
- tabOptions[route.name as keyof CgnMerchantsHomeTabParamsList].title;
+ const ListHeaderComponent = (
+ <>
+
+ {Object.keys(CgnMerchantsHomeTabRoutes).map(routeKey => {
+ const route = routeKey as keyof CgnMerchantsHomeTabParamsList;
+ const onPress = () => setSelectedTab(route);
+
+ const label = tabOptions[route].title;
return (
);
})}
-
+ >
);
-};
-const CgnMerchantsCategoriesSelectionScreen = () => {
- const { navigate } = useIONavigation();
- useDisableRootNavigatorGesture();
- useHeaderSecondLevel({
- title: "",
- supportRequest: true,
- secondAction: {
- icon: "search",
- testID: "search-button",
- onPress() {
- navigate(CGN_ROUTES.DETAILS.MAIN, {
- screen: CGN_ROUTES.DETAILS.MERCHANTS.SEARCH
- });
- },
- accessibilityLabel: I18n.t(
- "bonus.cgn.merchantSearch.goToSearchAccessibilityLabel"
- )
- }
- });
return (
- <>
-
-
- {I18n.t("bonus.cgn.merchantsList.screenTitle")}
-
-
-
-
-
-
-
- >
+ ("id" in item ? item.id : item.productCategory)}
+ title={{
+ label: I18n.t("bonus.cgn.merchantsList.screenTitle")
+ }}
+ headerActionsProp={{
+ showHelp: true,
+ headerType: "twoActions",
+ secondAction: {
+ icon: "search",
+ testID: "search-button",
+ onPress() {
+ navigate(CGN_ROUTES.DETAILS.MAIN, {
+ screen: CGN_ROUTES.DETAILS.MERCHANTS.SEARCH
+ });
+ },
+ accessibilityLabel: I18n.t(
+ "bonus.cgn.merchantSearch.goToSearchAccessibilityLabel"
+ )
+ }
+ }}
+ renderItem={({ item }) => renderItem(item as any)}
+ data={[...data]}
+ refreshControlProps={refreshControlProps}
+ ListHeaderComponent={ListHeaderComponent}
+ skeleton={skeleton}
+ ListFooterComponent={ListFooterComponent}
+ ListEmptyComponent={ListEmptyComponent}
+ />
);
};
diff --git a/ts/features/bonus/cgn/screens/merchants/CgnMerchantsListScreen.tsx b/ts/features/bonus/cgn/screens/merchants/CgnMerchantsListScreen.tsx
index c8296456883..d7acf361abe 100644
--- a/ts/features/bonus/cgn/screens/merchants/CgnMerchantsListScreen.tsx
+++ b/ts/features/bonus/cgn/screens/merchants/CgnMerchantsListScreen.tsx
@@ -1,28 +1,24 @@
import {
- ContentWrapper,
- Divider,
- ListItemHeader
+ Badge,
+ H6,
+ HSpacer,
+ IOStyles,
+ ListItemNav
} from "@pagopa/io-app-design-system";
import { useFocusEffect } from "@react-navigation/native";
-import { FunctionComponent, useCallback, useMemo } from "react";
-import { FlatList, RefreshControl, SafeAreaView } from "react-native";
-import { connect } from "react-redux";
+import { useCallback, useMemo } from "react";
+import { View } from "react-native";
+import { useDispatch, useSelector } from "react-redux";
import { Merchant } from "../../../../../../definitions/cgn/merchants/Merchant";
import { OfflineMerchant } from "../../../../../../definitions/cgn/merchants/OfflineMerchant";
import { OnlineMerchant } from "../../../../../../definitions/cgn/merchants/OnlineMerchant";
import {
getValueOrElse,
- isError,
- isLoading,
- isReady
+ isLoading
} from "../../../../../common/model/RemoteValue";
-import { IOStyles } from "../../../../../components/core/variables/IOStyles";
import { OperationResultScreenContent } from "../../../../../components/screens/OperationResultScreenContent";
import I18n from "../../../../../i18n";
import { useIONavigation } from "../../../../../navigation/params/AppParamsList";
-import { Dispatch } from "../../../../../store/actions/types";
-import { GlobalState } from "../../../../../store/reducers/types";
-import { CgnMerchantListViewRenderItem } from "../../components/merchants/CgnMerchantsListView";
import CGN_ROUTES from "../../navigation/routes";
import {
cgnOfflineMerchants,
@@ -33,34 +29,30 @@ import {
cgnOnlineMerchantsSelector
} from "../../store/reducers/merchants";
import { mixAndSortMerchants } from "../../utils/merchants";
-
-type Props = ReturnType &
- ReturnType;
+import { CgnMerchantListSkeleton } from "../../components/merchants/CgnMerchantListSkeleton";
export type MerchantsAll = OfflineMerchant | OnlineMerchant;
-/**
- * Screen that renders the list of the merchants which have an active discount for CGN
- * @param props
- * @constructor
- */
-const CgnMerchantsListScreen: FunctionComponent = (props: Props) => {
+
+export const CgnMerchantsListScreen = () => {
const navigator = useIONavigation();
- const { requestOfflineMerchants, requestOnlineMerchants } = props;
- // Mixes online and offline merchants to render on the same list
- // merchants are sorted by name
- const merchantsAll = useMemo(
+ const dispatch = useDispatch();
+
+ const onlineMerchants = useSelector(cgnOnlineMerchantsSelector);
+ const offlineMerchants = useSelector(cgnOfflineMerchantsSelector);
+
+ const data = useMemo(
() =>
mixAndSortMerchants(
- getValueOrElse(props.onlineMerchants, []),
- getValueOrElse(props.offlineMerchants, [])
+ getValueOrElse(onlineMerchants, []),
+ getValueOrElse(offlineMerchants, [])
),
- [props.onlineMerchants, props.offlineMerchants]
+ [onlineMerchants, offlineMerchants]
);
const initLoadingLists = useCallback(() => {
- requestOfflineMerchants();
- requestOnlineMerchants();
- }, [requestOfflineMerchants, requestOnlineMerchants]);
+ dispatch(cgnOfflineMerchants.request({}));
+ dispatch(cgnOnlineMerchants.request({}));
+ }, [dispatch]);
useFocusEffect(initLoadingLists);
@@ -74,62 +66,54 @@ const CgnMerchantsListScreen: FunctionComponent = (props: Props) => {
[navigator]
);
- const renderItem = useMemo(
- () => CgnMerchantListViewRenderItem({ onItemPress }),
- [onItemPress]
+ const renderItem = (item: MerchantsAll) => (
+ onItemPress(item.id)}
+ value={
+
+ {item.name}
+
+ {item.newDiscounts && (
+
+
+
+ )}
+
+ }
+ accessibilityLabel={item?.name}
+ />
);
- return (
-
- {!(isError(props.onlineMerchants) || isError(props.offlineMerchants)) && (
-
-
-
- )}
- {isReady(props.onlineMerchants) && isReady(props.offlineMerchants) && (
- item.id}
- renderItem={renderItem}
- ItemSeparatorComponent={() => }
- refreshControl={
-
- }
- />
- )}
- {(isError(props.onlineMerchants) || isError(props.offlineMerchants)) && (
-
- )}
-
- );
-};
-
-const mapStateToProps = (state: GlobalState) => ({
- onlineMerchants: cgnOnlineMerchantsSelector(state),
- offlineMerchants: cgnOfflineMerchantsSelector(state)
-});
+ const refreshControlProps = {
+ refreshing: isLoading(onlineMerchants) || isLoading(offlineMerchants),
+ onRefresh: initLoadingLists
+ };
-const mapDispatchToProps = (dispatch: Dispatch) => ({
- requestOnlineMerchants: () => dispatch(cgnOnlineMerchants.request({})),
- requestOfflineMerchants: () => dispatch(cgnOfflineMerchants.request({}))
-});
+ const ListEmptyComponent = (
+
+ );
-export default connect(
- mapStateToProps,
- mapDispatchToProps
-)(CgnMerchantsListScreen);
+ return {
+ data,
+ renderItem,
+ refreshControlProps,
+ ListFooterComponent: <>>,
+ ListEmptyComponent,
+ skeleton:
+ };
+};
diff --git a/ts/features/bonus/cgn/screens/merchants/__tests__/CgnMerchantsCategoriesSelectionScreen.test.tsx b/ts/features/bonus/cgn/screens/merchants/__tests__/CgnMerchantsCategoriesSelectionScreen.test.tsx
index bd1723623d0..889b8787fea 100644
--- a/ts/features/bonus/cgn/screens/merchants/__tests__/CgnMerchantsCategoriesSelectionScreen.test.tsx
+++ b/ts/features/bonus/cgn/screens/merchants/__tests__/CgnMerchantsCategoriesSelectionScreen.test.tsx
@@ -1,14 +1,16 @@
+import { fireEvent } from "@testing-library/react-native";
import { createStore } from "redux";
+import I18n from "../../../../../../i18n";
+import { useIONavigation } from "../../../../../../navigation/params/AppParamsList";
+import { applicationChangeState } from "../../../../../../store/actions/application";
+import { appReducer } from "../../../../../../store/reducers";
+import { GlobalState } from "../../../../../../store/reducers/types";
+import { renderScreenWithNavigationStoreContext } from "../../../../../../utils/testWrapper";
+import CGN_ROUTES from "../../../navigation/routes";
import CgnMerchantsCategoriesSelectionScreen, {
CgnMerchantsHomeTabRoutes
} from "../CgnMerchantsCategoriesSelectionScreen";
-import { useIONavigation } from "../../../../../../navigation/params/AppParamsList";
-import I18n from "../../../../../../i18n";
-import CGN_ROUTES from "../../../navigation/routes";
-import { renderScreenWithNavigationStoreContext } from "../../../../../../utils/testWrapper";
-import { GlobalState } from "../../../../../../store/reducers/types";
-import { appReducer } from "../../../../../../store/reducers";
-import { applicationChangeState } from "../../../../../../store/actions/application";
+
jest.mock("../../../../../../navigation/params/AppParamsList", () => ({
useIONavigation: jest.fn()
}));
@@ -17,10 +19,6 @@ jest.mock("../../../hooks/useDisableRootNavigatorGesture", () => ({
useDisableRootNavigatorGesture: jest.fn()
}));
-jest.mock("../../../../../../hooks/useHeaderSecondLevel", () => ({
- useHeaderSecondLevel: jest.fn()
-}));
-
const globalState = appReducer(undefined, applicationChangeState("active"));
const defaultState: GlobalState = {
@@ -57,13 +55,6 @@ describe("CgnMerchantsCategoriesSelectionScreen", () => {
expect(toJSON()).toMatchSnapshot();
});
- it("should render the title", () => {
- const { getByText } = renderComponent(defaultState);
- expect(
- getByText(I18n.t("bonus.cgn.merchantsList.screenTitle"))
- ).toBeTruthy();
- });
-
it("should render the tab navigation with categories and merchants tabs", () => {
const { getByTestId } = renderComponent(defaultState);
@@ -78,4 +69,30 @@ describe("CgnMerchantsCategoriesSelectionScreen", () => {
)
).toBeTruthy();
});
+
+ it("should navigate to search screen when search button is pressed", () => {
+ const { getByTestId } = renderComponent(defaultState);
+
+ const searchButton = getByTestId("search-button");
+
+ fireEvent.press(searchButton);
+
+ expect(mockNavigate).toHaveBeenCalledWith(CGN_ROUTES.DETAILS.MAIN, {
+ screen: CGN_ROUTES.DETAILS.MERCHANTS.SEARCH
+ });
+ });
+
+ it("should navigate to categories screen when categories tab is pressed", () => {
+ const { getByTestId, getByText } = renderComponent(defaultState);
+
+ const categoriesTab = getByTestId(
+ `cgn-merchants-tab-${CgnMerchantsHomeTabRoutes.CGN_CATEGORIES}`
+ );
+
+ fireEvent.press(categoriesTab);
+
+ expect(
+ getByText(I18n.t("bonus.cgn.merchantsList.tabs.perMerchant"))
+ ).toBeTruthy();
+ });
});
diff --git a/ts/features/bonus/cgn/screens/merchants/__tests__/__snapshots__/CgnMerchantsCategoriesSelectionScreen.test.tsx.snap b/ts/features/bonus/cgn/screens/merchants/__tests__/__snapshots__/CgnMerchantsCategoriesSelectionScreen.test.tsx.snap
index 363fbd8b6e7..75217ebf531 100644
--- a/ts/features/bonus/cgn/screens/merchants/__tests__/__snapshots__/CgnMerchantsCategoriesSelectionScreen.test.tsx.snap
+++ b/ts/features/bonus/cgn/screens/merchants/__tests__/__snapshots__/CgnMerchantsCategoriesSelectionScreen.test.tsx.snap
@@ -30,147 +30,6 @@ exports[`CgnMerchantsCategoriesSelectionScreen should render correctly 1`] = `
]
}
>
-
-
-
-
-
-
-
-
-
-
-
- CGN_MERCHANTS_CATEGORIES
-
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+ To facilitate your search and improve your experience, we have organized the categories according to frequency of use. This information is not tied to your profile, but is based on aggregate data from all users who use Carta Giovani Nazionale and have consented to data processing.
+
+
+
+
+
+
+
}
- >
-
- Discover opportunities
-
-
-
+
+
+ Discover opportunities
+
+
+
+
+
+
+
+
+
+
+
+
+
}
- />
-
+ }
+ refreshing={false}
+ removeClippedSubviews={false}
+ renderItem={[Function]}
+ scrollEventThrottle={8}
+ snapToEnd={false}
+ stickyHeaderIndices={[]}
+ viewabilityConfigCallbackPairs={[]}
>
+
-
-
-
+
+ Discover opportunities
+
+
+
+
+
-
-
-
-
-
-
- By category
-
-
-
-
-
+ >
+
+
+
+
+ By category
+
+
+
+
-
-
-
-
-
+ >
+
+
+
+
+ By partner
+
+
+
+
+
+
+
+
+
+
+
+
+
+
- By partner
+ How are categories ordered by?
-
-
-
-
-
-
-
+
-
-
-
-
-
- To facilitate your search and improve your experience, we have organized the categories according to frequency of use. This information is not tied to your profile, but is based on aggregate data from all users who use Carta Giovani Nazionale and have consented to data processing.
-
-
-
-
-
-
-
-
-
-
- }
- data={[]}
- getItem={[Function]}
- getItemCount={[Function]}
- keyExtractor={[Function]}
- onContentSizeChange={[Function]}
- onLayout={[Function]}
- onMomentumScrollBegin={[Function]}
- onMomentumScrollEnd={[Function]}
- onScroll={[Function]}
- onScrollBeginDrag={[Function]}
- onScrollEndDrag={[Function]}
- refreshControl={
-
- }
- removeClippedSubviews={false}
- renderItem={[Function]}
- scrollEventThrottle={0.0001}
- stickyHeaderIndices={[]}
- style={
- [
- {
- "paddingHorizontal": 24,
- },
- {
- "flex": 1,
- },
- {
- "paddingBottom": 0,
- },
- ]
}
- viewabilityConfigCallbackPairs={[]}
>
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
+ To facilitate your search and improve your experience, we have organized the categories according to frequency of use. This information is not tied to your profile, but is based on aggregate data from all users who use Carta Giovani Nazionale and have consented to data processing.
+
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
-
-
+
+
+
+
+
+ Discover opportunities
+
+
+
+
+
+
+
-
-
-
-
- How are categories ordered by?
-
-
-
-
-
-
+ />
+
+
-
-
-
-
-
+
+
+ {
+ "alignItems": "center",
+ },
+ {
+ "justifyContent": "center",
+ },
+ false,
+ ]
+ }
+ >
+
+
+
+
+
+
+
+
-
+
@@ -1376,6 +1395,20 @@ exports[`CgnMerchantsCategoriesSelectionScreen should render correctly 1`] = `
+
diff --git a/ts/features/cie/__tests__/__snapshots__/CieIdErrorScreen.test.tsx.snap b/ts/features/cie/__tests__/__snapshots__/CieIdErrorScreen.test.tsx.snap
index b2e28c07cfd..f10646259b5 100644
--- a/ts/features/cie/__tests__/__snapshots__/CieIdErrorScreen.test.tsx.snap
+++ b/ts/features/cie/__tests__/__snapshots__/CieIdErrorScreen.test.tsx.snap
@@ -281,7 +281,8 @@ exports[`CieIdErrorScreen where device doesn't support NFC Should match the snap
onStartShouldSetResponder={[Function]}
style={
{
- "alignSelf": "flex-start",
+ "alignSelf": "auto",
+ "flexShrink": 1,
}
}
testID="cie-id-error-primary-action"
@@ -771,7 +772,8 @@ Insert the CIE PIN and bring it close to the device to allow the data to be read
onStartShouldSetResponder={[Function]}
style={
{
- "alignSelf": "flex-start",
+ "alignSelf": "auto",
+ "flexShrink": 1,
}
}
testID="cie-id-error-primary-action"
diff --git a/ts/features/cie/__tests__/__snapshots__/CieIdNotInstalled.test.tsx.snap b/ts/features/cie/__tests__/__snapshots__/CieIdNotInstalled.test.tsx.snap
index 029abc01090..168ec958b03 100644
--- a/ts/features/cie/__tests__/__snapshots__/CieIdNotInstalled.test.tsx.snap
+++ b/ts/features/cie/__tests__/__snapshots__/CieIdNotInstalled.test.tsx.snap
@@ -267,7 +267,8 @@ exports[`CieIdNotInstalled Should match the snapshot 1`] = `
onStartShouldSetResponder={[Function]}
style={
{
- "alignSelf": "flex-start",
+ "alignSelf": "auto",
+ "flexShrink": 1,
}
}
testID="cie-id-not-installed-open-store"
diff --git a/ts/features/cie/__tests__/__snapshots__/CieIdNotInstalledScreen.test.tsx.snap b/ts/features/cie/__tests__/__snapshots__/CieIdNotInstalledScreen.test.tsx.snap
index d2bbf8c5b44..7f6604f4a82 100644
--- a/ts/features/cie/__tests__/__snapshots__/CieIdNotInstalledScreen.test.tsx.snap
+++ b/ts/features/cie/__tests__/__snapshots__/CieIdNotInstalledScreen.test.tsx.snap
@@ -267,7 +267,8 @@ exports[`CieIdNotInstalledScreen Should match snapshot 1`] = `
onStartShouldSetResponder={[Function]}
style={
{
- "alignSelf": "flex-start",
+ "alignSelf": "auto",
+ "flexShrink": 1,
}
}
testID="cie-id-not-installed-open-store"
diff --git a/ts/features/cie/__tests__/__snapshots__/wizards.test.tsx.snap b/ts/features/cie/__tests__/__snapshots__/wizards.test.tsx.snap
index 10b11704ead..99caabda2fa 100644
--- a/ts/features/cie/__tests__/__snapshots__/wizards.test.tsx.snap
+++ b/ts/features/cie/__tests__/__snapshots__/wizards.test.tsx.snap
@@ -389,7 +389,12 @@ To enter the IO app without physically using the Electronic Identity Card, you m
onResponderTerminate={[Function]}
onResponderTerminationRequest={[Function]}
onStartShouldSetResponder={[Function]}
- style={{}}
+ style={
+ {
+ "alignSelf": "stretch",
+ "flexShrink": 0,
+ }
+ }
testID="cie-id-wizard-login-with-cie-id"
>
diff --git a/ts/features/cieLogin/__tests__/__snapshots__/CieIdLoginScreen.test.tsx.snap b/ts/features/cieLogin/__tests__/__snapshots__/CieIdLoginScreen.test.tsx.snap
index 4be2535de5f..0479b03d693 100644
--- a/ts/features/cieLogin/__tests__/__snapshots__/CieIdLoginScreen.test.tsx.snap
+++ b/ts/features/cieLogin/__tests__/__snapshots__/CieIdLoginScreen.test.tsx.snap
@@ -374,7 +374,8 @@ exports[`CieIdLoginScreen Should match snapshot 1`] = `
onStartShouldSetResponder={[Function]}
style={
{
- "alignSelf": "flex-start",
+ "alignSelf": "auto",
+ "flexShrink": 1,
}
}
testID="loadingSpinnerOverlayCancelButton"
diff --git a/ts/features/design-system/DesignSystem.tsx b/ts/features/design-system/DesignSystem.tsx
index bc42e08a970..8648b1eaa46 100644
--- a/ts/features/design-system/DesignSystem.tsx
+++ b/ts/features/design-system/DesignSystem.tsx
@@ -1,7 +1,7 @@
import {
BodySmall,
Divider,
- H3,
+ H2,
IOVisualCostants,
ListItemNav,
VSpacer,
@@ -108,9 +108,9 @@ export const DesignSystem = () => {
section: { title: string; description?: string };
}) => (
-
+
{title}
-
+
{description && (
{description}
diff --git a/ts/features/design-system/core/DSIOListViewWithLargeHeader.tsx b/ts/features/design-system/core/DSIOListViewWithLargeHeader.tsx
new file mode 100644
index 00000000000..5bc32dccd38
--- /dev/null
+++ b/ts/features/design-system/core/DSIOListViewWithLargeHeader.tsx
@@ -0,0 +1,109 @@
+import { ListItemHeader, ListItemInfo } from "@pagopa/io-app-design-system";
+import { useState } from "react";
+import { Alert } from "react-native";
+import { IOListViewWithLargeHeader } from "../../../components/ui/IOListViewWithLargeHeader";
+import I18n from "../../../i18n";
+
+export const DSIOListViewWithLargeHeader = () => {
+ const [refreshing, setRefreshing] = useState(false);
+
+ const pullAction = () => {
+ setRefreshing(true);
+ Alert.alert("Alert", "Action triggered");
+ setTimeout(() => {
+ setRefreshing(false);
+ }, 2000);
+ };
+
+ const renderItems: Array = [
+ {
+ id: "1",
+ label: I18n.t("authentication.cie.nfc.listItemLabel1"),
+ value: I18n.t("authentication.cie.nfc.listItemValue1"),
+ icon: "systemSettingsAndroid"
+ },
+ {
+ id: "2",
+ label: I18n.t("authentication.cie.nfc.listItemLabel2"),
+ value: I18n.t("authentication.cie.nfc.listItemValue2"),
+ icon: "systemAppsAndroid"
+ },
+ {
+ id: "3",
+ label: I18n.t("authentication.cie.nfc.listItemLabel3"),
+ value: I18n.t("authentication.cie.nfc.listItemValue3"),
+ icon: "systemToggleInstructions"
+ },
+ {
+ id: "4",
+ label: I18n.t("authentication.cie.nfc.listItemLabel3"),
+ value: I18n.t("authentication.cie.nfc.listItemValue3"),
+ icon: "systemToggleInstructions"
+ },
+ {
+ id: "5",
+ label: I18n.t("authentication.cie.nfc.listItemLabel3"),
+ value: I18n.t("authentication.cie.nfc.listItemValue3"),
+ icon: "systemToggleInstructions"
+ },
+ {
+ id: "6",
+ label: I18n.t("authentication.cie.nfc.listItemLabel3"),
+ value: I18n.t("authentication.cie.nfc.listItemValue3"),
+ icon: "systemToggleInstructions"
+ },
+ {
+ id: "7",
+ label: I18n.t("authentication.cie.nfc.listItemLabel3"),
+ value: I18n.t("authentication.cie.nfc.listItemValue3"),
+ icon: "systemToggleInstructions"
+ },
+ {
+ id: "8",
+ label: I18n.t("authentication.cie.nfc.listItemLabel3"),
+ value: I18n.t("authentication.cie.nfc.listItemValue3"),
+ icon: "systemToggleInstructions"
+ },
+ {
+ id: "9",
+ label: I18n.t("authentication.cie.nfc.listItemLabel1"),
+ value: I18n.t("authentication.cie.nfc.listItemValue1"),
+ icon: "systemSettingsAndroid"
+ },
+ {
+ id: "10",
+ label: I18n.t("authentication.cie.nfc.listItemLabel2"),
+ value: I18n.t("authentication.cie.nfc.listItemValue2"),
+ icon: "systemAppsAndroid"
+ }
+ ];
+
+ return (
+ item.id}
+ subtitle={I18n.t("authentication.cie.nfc.subtitle")}
+ ListHeaderComponent={
+
+ }
+ headerActionsProp={{
+ showHelp: true
+ }}
+ renderItem={({ item }) => (
+
+ )}
+ refreshControlProps={{
+ onRefresh: pullAction,
+ refreshing
+ }}
+ />
+ );
+};
diff --git a/ts/features/design-system/core/__tests__/DSIOListViewWithLargeHeader.test.tsx b/ts/features/design-system/core/__tests__/DSIOListViewWithLargeHeader.test.tsx
new file mode 100644
index 00000000000..365dbafc216
--- /dev/null
+++ b/ts/features/design-system/core/__tests__/DSIOListViewWithLargeHeader.test.tsx
@@ -0,0 +1,24 @@
+import { createStore } from "redux";
+import { applicationChangeState } from "../../../../store/actions/application";
+import { appReducer } from "../../../../store/reducers";
+import { GlobalState } from "../../../../store/reducers/types";
+import { renderScreenWithNavigationStoreContext } from "../../../../utils/testWrapper";
+import DESIGN_SYSTEM_ROUTES from "../../navigation/routes";
+import { DSIOListViewWithLargeHeader } from "../DSIOListViewWithLargeHeader";
+
+const renderComponent = () => {
+ const globalState = appReducer(undefined, applicationChangeState("active"));
+ return renderScreenWithNavigationStoreContext(
+ () => ,
+ DESIGN_SYSTEM_ROUTES.SCREENS.IOLISTVIEW_LARGE_HEADER.route,
+ {},
+ createStore(appReducer, globalState as any)
+ );
+};
+
+describe("DSIOListViewWithLargeHeader", () => {
+ it("should render list items correctly", () => {
+ const { toJSON } = renderComponent();
+ expect(toJSON()).toMatchSnapshot();
+ });
+});
diff --git a/ts/features/design-system/core/__tests__/__snapshots__/DSIOListViewWithLargeHeader.test.tsx.snap b/ts/features/design-system/core/__tests__/__snapshots__/DSIOListViewWithLargeHeader.test.tsx.snap
new file mode 100644
index 00000000000..9e02cf4c8c9
--- /dev/null
+++ b/ts/features/design-system/core/__tests__/__snapshots__/DSIOListViewWithLargeHeader.test.tsx.snap
@@ -0,0 +1,1808 @@
+// Jest Snapshot v1, https://goo.gl/fbAQLP
+
+exports[`DSIOListViewWithLargeHeader should render list items correctly 1`] = `
+
+
+
+
+
+
+
+
+
+
+
+
+ }
+ ListHeaderComponent={
+
+
+
+ Enable NFC to continue
+
+
+
+
+
+ To enable IO to read your CIE, activate NFC from your device's settings.
+
+
+
+
+
+
+
+ }
+ collapsable={false}
+ contentContainerStyle={
+ [
+ {
+ "paddingBottom": 56,
+ "paddingHorizontal": 24,
+ },
+ {},
+ ]
+ }
+ data={
+ [
+ {
+ "icon": "systemSettingsAndroid",
+ "id": "1",
+ "label": "Step 1",
+ "value": "Open 'Settings'",
+ },
+ {
+ "icon": "systemAppsAndroid",
+ "id": "2",
+ "label": "Step 2",
+ "value": "Search for ‘NFC’ functionality",
+ },
+ {
+ "icon": "systemToggleInstructions",
+ "id": "3",
+ "label": "Step 3",
+ "value": "Turn it on",
+ },
+ {
+ "icon": "systemToggleInstructions",
+ "id": "4",
+ "label": "Step 3",
+ "value": "Turn it on",
+ },
+ {
+ "icon": "systemToggleInstructions",
+ "id": "5",
+ "label": "Step 3",
+ "value": "Turn it on",
+ },
+ {
+ "icon": "systemToggleInstructions",
+ "id": "6",
+ "label": "Step 3",
+ "value": "Turn it on",
+ },
+ {
+ "icon": "systemToggleInstructions",
+ "id": "7",
+ "label": "Step 3",
+ "value": "Turn it on",
+ },
+ {
+ "icon": "systemToggleInstructions",
+ "id": "8",
+ "label": "Step 3",
+ "value": "Turn it on",
+ },
+ {
+ "icon": "systemSettingsAndroid",
+ "id": "9",
+ "label": "Step 1",
+ "value": "Open 'Settings'",
+ },
+ {
+ "icon": "systemAppsAndroid",
+ "id": "10",
+ "label": "Step 2",
+ "value": "Search for ‘NFC’ functionality",
+ },
+ ]
+ }
+ decelerationRate="normal"
+ getItem={[Function]}
+ getItemCount={[Function]}
+ keyExtractor={[Function]}
+ onContentSizeChange={[Function]}
+ onLayout={[Function]}
+ onMomentumScrollBegin={[Function]}
+ onMomentumScrollEnd={[Function]}
+ onScroll={[Function]}
+ onScrollBeginDrag={[Function]}
+ onScrollEndDrag={[Function]}
+ refreshControl={
+
+ }
+ refreshing={false}
+ removeClippedSubviews={false}
+ renderItem={[Function]}
+ scrollEventThrottle={8}
+ snapToEnd={false}
+ stickyHeaderIndices={[]}
+ viewabilityConfigCallbackPairs={[]}
+ >
+
+
+
+
+
+ Enable NFC to continue
+
+
+
+
+ To enable IO to read your CIE, activate NFC from your device's settings.
+
+
+
+
+
+
+
+ How to do it::
+
+
+
+
+
+
+
+
+
+
+
+
+ Step 1
+
+
+ Open 'Settings'
+
+
+
+
+
+
+
+
+
+
+
+
+
+ Step 2
+
+
+ Search for ‘NFC’ functionality
+
+
+
+
+
+
+
+
+
+
+
+
+
+ Step 3
+
+
+ Turn it on
+
+
+
+
+
+
+
+
+
+
+
+
+
+ Step 3
+
+
+ Turn it on
+
+
+
+
+
+
+
+
+
+
+
+
+
+ Step 3
+
+
+ Turn it on
+
+
+
+
+
+
+
+
+
+
+
+
+
+ Step 3
+
+
+ Turn it on
+
+
+
+
+
+
+
+
+
+
+
+
+
+ Step 3
+
+
+ Turn it on
+
+
+
+
+
+
+
+
+
+
+
+
+
+ Step 3
+
+
+ Turn it on
+
+
+
+
+
+
+
+
+
+
+
+
+
+ Step 1
+
+
+ Open 'Settings'
+
+
+
+
+
+
+
+
+
+
+
+
+
+ Step 2
+
+
+ Search for ‘NFC’ functionality
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ Enable NFC to continue
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+`;
diff --git a/ts/features/design-system/navigation/navigator.tsx b/ts/features/design-system/navigation/navigator.tsx
index 8aa58cfe3ca..424937eb7ff 100644
--- a/ts/features/design-system/navigation/navigator.tsx
+++ b/ts/features/design-system/navigation/navigator.tsx
@@ -26,6 +26,7 @@ import { DSButtons } from "../core/DSButtons";
import { DSCards } from "../core/DSCards";
import { DSColors } from "../core/DSColors";
import { DSDynamicBackground } from "../core/DSDynamicBackground";
+import { DSDynamicCardRotation } from "../core/DSDynamicCardRotation";
import { DSEdgeToEdgeArea } from "../core/DSEdgeToEdgeArea";
import { DSFooterActions } from "../core/DSFooterActions";
import { DSFooterActionsInline } from "../core/DSFooterActionsInline";
@@ -42,6 +43,7 @@ import { DSIOScrollView } from "../core/DSIOScrollView";
import { DSIOScrollViewCentredContent } from "../core/DSIOScrollViewCentredContent";
import { DSIOScrollViewScreenWithLargeHeader } from "../core/DSIOScrollViewWithLargeHeader";
import { DSIOScrollViewWithListItems } from "../core/DSIOScrollViewWithListItems";
+import { DSIOListViewWithLargeHeader } from "../core/DSIOListViewWithLargeHeader";
import { DSIOScrollViewWithoutActions } from "../core/DSIOScrollViewWithoutActions";
import { DSIcons } from "../core/DSIcons";
import { DSIridescentTrustmark } from "../core/DSIridescentTrustmark";
@@ -67,7 +69,6 @@ import { DSTextFields } from "../core/DSTextFields";
import { DSToastNotifications } from "../core/DSToastNotifications";
import { DSTypography } from "../core/DSTypography";
import { DSWallet } from "../core/DSWallet";
-import { DSDynamicCardRotation } from "../core/DSDynamicCardRotation";
import { DesignSystemParamsList } from "./params";
import DESIGN_SYSTEM_ROUTES from "./routes";
@@ -448,6 +449,12 @@ export const DesignSystemNavigator = () => {
options={{ headerShown: true }}
/>
+
+
@@ -1613,7 +1614,8 @@ exports[`ItwIssuanceCredentialAsyncContinuationScreen it should render the docum
onStartShouldSetResponder={[Function]}
style={
{
- "alignSelf": "flex-start",
+ "alignSelf": "auto",
+ "flexShrink": 1,
}
}
>
@@ -2461,7 +2463,8 @@ exports[`ItwIssuanceCredentialAsyncContinuationScreen it should render the gener
onStartShouldSetResponder={[Function]}
style={
{
- "alignSelf": "flex-start",
+ "alignSelf": "auto",
+ "flexShrink": 1,
}
}
>
@@ -3189,7 +3192,8 @@ exports[`ItwIssuanceCredentialAsyncContinuationScreen it should render the gener
onStartShouldSetResponder={[Function]}
style={
{
- "alignSelf": "flex-start",
+ "alignSelf": "auto",
+ "flexShrink": 1,
}
}
>
diff --git a/ts/features/messages/components/Home/__tests__/__snapshots__/EmptyList.test.tsx.snap b/ts/features/messages/components/Home/__tests__/__snapshots__/EmptyList.test.tsx.snap
index 231f1f63f32..8d75709145a 100644
--- a/ts/features/messages/components/Home/__tests__/__snapshots__/EmptyList.test.tsx.snap
+++ b/ts/features/messages/components/Home/__tests__/__snapshots__/EmptyList.test.tsx.snap
@@ -1165,7 +1165,8 @@ exports[`EmptyList should match snapshot, ARCHIVE category, pot.noneError 1`] =
onStartShouldSetResponder={[Function]}
style={
{
- "alignSelf": "flex-start",
+ "alignSelf": "auto",
+ "flexShrink": 1,
}
}
testID="home_emptyList_retry"
@@ -6827,7 +6828,8 @@ exports[`EmptyList should match snapshot, INBOX category, pot.noneError 1`] =
onStartShouldSetResponder={[Function]}
style={
{
- "alignSelf": "flex-start",
+ "alignSelf": "auto",
+ "flexShrink": 1,
}
}
testID="home_emptyList_retry"
diff --git a/ts/features/messages/components/Home/__tests__/__snapshots__/PreconditionsFooter.test.tsx.snap b/ts/features/messages/components/Home/__tests__/__snapshots__/PreconditionsFooter.test.tsx.snap
index b31c4db6079..4568066a6a9 100644
--- a/ts/features/messages/components/Home/__tests__/__snapshots__/PreconditionsFooter.test.tsx.snap
+++ b/ts/features/messages/components/Home/__tests__/__snapshots__/PreconditionsFooter.test.tsx.snap
@@ -415,7 +415,12 @@ exports[`PreconditionsFooter should match snapshot for 'content' footer category
onResponderTerminate={[Function]}
onResponderTerminationRequest={[Function]}
onStartShouldSetResponder={[Function]}
- style={{}}
+ style={
+ {
+ "alignSelf": "stretch",
+ "flexShrink": 0,
+ }
+ }
testID="message_preconditions_footer_continue"
>
@@ -2559,7 +2560,7 @@ exports[`MessageRouterScreen should match snapshot on message data retrieval fai
accessibilityLabel=""
accessibilityRole="header"
accessible={false}
- importantForAccessibility="no-hide-descendants"
+ importantForAccessibility="yes"
style={
{
"flexGrow": 1,
@@ -3405,7 +3406,7 @@ exports[`MessageRouterScreen should match snapshot on message data retrieval suc
accessibilityLabel=""
accessibilityRole="header"
accessible={false}
- importantForAccessibility="no-hide-descendants"
+ importantForAccessibility="yes"
style={
{
"flexGrow": 1,
@@ -4251,7 +4252,7 @@ exports[`MessageRouterScreen should match snapshot while retrieving message data
accessibilityLabel=""
accessibilityRole="header"
accessible={false}
- importantForAccessibility="no-hide-descendants"
+ importantForAccessibility="yes"
style={
{
"flexGrow": 1,
diff --git a/ts/features/messages/screens/__tests__/__snapshots__/MessagesHomeScreen.test.tsx.snap b/ts/features/messages/screens/__tests__/__snapshots__/MessagesHomeScreen.test.tsx.snap
index 30faafbcd86..4f3ab8522f0 100644
--- a/ts/features/messages/screens/__tests__/__snapshots__/MessagesHomeScreen.test.tsx.snap
+++ b/ts/features/messages/screens/__tests__/__snapshots__/MessagesHomeScreen.test.tsx.snap
@@ -333,8 +333,8 @@ exports[`MessagesHomeScreen should match snapshot (with mocked components 1`] =
>
@@ -3727,7 +3729,7 @@ Se dopo 5 giorni lavorativi non hai ancora ricevuto il rimborso, contatta l’as
accessibilityLabel=""
accessibilityRole="header"
accessible={false}
- importantForAccessibility="no-hide-descendants"
+ importantForAccessibility="yes"
style={
{
"flexGrow": 1,
@@ -4315,7 +4317,8 @@ Controlla di aver seguito correttamente le istruzioni della tua banca.
onStartShouldSetResponder={[Function]}
style={
{
- "alignSelf": "flex-start",
+ "alignSelf": "auto",
+ "flexShrink": 1,
}
}
>
@@ -5638,7 +5641,7 @@ Se dopo 5 giorni lavorativi non hai ancora ricevuto il rimborso, contatta l’as
accessibilityLabel=""
accessibilityRole="header"
accessible={false}
- importantForAccessibility="no-hide-descendants"
+ importantForAccessibility="yes"
style={
{
"flexGrow": 1,
@@ -6211,7 +6214,8 @@ exports[`WalletPaymentOutcomeScreen for all outcomes should render the WalletPay
onStartShouldSetResponder={[Function]}
style={
{
- "alignSelf": "flex-start",
+ "alignSelf": "auto",
+ "flexShrink": 1,
}
}
>
@@ -7534,7 +7538,7 @@ Se dopo 5 giorni lavorativi non hai ancora ricevuto il rimborso, contatta l’as
accessibilityLabel=""
accessibilityRole="header"
accessible={false}
- importantForAccessibility="no-hide-descendants"
+ importantForAccessibility="yes"
style={
{
"flexGrow": 1,
@@ -8121,7 +8125,8 @@ exports[`WalletPaymentOutcomeScreen for all outcomes should render the WalletPay
onStartShouldSetResponder={[Function]}
style={
{
- "alignSelf": "flex-start",
+ "alignSelf": "auto",
+ "flexShrink": 1,
}
}
>
@@ -9444,7 +9449,7 @@ Se dopo 5 giorni lavorativi non hai ancora ricevuto il rimborso, contatta l’as
accessibilityLabel=""
accessibilityRole="header"
accessible={false}
- importantForAccessibility="no-hide-descendants"
+ importantForAccessibility="yes"
style={
{
"flexGrow": 1,
@@ -9987,7 +9992,8 @@ exports[`WalletPaymentOutcomeScreen for all outcomes should render the WalletPay
onStartShouldSetResponder={[Function]}
style={
{
- "alignSelf": "flex-start",
+ "alignSelf": "auto",
+ "flexShrink": 1,
}
}
>
@@ -11430,7 +11436,7 @@ Se dopo 5 giorni lavorativi non hai ancora ricevuto il rimborso, contatta l’as
accessibilityLabel=""
accessibilityRole="header"
accessible={false}
- importantForAccessibility="no-hide-descendants"
+ importantForAccessibility="yes"
style={
{
"flexGrow": 1,
@@ -11945,7 +11951,8 @@ exports[`WalletPaymentOutcomeScreen for all outcomes should render the WalletPay
onStartShouldSetResponder={[Function]}
style={
{
- "alignSelf": "flex-start",
+ "alignSelf": "auto",
+ "flexShrink": 1,
}
}
>
@@ -13388,7 +13395,7 @@ Se dopo 5 giorni lavorativi non hai ancora ricevuto il rimborso, contatta l’as
accessibilityLabel=""
accessibilityRole="header"
accessible={false}
- importantForAccessibility="no-hide-descendants"
+ importantForAccessibility="yes"
style={
{
"flexGrow": 1,
@@ -13961,7 +13968,8 @@ exports[`WalletPaymentOutcomeScreen for all outcomes should render the WalletPay
onStartShouldSetResponder={[Function]}
style={
{
- "alignSelf": "flex-start",
+ "alignSelf": "auto",
+ "flexShrink": 1,
}
}
>
@@ -15284,7 +15292,7 @@ Se dopo 5 giorni lavorativi non hai ancora ricevuto il rimborso, contatta l’as
accessibilityLabel=""
accessibilityRole="header"
accessible={false}
- importantForAccessibility="no-hide-descendants"
+ importantForAccessibility="yes"
style={
{
"flexGrow": 1,
@@ -15807,7 +15815,8 @@ exports[`WalletPaymentOutcomeScreen for all outcomes should render the WalletPay
onStartShouldSetResponder={[Function]}
style={
{
- "alignSelf": "flex-start",
+ "alignSelf": "auto",
+ "flexShrink": 1,
}
}
>
@@ -17130,7 +17139,7 @@ Se dopo 5 giorni lavorativi non hai ancora ricevuto il rimborso, contatta l’as
accessibilityLabel=""
accessibilityRole="header"
accessible={false}
- importantForAccessibility="no-hide-descendants"
+ importantForAccessibility="yes"
style={
{
"flexGrow": 1,
@@ -17737,7 +17746,8 @@ exports[`WalletPaymentOutcomeScreen for all outcomes should render the WalletPay
onStartShouldSetResponder={[Function]}
style={
{
- "alignSelf": "flex-start",
+ "alignSelf": "auto",
+ "flexShrink": 1,
}
}
>
@@ -19060,7 +19070,7 @@ Se dopo 5 giorni lavorativi non hai ancora ricevuto il rimborso, contatta l’as
accessibilityLabel=""
accessibilityRole="header"
accessible={false}
- importantForAccessibility="no-hide-descendants"
+ importantForAccessibility="yes"
style={
{
"flexGrow": 1,
@@ -19647,7 +19657,8 @@ exports[`WalletPaymentOutcomeScreen for all outcomes should render the WalletPay
onStartShouldSetResponder={[Function]}
style={
{
- "alignSelf": "flex-start",
+ "alignSelf": "auto",
+ "flexShrink": 1,
}
}
>
@@ -20970,7 +20981,7 @@ Se dopo 5 giorni lavorativi non hai ancora ricevuto il rimborso, contatta l’as
accessibilityLabel=""
accessibilityRole="header"
accessible={false}
- importantForAccessibility="no-hide-descendants"
+ importantForAccessibility="yes"
style={
{
"flexGrow": 1,
@@ -21577,7 +21588,8 @@ exports[`WalletPaymentOutcomeScreen for all outcomes should render the WalletPay
onStartShouldSetResponder={[Function]}
style={
{
- "alignSelf": "flex-start",
+ "alignSelf": "auto",
+ "flexShrink": 1,
}
}
>
@@ -22900,7 +22912,7 @@ Se dopo 5 giorni lavorativi non hai ancora ricevuto il rimborso, contatta l’as
accessibilityLabel=""
accessibilityRole="header"
accessible={false}
- importantForAccessibility="no-hide-descendants"
+ importantForAccessibility="yes"
style={
{
"flexGrow": 1,
@@ -23443,7 +23455,8 @@ exports[`WalletPaymentOutcomeScreen for all outcomes should render the WalletPay
onStartShouldSetResponder={[Function]}
style={
{
- "alignSelf": "flex-start",
+ "alignSelf": "auto",
+ "flexShrink": 1,
}
}
>
@@ -24886,7 +24899,7 @@ Se dopo 5 giorni lavorativi non hai ancora ricevuto il rimborso, contatta l’as
accessibilityLabel=""
accessibilityRole="header"
accessible={false}
- importantForAccessibility="no-hide-descendants"
+ importantForAccessibility="yes"
style={
{
"flexGrow": 1,
@@ -25493,7 +25506,8 @@ exports[`WalletPaymentOutcomeScreen for all outcomes should render the WalletPay
onStartShouldSetResponder={[Function]}
style={
{
- "alignSelf": "flex-start",
+ "alignSelf": "auto",
+ "flexShrink": 1,
}
}
>
@@ -26816,7 +26830,7 @@ Se dopo 5 giorni lavorativi non hai ancora ricevuto il rimborso, contatta l’as
accessibilityLabel=""
accessibilityRole="header"
accessible={false}
- importantForAccessibility="no-hide-descendants"
+ importantForAccessibility="yes"
style={
{
"flexGrow": 1,
@@ -27423,7 +27437,8 @@ exports[`WalletPaymentOutcomeScreen for all outcomes should render the WalletPay
onStartShouldSetResponder={[Function]}
style={
{
- "alignSelf": "flex-start",
+ "alignSelf": "auto",
+ "flexShrink": 1,
}
}
>
@@ -28746,7 +28761,7 @@ Se dopo 5 giorni lavorativi non hai ancora ricevuto il rimborso, contatta l’as
accessibilityLabel=""
accessibilityRole="header"
accessible={false}
- importantForAccessibility="no-hide-descendants"
+ importantForAccessibility="yes"
style={
{
"flexGrow": 1,
@@ -29361,7 +29376,8 @@ exports[`WalletPaymentOutcomeScreen for all outcomes should render the WalletPay
onStartShouldSetResponder={[Function]}
style={
{
- "alignSelf": "flex-start",
+ "alignSelf": "auto",
+ "flexShrink": 1,
}
}
>
@@ -30684,7 +30700,7 @@ Se dopo 5 giorni lavorativi non hai ancora ricevuto il rimborso, contatta l’as
accessibilityLabel=""
accessibilityRole="header"
accessible={false}
- importantForAccessibility="no-hide-descendants"
+ importantForAccessibility="yes"
style={
{
"flexGrow": 1,
@@ -31271,7 +31287,8 @@ exports[`WalletPaymentOutcomeScreen for all outcomes should render the WalletPay
onStartShouldSetResponder={[Function]}
style={
{
- "alignSelf": "flex-start",
+ "alignSelf": "auto",
+ "flexShrink": 1,
}
}
>
@@ -32594,7 +32611,7 @@ Se dopo 5 giorni lavorativi non hai ancora ricevuto il rimborso, contatta l’as
accessibilityLabel=""
accessibilityRole="header"
accessible={false}
- importantForAccessibility="no-hide-descendants"
+ importantForAccessibility="yes"
style={
{
"flexGrow": 1,
@@ -33139,7 +33156,8 @@ exports[`WalletPaymentOutcomeScreen for all outcomes should render the WalletPay
onStartShouldSetResponder={[Function]}
style={
{
- "alignSelf": "flex-start",
+ "alignSelf": "auto",
+ "flexShrink": 1,
}
}
>
@@ -34582,7 +34600,7 @@ Se dopo 5 giorni lavorativi non hai ancora ricevuto il rimborso, contatta l’as
accessibilityLabel=""
accessibilityRole="header"
accessible={false}
- importantForAccessibility="no-hide-descendants"
+ importantForAccessibility="yes"
style={
{
"flexGrow": 1,
@@ -35169,7 +35187,8 @@ exports[`WalletPaymentOutcomeScreen for all outcomes should render the WalletPay
onStartShouldSetResponder={[Function]}
style={
{
- "alignSelf": "flex-start",
+ "alignSelf": "auto",
+ "flexShrink": 1,
}
}
>
@@ -36492,7 +36511,7 @@ Se dopo 5 giorni lavorativi non hai ancora ricevuto il rimborso, contatta l’as
accessibilityLabel=""
accessibilityRole="header"
accessible={false}
- importantForAccessibility="no-hide-descendants"
+ importantForAccessibility="yes"
style={
{
"flexGrow": 1,
@@ -37085,7 +37104,8 @@ exports[`WalletPaymentOutcomeScreen for all outcomes should render the WalletPay
onStartShouldSetResponder={[Function]}
style={
{
- "alignSelf": "flex-start",
+ "alignSelf": "auto",
+ "flexShrink": 1,
}
}
>
@@ -38640,7 +38660,7 @@ Se dopo 5 giorni lavorativi non hai ancora ricevuto il rimborso, contatta l’as
accessibilityLabel=""
accessibilityRole="header"
accessible={false}
- importantForAccessibility="no-hide-descendants"
+ importantForAccessibility="yes"
style={
{
"flexGrow": 1,
@@ -39269,7 +39289,8 @@ exports[`WalletPaymentOutcomeScreen for all outcomes should render the WalletPay
onStartShouldSetResponder={[Function]}
style={
{
- "alignSelf": "flex-start",
+ "alignSelf": "auto",
+ "flexShrink": 1,
}
}
>
@@ -40592,7 +40613,7 @@ Se dopo 5 giorni lavorativi non hai ancora ricevuto il rimborso, contatta l’as
accessibilityLabel=""
accessibilityRole="header"
accessible={false}
- importantForAccessibility="no-hide-descendants"
+ importantForAccessibility="yes"
style={
{
"flexGrow": 1,
@@ -41138,7 +41159,8 @@ Se il problema persiste, prova a usare un altro metodo o gestore del pagamento.
onStartShouldSetResponder={[Function]}
style={
{
- "alignSelf": "flex-start",
+ "alignSelf": "auto",
+ "flexShrink": 1,
}
}
>
@@ -42581,7 +42603,7 @@ Se dopo 5 giorni lavorativi non hai ancora ricevuto il rimborso, contatta l’as
accessibilityLabel=""
accessibilityRole="header"
accessible={false}
- importantForAccessibility="no-hide-descendants"
+ importantForAccessibility="yes"
style={
{
"flexGrow": 1,
@@ -43188,7 +43210,8 @@ exports[`WalletPaymentOutcomeScreen for all outcomes should render the WalletPay
onStartShouldSetResponder={[Function]}
style={
{
- "alignSelf": "flex-start",
+ "alignSelf": "auto",
+ "flexShrink": 1,
}
}
>
@@ -44631,7 +44654,7 @@ Se dopo 5 giorni lavorativi non hai ancora ricevuto il rimborso, contatta l’as
accessibilityLabel=""
accessibilityRole="header"
accessible={false}
- importantForAccessibility="no-hide-descendants"
+ importantForAccessibility="yes"
style={
{
"flexGrow": 1,
@@ -45204,7 +45227,8 @@ exports[`WalletPaymentOutcomeScreen for all outcomes should render the WalletPay
onStartShouldSetResponder={[Function]}
style={
{
- "alignSelf": "flex-start",
+ "alignSelf": "auto",
+ "flexShrink": 1,
}
}
>
@@ -46527,7 +46551,7 @@ Se dopo 5 giorni lavorativi non hai ancora ricevuto il rimborso, contatta l’as
accessibilityLabel=""
accessibilityRole="header"
accessible={false}
- importantForAccessibility="no-hide-descendants"
+ importantForAccessibility="yes"
style={
{
"flexGrow": 1,
@@ -47142,7 +47166,8 @@ exports[`WalletPaymentOutcomeScreen for all outcomes should render the WalletPay
onStartShouldSetResponder={[Function]}
style={
{
- "alignSelf": "flex-start",
+ "alignSelf": "auto",
+ "flexShrink": 1,
}
}
>
@@ -48465,7 +48490,7 @@ Se dopo 5 giorni lavorativi non hai ancora ricevuto il rimborso, contatta l’as
accessibilityLabel=""
accessibilityRole="header"
accessible={false}
- importantForAccessibility="no-hide-descendants"
+ importantForAccessibility="yes"
style={
{
"flexGrow": 1,
@@ -49052,7 +49077,8 @@ exports[`WalletPaymentOutcomeScreen for all outcomes should render the WalletPay
onStartShouldSetResponder={[Function]}
style={
{
- "alignSelf": "flex-start",
+ "alignSelf": "auto",
+ "flexShrink": 1,
}
}
>
@@ -50375,7 +50401,7 @@ Se dopo 5 giorni lavorativi non hai ancora ricevuto il rimborso, contatta l’as
accessibilityLabel=""
accessibilityRole="header"
accessible={false}
- importantForAccessibility="no-hide-descendants"
+ importantForAccessibility="yes"
style={
{
"flexGrow": 1,
@@ -50982,7 +51008,8 @@ exports[`WalletPaymentOutcomeScreen for all outcomes should render the WalletPay
onStartShouldSetResponder={[Function]}
style={
{
- "alignSelf": "flex-start",
+ "alignSelf": "auto",
+ "flexShrink": 1,
}
}
>
@@ -52305,7 +52332,7 @@ Se dopo 5 giorni lavorativi non hai ancora ricevuto il rimborso, contatta l’as
accessibilityLabel=""
accessibilityRole="header"
accessible={false}
- importantForAccessibility="no-hide-descendants"
+ importantForAccessibility="yes"
style={
{
"flexGrow": 1,
diff --git a/ts/features/pn/components/__test__/__snapshots__/MessageFooter.test.tsx.snap b/ts/features/pn/components/__test__/__snapshots__/MessageFooter.test.tsx.snap
index bc7c13cb82b..620ed249c6b 100644
--- a/ts/features/pn/components/__test__/__snapshots__/MessageFooter.test.tsx.snap
+++ b/ts/features/pn/components/__test__/__snapshots__/MessageFooter.test.tsx.snap
@@ -1081,7 +1081,12 @@ exports[`MessageFooter should match snapshot for visibleEnabled button 1`] = `
onResponderTerminate={[Function]}
onResponderTerminationRequest={[Function]}
onStartShouldSetResponder={[Function]}
- style={{}}
+ style={
+ {
+ "alignSelf": "stretch",
+ "flexShrink": 0,
+ }
+ }
>
@@ -755,7 +756,7 @@ exports[`PaidPaymentScreen should match snapshot 1`] = `
accessibilityLabel=""
accessibilityRole="header"
accessible={false}
- importantForAccessibility="no-hide-descendants"
+ importantForAccessibility="yes"
style={
{
"flexGrow": 1,
diff --git a/ts/features/pushNotifications/screens/__tests__/__snapshots__/OnboardingNotificationsInfoScreenConsent.test.tsx.snap b/ts/features/pushNotifications/screens/__tests__/__snapshots__/OnboardingNotificationsInfoScreenConsent.test.tsx.snap
index 38d9543764f..77c490c958f 100644
--- a/ts/features/pushNotifications/screens/__tests__/__snapshots__/OnboardingNotificationsInfoScreenConsent.test.tsx.snap
+++ b/ts/features/pushNotifications/screens/__tests__/__snapshots__/OnboardingNotificationsInfoScreenConsent.test.tsx.snap
@@ -993,7 +993,12 @@ exports[`OnboardingNotificationsInfoScreenConsent should match snapshot 1`] = `
onResponderTerminate={[Function]}
onResponderTerminationRequest={[Function]}
onStartShouldSetResponder={[Function]}
- style={{}}
+ style={
+ {
+ "alignSelf": "stretch",
+ "flexShrink": 0,
+ }
+ }
testID="settings-btn"
>
["scrollValues"];
+
+export type ScrollAnimationHookProps = {
+ headerConfig?: ComponentProps["headerConfig"];
+ snapOffset?: number;
+};
+
+export const useScrollHeaderAnimation = ({
+ snapOffset,
+ headerConfig
+}: ScrollAnimationHookProps) => {
+ const scrollPositionAbsolute = useSharedValue(0);
+ const scrollPositionPercentage = useSharedValue(0);
+ const theme = useIOTheme();
+ const alertProps = useStatusAlertProps();
+ const navigation = useNavigation();
+
+ const HEADER_BG_COLOR: ColorValue = IOColors[theme["appBackground-primary"]];
+
+ const handleScroll = useAnimatedScrollHandler(
+ ({ contentOffset, layoutMeasurement, contentSize }) => {
+ const scrollPosition = contentOffset.y;
+ const maxScrollHeight = contentSize.height - layoutMeasurement.height;
+ const scrollPercentage =
+ maxScrollHeight > 0 ? scrollPosition / maxScrollHeight : 0;
+
+ scrollPositionAbsolute.value = scrollPosition;
+ scrollPositionPercentage.value = scrollPercentage;
+ }
+ );
+
+ const opacityTransition = useAnimatedStyle(() => ({
+ opacity: interpolate(
+ scrollPositionPercentage.value,
+ [0, GRADIENT_OPACITY_SCROLL_TRIGGER, 1],
+ [1, 1, 0],
+ Extrapolation.CLAMP
+ )
+ }));
+
+ const { colors, locations } = useMemo(
+ () =>
+ easeGradient({
+ colorStops: {
+ 0: { color: hexToRgba(HEADER_BG_COLOR, 0) },
+ 1: { color: HEADER_BG_COLOR }
+ },
+ easing: Easing.ease,
+ extraColorStopsPerTransition: EXTRA_COLOR_STOPS
+ }),
+ [HEADER_BG_COLOR]
+ );
+
+ const ignoreSafeAreaMargin = useMemo(() => {
+ if (alertProps !== undefined) {
+ return true;
+ }
+ return headerConfig?.ignoreSafeAreaMargin;
+ }, [headerConfig?.ignoreSafeAreaMargin, alertProps]);
+
+ useLayoutEffect(() => {
+ const scrollValues: IOViewHeaderScrollValues = {
+ contentOffsetY: scrollPositionAbsolute,
+ triggerOffset: snapOffset ?? 0
+ };
+
+ if (headerConfig) {
+ navigation.setOptions({
+ header: () => (
+
+ ),
+ headerTransparent: headerConfig.transparent
+ });
+ }
+ }, [
+ headerConfig,
+ navigation,
+ scrollPositionAbsolute,
+ snapOffset,
+ ignoreSafeAreaMargin
+ ]);
+
+ return {
+ handleScroll,
+ opacityTransition,
+ scrollPositionAbsolute,
+ scrollPositionPercentage,
+ colors,
+ locations
+ };
+};
diff --git a/ts/screens/authentication/__tests__/__snapshots__/LandingScreen.test.tsx.snap b/ts/screens/authentication/__tests__/__snapshots__/LandingScreen.test.tsx.snap
index 67a683737e4..0f91745645d 100644
--- a/ts/screens/authentication/__tests__/__snapshots__/LandingScreen.test.tsx.snap
+++ b/ts/screens/authentication/__tests__/__snapshots__/LandingScreen.test.tsx.snap
@@ -1481,7 +1481,12 @@ exports[`LandingScreen Should match the snapshot 1`] = `
onResponderTerminate={[Function]}
onResponderTerminationRequest={[Function]}
onStartShouldSetResponder={[Function]}
- style={{}}
+ style={
+ {
+ "alignSelf": "stretch",
+ "flexShrink": 0,
+ }
+ }
testID="landing-button-login-cie"
>
Date: Fri, 28 Feb 2025 16:16:30 +0100
Subject: [PATCH 3/4] fix: [IOPLT-996] Enable quick dev access in the
`IdentificationLockScreen` with screen reader enabled (#6769)
## Short description
This PR allows quick developer access in the `IdentificationLockScreen`
when the screen reader is enabled. Previously you were forced to enter
each single digit of the predefined unlock code (`defaultPin`), making
the developer experience even more miserable.
## List of changes proposed in this pull request
- Remove props that override accessibility features in the dev-only view
## How to test
Run the app in the local environment with VoiceOver/Talkback enabled.
You should be able to access the button with the unlock icon with the
screen reader.
---
ts/screens/modal/components/IdentificationNumberPad.tsx | 5 +----
1 file changed, 1 insertion(+), 4 deletions(-)
diff --git a/ts/screens/modal/components/IdentificationNumberPad.tsx b/ts/screens/modal/components/IdentificationNumberPad.tsx
index 4f6577f8acb..db4663328d1 100644
--- a/ts/screens/modal/components/IdentificationNumberPad.tsx
+++ b/ts/screens/modal/components/IdentificationNumberPad.tsx
@@ -85,9 +85,6 @@ export const IdentificationNumberPad = (
{isDevEnv && (
{
setValue(pin);
}}
- accessibilityLabel={"Insert valid pin button (dev only)"}
+ accessibilityLabel={"Inserisci il PIN predefinito per sviluppatori"}
/>
)}
From 04d2853a3b061562465eca2aab189a5ced8f195b Mon Sep 17 00:00:00 2001
From: Damiano Plebani
Date: Fri, 28 Feb 2025 17:21:39 +0100
Subject: [PATCH 4/4] chore: [IOAPPX-476] Change `StatusBar` style according to
the current color scheme (#6680)
## Short description
This PR adds a `StatusBar` that changes its style according to the color
scheme of the app.
## List of changes proposed in this pull request
- Add a `StatusBar` with a specific color scheme condition in the
`AppStackNavigator`
### Preview
> [!note]
> Please focus on the statusbar on the top while playing the videos
| Before | After |
|--------|--------|
| | |
## How to test
Change the color scheme of your app from **Dev Settings** and randomly
check the headers of the screens. Focus your attention on the screens
with an existing specific `StatusBar` behavior.
---------
Co-authored-by: Emanuele Dall'Ara <71103219+LeleDallas@users.noreply.github.com>
---
ts/RootContainer.tsx | 13 +---
.../BonusCard/BonusCardScreenComponent.tsx | 43 ++++++++++---
ts/components/ui/AppHeader.tsx | 7 +-
ts/components/ui/FocusAwareStatusBar.tsx | 3 +-
.../merchants/ModuleCgnDiscount.tsx | 3 +-
.../merchants/discount/CgnDiscountHeader.tsx | 6 +-
.../bonus/cgn/screens/CgnDetailScreen.tsx | 1 +
.../discount/CgnDiscountDetailScreen.tsx | 8 ++-
.../CgnMerchantCategoriesListScreen.tsx | 6 +-
.../merchants/CgnMerchantsListByCategory.tsx | 64 ++++---------------
ts/features/bonus/cgn/utils/filters.ts | 52 +++++++++++----
ts/features/design-system/DesignSystem.tsx | 43 ++++++-------
.../components/DesignSystemScreen.tsx | 41 ++++--------
.../core/DSDynamicBackground.tsx | 15 ++---
ts/features/fci/components/ErrorComponent.tsx | 10 +--
.../receipts/screens/ReceiptDetailsScreen.tsx | 2 -
ts/navigation/AppStackNavigator.tsx | 29 ++++++---
ts/theme/variables.ts | 2 -
18 files changed, 170 insertions(+), 178 deletions(-)
diff --git a/ts/RootContainer.tsx b/ts/RootContainer.tsx
index 1a15684fa77..fb0fda38843 100644
--- a/ts/RootContainer.tsx
+++ b/ts/RootContainer.tsx
@@ -6,29 +6,27 @@ import {
AppState,
AppStateStatus,
EmitterSubscription,
- NativeEventSubscription,
- StatusBar
+ NativeEventSubscription
} from "react-native";
import SplashScreen from "react-native-splash-screen";
import { connect } from "react-redux";
-import configurePushNotifications from "./features/pushNotifications/utils/configurePushNotification";
import DebugInfoOverlay from "./components/DebugInfoOverlay";
import PagoPATestIndicatorOverlay from "./components/PagoPATestIndicatorOverlay";
import { LightModalRoot } from "./components/ui/LightModal";
+import configurePushNotifications from "./features/pushNotifications/utils/configurePushNotification";
import { setLocale } from "./i18n";
import { IONavigationContainer } from "./navigation/AppStackNavigator";
import RootModal from "./screens/modal/RootModal";
import { applicationChangeState } from "./store/actions/application";
import { setDebugCurrentRouteName } from "./store/actions/debug";
import { navigateBack } from "./store/actions/navigation";
+import { setScreenReaderEnabled } from "./store/actions/preferences";
import { isDebugModeEnabledSelector } from "./store/reducers/debug";
import {
isPagoPATestEnabledSelector,
preferredLanguageSelector
} from "./store/reducers/persistedPreferences";
import { GlobalState } from "./store/reducers/types";
-import customVariables from "./theme/variables";
-import { setScreenReaderEnabled } from "./store/actions/preferences";
type Props = ReturnType & typeof mapDispatchToProps;
@@ -109,11 +107,6 @@ class RootContainer extends PureComponent {
return (
<>
-
-
{/* When debug mode is enabled, the following information
diff --git a/ts/components/BonusCard/BonusCardScreenComponent.tsx b/ts/components/BonusCard/BonusCardScreenComponent.tsx
index c1d587d1f38..500ce5f6361 100644
--- a/ts/components/BonusCard/BonusCardScreenComponent.tsx
+++ b/ts/components/BonusCard/BonusCardScreenComponent.tsx
@@ -1,9 +1,15 @@
-import { HeaderActionProps } from "@pagopa/io-app-design-system";
+import {
+ HeaderActionProps,
+ IOColors,
+ useIOThemeContext
+} from "@pagopa/io-app-design-system";
import { ReactNode } from "react";
import { Dimensions } from "react-native";
import Animated, { useAnimatedRef } from "react-native-reanimated";
import { useHeaderSecondLevel } from "../../hooks/useHeaderSecondLevel";
import { SupportRequestParams } from "../../hooks/useStartSupportRequest";
+import { isAndroid } from "../../utils/platform";
+import FocusAwareStatusBar from "../ui/FocusAwareStatusBar";
import { IOScrollView, IOScrollViewActions } from "../ui/IOScrollView";
import { BonusCard } from "./BonusCard";
@@ -41,10 +47,24 @@ const BonusCardScreenComponent = ({
const screenHeight = Dimensions.get("window").height;
const shouldHideLogo = screenHeight < MIN_HEIGHT_TO_SHOW_FULL_RENDER;
+ const { themeType } = useIOThemeContext();
+
+ const isDark = themeType === "dark";
+ // We need to check if the card is a CGN type to set the background color
+ const isCGNType = !cardProps.isLoading && cardProps.cardBackground;
+ // Custom background color for CGN based on the platform
+ const cgnBackgroundColor = isAndroid ? IOColors.white : IOColors["grey-50"];
+ // If the card is not a CGN type, we set the default header background color as card color
+ const backgroundColor = isCGNType
+ ? cgnBackgroundColor
+ : IOColors["blueIO-50"];
+
useHeaderSecondLevel({
title: title || "",
transparent: true,
supportRequest: true,
+ variant: "neutral",
+ backgroundColor,
faqCategories,
contextualHelpMarkdown,
contextualHelp,
@@ -54,14 +74,19 @@ const BonusCardScreenComponent = ({
});
return (
-
-
- {children}
-
+ <>
+
+
+
+ {children}
+
+ >
);
};
diff --git a/ts/components/ui/AppHeader.tsx b/ts/components/ui/AppHeader.tsx
index a50a69799d7..d141b15baaa 100644
--- a/ts/components/ui/AppHeader.tsx
+++ b/ts/components/ui/AppHeader.tsx
@@ -4,9 +4,8 @@
import { IOStyles, IOVisualCostants } from "@pagopa/io-app-design-system";
import { PropsWithChildren } from "react";
-import { ColorValue, StatusBar, View, ViewProps } from "react-native";
+import { ColorValue, View, ViewProps } from "react-native";
import { useSafeAreaInsets } from "react-native-safe-area-context";
-import variables from "../../theme/variables";
type Props = ViewProps & {
backgroundColor?: ColorValue;
@@ -23,10 +22,6 @@ const AppHeader = (props: PropsWithChildren) => {
backgroundColor: props.backgroundColor
}}
>
-
{
const isFocused = useIsFocused();
- return <>{isFocused && }>;
+
+ return isFocused && ;
};
export default FocusAwareStatusBar;
diff --git a/ts/features/bonus/cgn/components/merchants/ModuleCgnDiscount.tsx b/ts/features/bonus/cgn/components/merchants/ModuleCgnDiscount.tsx
index cd1d921337f..8df3725f91d 100644
--- a/ts/features/bonus/cgn/components/merchants/ModuleCgnDiscount.tsx
+++ b/ts/features/bonus/cgn/components/merchants/ModuleCgnDiscount.tsx
@@ -49,6 +49,7 @@ export const CategoryTag = ({ category }: CategoryTagProps) => {
return O.isSome(categorySpecs) ? (
{
)}
- {discount.name}
+ {discount.name}
{discount.productCategories.map(categoryKey => (
diff --git a/ts/features/bonus/cgn/components/merchants/discount/CgnDiscountHeader.tsx b/ts/features/bonus/cgn/components/merchants/discount/CgnDiscountHeader.tsx
index 82bab063c2f..7f5b1c0abeb 100644
--- a/ts/features/bonus/cgn/components/merchants/discount/CgnDiscountHeader.tsx
+++ b/ts/features/bonus/cgn/components/merchants/discount/CgnDiscountHeader.tsx
@@ -6,7 +6,6 @@ import {
IOStyles,
VSpacer
} from "@pagopa/io-app-design-system";
-import { useHeaderHeight } from "@react-navigation/elements";
import { StyleSheet, View } from "react-native";
import { Discount } from "../../../../../../../definitions/cgn/merchants/Discount";
import I18n from "../../../../../../i18n";
@@ -26,8 +25,6 @@ export const CgnDiscountHeader = ({
? styles.backgroundNewItem
: styles.backgroundDefault;
- const headerHeight = useHeaderHeight();
-
const { isNew, discount, name, productCategories } = discountDetails;
return (
@@ -36,7 +33,6 @@ export const CgnDiscountHeader = ({
style={[
IOStyles.horizontalContentPadding,
{
- paddingTop: headerHeight,
backgroundColor: discountColor.backgroundColor,
paddingBottom: 24
}
@@ -63,7 +59,7 @@ export const CgnDiscountHeader = ({
>
)}
- {name}
+ {name}
{productCategories.map(categoryKey => (
diff --git a/ts/features/bonus/cgn/screens/CgnDetailScreen.tsx b/ts/features/bonus/cgn/screens/CgnDetailScreen.tsx
index c5696e11d74..07c54e42c0e 100644
--- a/ts/features/bonus/cgn/screens/CgnDetailScreen.tsx
+++ b/ts/features/bonus/cgn/screens/CgnDetailScreen.tsx
@@ -196,6 +196,7 @@ const CgnDetailScreen = (props: Props): ReactElement => {
}
cardFooter={
{
},
backgroundColor,
canGoBack: true,
- supportRequest: true
+ supportRequest: true,
+ variant: "neutral"
});
useEffect(() => {
@@ -256,6 +258,10 @@ const CgnDiscountDetailScreen = () => {
if (discountDetails && merchantDetails) {
return (
<>
+
{
+ const theme = useIOTheme();
const insets = useSafeAreaInsets();
const dispatch = useIODispatch();
const [isPullRefresh, setIsPullRefresh] = useState(false);
@@ -110,7 +112,7 @@ export const CgnMerchantCategoriesListScreen = () => {
}
);
}}
- iconColor="grey-300"
+ iconColor={theme["icon-decorative"]}
icon={s.icon}
/>
)
diff --git a/ts/features/bonus/cgn/screens/merchants/CgnMerchantsListByCategory.tsx b/ts/features/bonus/cgn/screens/merchants/CgnMerchantsListByCategory.tsx
index 342b1a5a69b..a2d5e80d50d 100644
--- a/ts/features/bonus/cgn/screens/merchants/CgnMerchantsListByCategory.tsx
+++ b/ts/features/bonus/cgn/screens/merchants/CgnMerchantsListByCategory.tsx
@@ -3,9 +3,7 @@ import {
H3,
HSpacer,
IOColors,
- IOVisualCostants,
Icon,
- VSpacer,
hexToRgba
} from "@pagopa/io-app-design-system";
import { Route, useNavigation, useRoute } from "@react-navigation/native";
@@ -13,18 +11,8 @@ import { pipe } from "fp-ts/lib/function";
import * as O from "fp-ts/lib/Option";
import { useCallback, useEffect, useMemo, useState } from "react";
-import {
- Dimensions,
- LayoutChangeEvent,
- Platform,
- RefreshControl,
- View
-} from "react-native";
-import Animated, {
- useAnimatedScrollHandler,
- useSharedValue
-} from "react-native-reanimated";
-import { useSafeAreaInsets } from "react-native-safe-area-context";
+import { Platform, RefreshControl, View } from "react-native";
+import Animated, { useAnimatedRef } from "react-native-reanimated";
import { Merchant } from "../../../../../../definitions/cgn/merchants/Merchant";
import { ProductCategoryEnum } from "../../../../../../definitions/cgn/merchants/ProductCategory";
import {
@@ -59,23 +47,11 @@ export type CgnMerchantListByCategoryScreenNavigationParams = Readonly<{
}>;
const CgnMerchantsListByCategory = () => {
- const screenHeight = Dimensions.get("window").height;
- const [titleHeight, setTitleHeight] = useState(0);
- const translationY = useSharedValue(0);
+ // const screenHeight = Dimensions.get("window").height;
+ // const translationY = useSharedValue(0);
- const getTitleHeight = (event: LayoutChangeEvent) => {
- const { height } = event.nativeEvent.layout;
- if (titleHeight === 0) {
- setTitleHeight(height - insets.top - IOVisualCostants.headerHeight);
- }
- };
-
- const scrollHandler = useAnimatedScrollHandler(event => {
- // eslint-disable-next-line functional/immutable-data
- translationY.value = event.contentOffset.y;
- });
+ const animatedFlatListRef = useAnimatedRef>();
- const insets = useSafeAreaInsets();
const dispatch = useIODispatch();
const route =
useRoute<
@@ -148,11 +124,10 @@ const CgnMerchantsListByCategory = () => {
)
)
),
- scrollValues: {
- contentOffsetY: translationY,
- triggerOffset: titleHeight
- },
+ enableDiscreteTransition: true,
+ animatedRef: animatedFlatListRef,
backgroundColor: categorySpecs?.colors,
+ variant: categorySpecs?.headerVariant,
supportRequest: true,
secondAction: {
icon: "search",
@@ -191,7 +166,6 @@ const CgnMerchantsListByCategory = () => {
)}
{categorySpecs && (
{
}
]}
>
-
{
const refreshControl = (
{
initLoadingLists();
@@ -241,21 +214,12 @@ const CgnMerchantsListByCategory = () => {
/>
);
- const getPaddingBottom = () => {
- const ELEMENT_HEIGHT = 49;
- const totalListElementsHeight = ELEMENT_HEIGHT * merchantsAll.length;
- const usedVerticalSpace =
- titleHeight + totalListElementsHeight + insets.bottom;
- const availableVerticalSpace = screenHeight - usedVerticalSpace;
-
- return availableVerticalSpace < titleHeight ? availableVerticalSpace : 0;
- };
-
return (
<>
{isError(onlineMerchants) && isError(offlineMerchants) ? (
{
/>
) : (
= {
@@ -22,70 +28,90 @@ export const categories: Record = {
icon: "categCulture",
nameKey: "bonus.cgn.merchantDetail.categories.cultureAndEntertainment",
colors: "#AA338B",
- textColor: "white"
+ textColor: "white",
+ statusBarStyle: "light-content",
+ headerVariant: "contrast"
},
[ProductCategoryEnum.health]: {
type: ProductCategoryEnum.health,
icon: "categWellness",
nameKey: "bonus.cgn.merchantDetail.categories.health",
colors: "#B5D666",
- textColor: "black"
+ textColor: "black",
+ statusBarStyle: "dark-content",
+ headerVariant: "neutral"
},
[ProductCategoryEnum.learning]: {
type: ProductCategoryEnum.learning,
icon: "categLearning",
nameKey: "bonus.cgn.merchantDetail.categories.learning",
colors: "#2A61AE",
- textColor: "white"
+ textColor: "white",
+ statusBarStyle: "light-content",
+ headerVariant: "contrast"
},
[ProductCategoryEnum.sports]: {
type: ProductCategoryEnum.sports,
icon: "categSport",
nameKey: "bonus.cgn.merchantDetail.categories.sport",
colors: "#65BE72",
- textColor: "black"
+ textColor: "black",
+ statusBarStyle: "dark-content",
+ headerVariant: "neutral"
},
[ProductCategoryEnum.home]: {
type: ProductCategoryEnum.home,
icon: "categHome",
nameKey: "bonus.cgn.merchantDetail.categories.home",
colors: "#F8D547",
- textColor: "black"
+ textColor: "black",
+ statusBarStyle: "dark-content",
+ headerVariant: "neutral"
},
[ProductCategoryEnum.telephonyAndInternet]: {
type: ProductCategoryEnum.telephonyAndInternet,
icon: "categTelco",
nameKey: "bonus.cgn.merchantDetail.categories.telco",
colors: "#0089C7",
- textColor: "white"
+ textColor: "white",
+ statusBarStyle: "light-content",
+ headerVariant: "contrast"
},
[ProductCategoryEnum.bankingServices]: {
type: ProductCategoryEnum.bankingServices,
icon: "categFinance",
nameKey: "bonus.cgn.merchantDetail.categories.finance",
colors: "#4F51A3",
- textColor: "white"
+ textColor: "white",
+ statusBarStyle: "light-content",
+ headerVariant: "contrast"
},
[ProductCategoryEnum.travelling]: {
type: ProductCategoryEnum.travelling,
icon: "categTravel",
nameKey: "bonus.cgn.merchantDetail.categories.travel",
colors: "#E02F6E",
- textColor: "white"
+ textColor: "white",
+ statusBarStyle: "light-content",
+ headerVariant: "contrast"
},
[ProductCategoryEnum.sustainableMobility]: {
type: ProductCategoryEnum.sustainableMobility,
icon: "categMobility",
nameKey: "bonus.cgn.merchantDetail.categories.mobility",
colors: "#00AEB1",
- textColor: "black"
+ textColor: "black",
+ statusBarStyle: "dark-content",
+ headerVariant: "neutral"
},
[ProductCategoryEnum.jobOffers]: {
type: ProductCategoryEnum.jobOffers,
icon: "categJobOffers",
nameKey: "bonus.cgn.merchantDetail.categories.job",
colors: "#FAAE56",
- textColor: "black"
+ textColor: "black",
+ statusBarStyle: "dark-content",
+ headerVariant: "neutral"
}
};
diff --git a/ts/features/design-system/DesignSystem.tsx b/ts/features/design-system/DesignSystem.tsx
index 8648b1eaa46..d9501d6bab3 100644
--- a/ts/features/design-system/DesignSystem.tsx
+++ b/ts/features/design-system/DesignSystem.tsx
@@ -8,7 +8,7 @@ import {
VStack,
useIOTheme
} from "@pagopa/io-app-design-system";
-import { SectionList, StatusBar, useColorScheme } from "react-native";
+import { SectionList } from "react-native";
import { IOStyles } from "../../components/core/variables/IOStyles";
import { useScreenEndMargin } from "../../hooks/useScreenEndMargin";
import { useIONavigation } from "../../navigation/params/AppParamsList";
@@ -85,7 +85,6 @@ const DESIGN_SYSTEM_SECTION_DATA: Array = [
export const DesignSystem = () => {
const theme = useIOTheme();
- const colorScheme = useColorScheme();
const navigation = useIONavigation();
const { screenEndMargin } = useScreenEndMargin();
@@ -128,28 +127,22 @@ export const DesignSystem = () => {
) : null;
return (
- <>
-
- `${item.route}-${index}`}
- stickySectionHeadersEnabled={false}
- contentContainerStyle={[
- IOStyles.horizontalContentPadding,
- {
- paddingTop: IOVisualCostants.appMarginDefault,
- paddingBottom: screenEndMargin
- }
- ]}
- renderSectionHeader={renderDSSection}
- renderSectionFooter={renderDSSectionFooter}
- SectionSeparatorComponent={() => }
- renderItem={renderDSNavItem}
- ItemSeparatorComponent={() => }
- sections={DESIGN_SYSTEM_SECTION_DATA}
- />
- >
+ `${item.route}-${index}`}
+ stickySectionHeadersEnabled={false}
+ contentContainerStyle={[
+ IOStyles.horizontalContentPadding,
+ {
+ paddingTop: IOVisualCostants.appMarginDefault,
+ paddingBottom: screenEndMargin
+ }
+ ]}
+ renderSectionHeader={renderDSSection}
+ renderSectionFooter={renderDSSectionFooter}
+ SectionSeparatorComponent={() => }
+ renderItem={renderDSNavItem}
+ ItemSeparatorComponent={() => }
+ sections={DESIGN_SYSTEM_SECTION_DATA}
+ />
);
};
diff --git a/ts/features/design-system/components/DesignSystemScreen.tsx b/ts/features/design-system/components/DesignSystemScreen.tsx
index 9ef596b7b5e..082bda038d9 100644
--- a/ts/features/design-system/components/DesignSystemScreen.tsx
+++ b/ts/features/design-system/components/DesignSystemScreen.tsx
@@ -1,11 +1,7 @@
-import {
- ContentWrapper,
- IOVisualCostants,
- useIOTheme
-} from "@pagopa/io-app-design-system";
+import { ContentWrapper, IOVisualCostants } from "@pagopa/io-app-design-system";
import { ReactNode } from "react";
-import { ScrollView, StatusBar, View, useColorScheme } from "react-native";
+import { ScrollView, View } from "react-native";
import { useScreenEndMargin } from "../../../hooks/useScreenEndMargin";
type Props = {
@@ -15,29 +11,20 @@ type Props = {
};
export const DesignSystemScreen = ({ children, noMargin = false }: Props) => {
- const colorScheme = useColorScheme();
- const theme = useIOTheme();
-
const { screenEndMargin } = useScreenEndMargin();
return (
- <>
-
-
- {noMargin ? (
- {children}
- ) : (
- {children}
- )}
-
- >
+
+ {noMargin ? (
+ {children}
+ ) : (
+ {children}
+ )}
+
);
};
diff --git a/ts/features/design-system/core/DSDynamicBackground.tsx b/ts/features/design-system/core/DSDynamicBackground.tsx
index a4365fd921f..06811b2436d 100644
--- a/ts/features/design-system/core/DSDynamicBackground.tsx
+++ b/ts/features/design-system/core/DSDynamicBackground.tsx
@@ -1,12 +1,12 @@
import {
Avatar,
Body,
+ BodySmall,
ContentWrapper,
H3,
HStack,
IOColors,
IOVisualCostants,
- BodySmall,
RadioGroup,
VStack,
hexToRgba,
@@ -17,17 +17,18 @@ import {
Canvas,
Group,
Image,
- LinearGradient as SkiaLinearGradient,
Mask,
Rect,
+ LinearGradient as SkiaLinearGradient,
useImage,
vec
} from "@shopify/react-native-skia";
import { useCallback, useMemo, useState } from "react";
-import { Dimensions, Platform, View, StatusBar } from "react-native";
+import { Dimensions, Platform, View } from "react-native";
import { ScrollView } from "react-native-gesture-handler";
-import { useSafeAreaInsets } from "react-native-safe-area-context";
import LinearGradient from "react-native-linear-gradient";
+import { useSafeAreaInsets } from "react-native-safe-area-context";
+import FocusAwareStatusBar from "../../../components/ui/FocusAwareStatusBar";
const cdnPath = "https://assets.cdn.io.italia.it/logos/organizations/";
@@ -98,11 +99,7 @@ export const DSDynamicBackground = () => {
return (
<>
-
+