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/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/__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/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/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) => ( { return O.isSome(categorySpecs) ? ( { )} -
{discount.name}
+
{discount.name}
{discount.productCategories.map(categoryKey => ( diff --git a/ts/features/bonus/cgn/components/merchants/__tests__/CgnMerchantListSkeleton.test.tsx b/ts/features/bonus/cgn/components/merchants/__tests__/CgnMerchantListSkeleton.test.tsx new file mode 100644 index 00000000000..28df5e4c833 --- /dev/null +++ b/ts/features/bonus/cgn/components/merchants/__tests__/CgnMerchantListSkeleton.test.tsx @@ -0,0 +1,24 @@ +import { render } from "@testing-library/react-native"; +import { CgnMerchantListSkeleton } from "../CgnMerchantListSkeleton"; + +describe("CgnMerchantListSkeleton", () => { + 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/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={

( - - - - - +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(); + }); +}); diff --git a/ts/features/bonus/cgn/screens/discount/CgnDiscountDetailScreen.tsx b/ts/features/bonus/cgn/screens/discount/CgnDiscountDetailScreen.tsx index 18777c4cc17..41d8e659ca4 100644 --- a/ts/features/bonus/cgn/screens/discount/CgnDiscountDetailScreen.tsx +++ b/ts/features/bonus/cgn/screens/discount/CgnDiscountDetailScreen.tsx @@ -46,6 +46,7 @@ import { } from "../../store/reducers/merchants"; import { cgnOtpDataSelector } from "../../store/reducers/otp"; import { getCgnUserAgeRange } from "../../utils/dates"; +import FocusAwareStatusBar from "../../../../../components/ui/FocusAwareStatusBar"; const gradientSafeAreaHeight: IOSpacingScale = 96; @@ -203,7 +204,8 @@ const CgnDiscountDetailScreen = () => { }, 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); @@ -75,19 +76,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) @@ -113,7 +112,7 @@ export const CgnMerchantCategoriesListScreen = () => { } ); }} - iconColor="grey-300" + iconColor={theme["icon-decorative"]} icon={s.icon} /> ) @@ -127,42 +126,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/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) ? ( { /> ) : ( & - 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/bonus/cgn/utils/filters.ts b/ts/features/bonus/cgn/utils/filters.ts index 4b1c12f7037..05090ade129 100644 --- a/ts/features/bonus/cgn/utils/filters.ts +++ b/ts/features/bonus/cgn/utils/filters.ts @@ -1,12 +1,16 @@ +import { + HeaderSecondLevel, + IOCategoryIcons +} from "@pagopa/io-app-design-system"; import * as O from "fp-ts/lib/Option"; -import { IOCategoryIcons } from "@pagopa/io-app-design-system"; -import { TranslationKeys } from "../../../../../locales/locales"; +import { StatusBarProps } from "react-native"; import { ProductCategory, ProductCategoryEnum } from "../../../../../definitions/cgn/merchants/ProductCategory"; -import I18n from "../../../../i18n"; import { ProductCategoryWithNewDiscountsCount } from "../../../../../definitions/cgn/merchants/ProductCategoryWithNewDiscountsCount"; +import { TranslationKeys } from "../../../../../locales/locales"; +import I18n from "../../../../i18n"; export type Category = { type: ProductCategory; @@ -14,6 +18,8 @@ export type Category = { nameKey: TranslationKeys; colors: string; textColor: "white" | "black"; + statusBarStyle: StatusBarProps["barStyle"]; + headerVariant: HeaderSecondLevel["variant"]; }; export const categories: Record = { @@ -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/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..d9501d6bab3 100644 --- a/ts/features/design-system/DesignSystem.tsx +++ b/ts/features/design-system/DesignSystem.tsx @@ -1,14 +1,14 @@ import { BodySmall, Divider, - H3, + H2, IOVisualCostants, ListItemNav, VSpacer, 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(); @@ -108,9 +107,9 @@ export const DesignSystem = () => { section: { title: string; description?: string }; }) => ( -

+

{title} -

+

{description && ( {description} @@ -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 ( <> - + { + 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/payments/receipts/screens/ReceiptDetailsScreen.tsx b/ts/features/payments/receipts/screens/ReceiptDetailsScreen.tsx index 7925bddf738..adea30ae012 100644 --- a/ts/features/payments/receipts/screens/ReceiptDetailsScreen.tsx +++ b/ts/features/payments/receipts/screens/ReceiptDetailsScreen.tsx @@ -5,7 +5,6 @@ import { Dimensions, StyleSheet, View } from "react-native"; import Animated, { useAnimatedRef } from "react-native-reanimated"; import { OriginEnum } from "../../../../../definitions/pagopa/biz-events/InfoNotice"; import { OperationResultScreenContent } from "../../../../components/screens/OperationResultScreenContent"; -import FocusAwareStatusBar from "../../../../components/ui/FocusAwareStatusBar"; import { IOScrollView } from "../../../../components/ui/IOScrollView"; import { useHeaderSecondLevel } from "../../../../hooks/useHeaderSecondLevel"; import I18n from "../../../../i18n"; @@ -175,7 +174,6 @@ const ReceiptDetailsScreen = () => { : undefined } > - {/* The following line is used to show the background color gray that overlay the basic one which is white */} 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/navigation/AppStackNavigator.tsx b/ts/navigation/AppStackNavigator.tsx index 5c0dc1e49ab..8918a0c0cb1 100644 --- a/ts/navigation/AppStackNavigator.tsx +++ b/ts/navigation/AppStackNavigator.tsx @@ -1,5 +1,9 @@ /* eslint-disable functional/immutable-data */ -import { useIOThemeContext } from "@pagopa/io-app-design-system"; +import { + IOColors, + useIOTheme, + useIOThemeContext +} from "@pagopa/io-app-design-system"; import { LinkingOptions, NavigationContainer, @@ -8,13 +12,19 @@ import { import { PropsWithChildren, ReactElement, useEffect, useRef } from "react"; import { Linking, View } from "react-native"; +import { ReactNavigationInstrumentation } from "../App"; import { useStoredExperimentalDesign } from "../common/context/DSExperimentalContext"; +import { useStoredFontPreference } from "../common/context/DSTypefaceContext"; import LoadingSpinnerOverlay from "../components/LoadingSpinnerOverlay"; +import FocusAwareStatusBar from "../components/ui/FocusAwareStatusBar"; import { cgnLinkingOptions } from "../features/bonus/cgn/navigation/navigator"; import { fciLinkingOptions } from "../features/fci/navigation/FciStackNavigator"; import { idPayLinkingOptions } from "../features/idpay/common/navigation/linking"; -import { MESSAGES_ROUTES } from "../features/messages/navigation/routes"; import { IngressScreen } from "../features/ingress/screens/IngressScreen"; +import { useItwLinkingOptions } from "../features/itwallet/navigation/useItwLinkingOptions"; +import { MESSAGES_ROUTES } from "../features/messages/navigation/routes"; +import { SERVICES_ROUTES } from "../features/services/common/navigation/routes"; +import { processUtmLink } from "../features/utmLink"; import { startApplicationInitialization } from "../store/actions/application"; import { setDebugCurrentRouteName } from "../store/actions/debug"; import { useIODispatch, useIOSelector, useIOStore } from "../store/hooks"; @@ -26,17 +36,13 @@ import { IONavigationLightTheme } from "../theme/navigations"; import { isTestEnv } from "../utils/environment"; +import { useOnFirstRender } from "../utils/hooks/useOnFirstRender"; import { IO_INTERNAL_LINK_PREFIX, IO_UNIVERSAL_LINK_PREFIX } from "../utils/navigation"; -import { SERVICES_ROUTES } from "../features/services/common/navigation/routes"; -import { useItwLinkingOptions } from "../features/itwallet/navigation/useItwLinkingOptions"; -import { ReactNavigationInstrumentation } from "../App"; -import { useOnFirstRender } from "../utils/hooks/useOnFirstRender"; -import { processUtmLink } from "../features/utmLink"; -import { useStoredFontPreference } from "../common/context/DSTypefaceContext"; import AuthenticatedStackNavigator from "./AuthenticatedStackNavigator"; +import { linkingSubscription } from "./linkingSubscription"; import NavigationService, { navigationRef, setMainNavigatorReady @@ -44,7 +50,6 @@ import NavigationService, { import NotAuthenticatedStackNavigator from "./NotAuthenticatedStackNavigator"; import { AppParamsList } from "./params/AppParamsList"; import ROUTES from "./routes"; -import { linkingSubscription } from "./linkingSubscription"; type OnStateChangeStateType = Parameters< NonNullable @@ -94,6 +99,7 @@ const InnerNavigationContainer = (props: InnerNavigationContainerProps) => { // Dark/Light Mode const { themeType } = useIOThemeContext(); + const theme = useIOTheme(); const linking: LinkingOptions = { enabled: !isTestEnv, // disable linking in test env @@ -184,6 +190,11 @@ const InnerNavigationContainer = (props: InnerNavigationContainerProps) => { routeNameRef.current = currentRouteName; }} > + {props.children} ); 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" > {isDevEnv && ( { setValue(pin); }} - accessibilityLabel={"Insert valid pin button (dev only)"} + accessibilityLabel={"Inserisci il PIN predefinito per sviluppatori"} /> )} diff --git a/ts/theme/variables.ts b/ts/theme/variables.ts index 12e72de5ae4..ab2a3afa3d5 100644 --- a/ts/theme/variables.ts +++ b/ts/theme/variables.ts @@ -93,8 +93,6 @@ const customVariables = { headerBodyFontSize: 14, headerBodyFontWeight: "600" as FontWeight, - androidStatusBarColor: IOColors.white, - // Text textColor: IOColors["grey-700"], textMessageDetailLinkColor: "#0073E6", diff --git a/yarn.lock b/yarn.lock index b696e96b76f..dfe042fbbe4 100644 --- a/yarn.lock +++ b/yarn.lock @@ -3753,9 +3753,9 @@ __metadata: languageName: node linkType: hard -"@pagopa/io-app-design-system@npm:5.0.2": - version: 5.0.2 - resolution: "@pagopa/io-app-design-system@npm:5.0.2" +"@pagopa/io-app-design-system@npm:5.0.3": + version: 5.0.3 + resolution: "@pagopa/io-app-design-system@npm:5.0.3" dependencies: "@testing-library/jest-native": ^5.4.2 "@types/react-test-renderer": ^18.0.0 @@ -3779,7 +3779,7 @@ __metadata: react-native-reanimated: "*" react-native-safe-area-context: "*" react-native-svg: "*" - checksum: f9ae39263ca0379b6776b681ea0334b95b319668e34e859ac378d344c5f0b7aac197234391ddea70e9e80bac6f01ccc5f6aaada562251c77d07ed38b5a8de99b + checksum: a3b360d8ff7f2fd6f9b712b856f06f74b25f0363feda683c386b818742c67cafcf9c38cbf347180b39ea905efe6077dc675343a37f70efa1fe6f28647932a06a languageName: node linkType: hard @@ -13657,7 +13657,7 @@ __metadata: "@babel/runtime": ^7.20.0 "@gorhom/bottom-sheet": ^4.1.5 "@jambit/eslint-plugin-typed-redux-saga": ^0.4.0 - "@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