Skip to content

Commit

Permalink
[IOPLT-161] Code refinement for HeaderFirstLevel component (#93)
Browse files Browse the repository at this point in the history
## Short description
This PR refines the code for `HeaderFirstLevel` component

## List of changes proposed in this pull request
- Applies the same logic of the header second level to handle the three
actions

## How to test
Check the example app



https://github.com/pagopa/io-app-design-system/assets/3959405/e61dd346-862c-4e12-9a48-99f40b73dee3
  • Loading branch information
CrisTofani authored Oct 4, 2023
1 parent 962aba4 commit 1db5437
Show file tree
Hide file tree
Showing 8 changed files with 146 additions and 31 deletions.
10 changes: 10 additions & 0 deletions example/src/navigation/navigator.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ import Modules from "../pages/Modules";
import { HeaderSecondLevelScreen } from "../pages/HeaderSecondLevel";
import { StaticHeaderSecondLevelScreen } from "../pages/StaticHeaderSecondLevel";
import { Toasts } from "../pages/Toasts";
import { HeaderFirstLevelScreen } from "../pages/HeaderFirstLevel";
import { AppParamsList } from "./params";
import APP_ROUTES from "./routes";

Expand Down Expand Up @@ -189,6 +190,15 @@ const AppNavigator = () => (
}}
/>

<Stack.Screen
name={APP_ROUTES.COMPONENTS.HEADER_FIRST_LEVEL.route}
component={HeaderFirstLevelScreen}
options={{
headerTitle: APP_ROUTES.COMPONENTS.HEADER_FIRST_LEVEL.title,
headerBackTitleVisible: false
}}
/>

<Stack.Screen
name={APP_ROUTES.COMPONENTS.HEADER_SECOND_LEVEL.route}
component={HeaderSecondLevelScreen}
Expand Down
1 change: 1 addition & 0 deletions example/src/navigation/params.ts
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ export type AppParamsList = {
[DESIGN_SYSTEM_ROUTES.COMPONENTS.TAB_NAVIGATION.route]: undefined;
[DESIGN_SYSTEM_ROUTES.COMPONENTS.TEXT_INPUT.route]: undefined;
[DESIGN_SYSTEM_ROUTES.COMPONENTS.FOOTER_WITH_BUTTON.route]: undefined;
[DESIGN_SYSTEM_ROUTES.COMPONENTS.HEADER_FIRST_LEVEL.route]: undefined;
[DESIGN_SYSTEM_ROUTES.COMPONENTS.HEADER_SECOND_LEVEL.route]: undefined;
[DESIGN_SYSTEM_ROUTES.COMPONENTS.HEADER_SECOND_LEVEL_STATIC.route]: undefined;
[DESIGN_SYSTEM_ROUTES.COMPONENTS.TOASTS.route]: undefined;
Expand Down
4 changes: 4 additions & 0 deletions example/src/navigation/routes.ts
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,10 @@ const APP_ROUTES = {
route: "DESIGN_SYSTEM_TAB_NAVIGATION",
title: "Tab Navigation"
},
HEADER_FIRST_LEVEL: {
route: "DESIGN_SYSTEM_HEADER_FIRST_LEVEL",
title: "Header First Level"
},
HEADER_SECOND_LEVEL: {
route: "DESIGN_SYSTEM_HEADER_SECOND_LEVEL",
title: "Header Second Level"
Expand Down
59 changes: 59 additions & 0 deletions example/src/pages/HeaderFirstLevel.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
import * as React from "react";
import { Alert, ScrollView } from "react-native";
import { useSafeAreaInsets } from "react-native-safe-area-context";
import { useNavigation } from "@react-navigation/native";
import {
Body,
ButtonSolid,
H3,
HeaderFirstLevel,
IOVisualCostants,
VSpacer
} from "@pagopa/io-app-design-system";

export const HeaderFirstLevelScreen = () => {
const insets = useSafeAreaInsets();
const navigation = useNavigation();

React.useLayoutEffect(() => {
navigation.setOptions({
header: () => (
<HeaderFirstLevel
title={"Pagina"}
type="singleAction"
firstAction={{
icon: "help",
onPress: () => {
Alert.alert("Contextual Help");
},
accessibilityLabel: ""
}}
/>
)
});
}, [navigation]);

return (
<ScrollView
contentContainerStyle={{
paddingBottom: insets.bottom,
paddingHorizontal: IOVisualCostants.appMarginDefault
}}
scrollEventThrottle={8}
snapToEnd={false}
decelerationRate="normal"
>
<H3>Questo è un titolo lungo, ma lungo lungo davvero, eh!</H3>
<VSpacer />
<ButtonSolid
label="Torna indietro"
onPress={navigation.goBack}
accessibilityLabel=""
/>
<VSpacer />
{[...Array(50)].map((_el, i) => (
<Body key={`body-${i}`}>Repeated text</Body>
))}
</ScrollView>
);
};
55 changes: 42 additions & 13 deletions src/components/layout/HeaderFirstLevel.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,16 +5,43 @@ import { WithTestID } from "../../utils/types";
import { IOStyles, IOVisualCostants, IOColors } from "../../core";
import { H3 } from "../typography";
import { HSpacer } from "../spacer";
import { IconButton } from "../buttons";
import { ActionProp } from "./common";

export type HeaderFirstLevel = WithTestID<{
type CommonProps = WithTestID<{
title: string;
// Accepted components: IconButton
// Don't use any components other than this, please.
firstAction?: React.ReactNode;
secondAction?: React.ReactNode;
thirdAction?: React.ReactNode;
}>;

interface Base extends CommonProps {
type: "base";
firstAction?: never;
secondAction?: never;
thirdAction?: never;
}

interface OneAction extends CommonProps {
type: "singleAction";
firstAction: ActionProp;
secondAction?: never;
thirdAction?: never;
}

interface TwoActions extends CommonProps {
type: "twoActions";
firstAction: ActionProp;
secondAction: ActionProp;
thirdAction?: never;
}

interface ThreeActions extends CommonProps {
type: "threeActions";
firstAction: ActionProp;
secondAction: ActionProp;
thirdAction: ActionProp;
}

export type HeaderFirstLevel = Base | OneAction | TwoActions | ThreeActions;

const HEADER_BG_COLOR: IOColors = "white";

const styles = StyleSheet.create({
Expand All @@ -30,10 +57,11 @@ const styles = StyleSheet.create({

export const HeaderFirstLevel = ({
title,
type,
testID,
firstAction,
secondAction,
thirdAction,
testID
thirdAction
}: HeaderFirstLevel) => {
const insets = useSafeAreaInsets();

Expand All @@ -51,20 +79,21 @@ export const HeaderFirstLevel = ({
{title}
</H3>
<View style={[IOStyles.row, { flexShrink: 0 }]}>
{firstAction}
{secondAction && (
{type !== "base" && <IconButton {...firstAction} color="neutral" />}
{(type === "twoActions" || type === "threeActions") && (
<>
{/* Ideally, with the "gap" flex property,
we can get rid of these ugly constructs */}
+.9+
<HSpacer size={16} />
{secondAction}
<IconButton {...secondAction} color="neutral" />
</>
)}
{thirdAction && (
{type === "threeActions" && (
<>
{/* Same as above */}
<HSpacer size={16} />
{thirdAction}
<IconButton {...thirdAction} color="neutral" />
</>
)}
</View>
Expand Down
40 changes: 22 additions & 18 deletions src/components/layout/HeaderSecondLevel.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -19,11 +19,7 @@ import type { IOSpacer } from "../../core/IOSpacing";
import { WithTestID } from "../../utils/types";
import IconButton from "../buttons/IconButton";
import { makeFontStyleObject } from "../../utils/fonts";

type ActionProp = Pick<
React.ComponentProps<typeof IconButton>,
"icon" | "onPress" | "accessibilityLabel" | "accessibilityHint" | "testID"
>;
import { ActionProp } from "./common";

type ScrollValues = {
contentOffsetY: Animated.SharedValue<number>;
Expand All @@ -41,17 +37,23 @@ type CommonProps = WithTestID<{

interface Base extends CommonProps {
type: "base";
firstAction?: never;
secondAction?: never;
thirdAction?: never;
}

interface OneAction extends CommonProps {
type: "singleAction";
firstAction: ActionProp;
secondAction?: never;
thirdAction?: never;
}

interface TwoActions extends CommonProps {
type: "twoActions";
firstAction: ActionProp;
secondAction: ActionProp;
thirdAction?: never;
}

interface ThreeActions extends CommonProps {
Expand Down Expand Up @@ -98,16 +100,18 @@ const styles = StyleSheet.create({
* @param {HeaderSecondLevel} props - The props of the component
* @returns React Element
*/
export const HeaderSecondLevel = (props: HeaderSecondLevel) => {
const {
scrollValues = undefined,
goBack,
backAccessibilityLabel,
title,
type,
transparent = false,
testID
} = props;
export const HeaderSecondLevel = ({
scrollValues = undefined,
goBack,
backAccessibilityLabel,
title,
type,
transparent = false,
testID,
firstAction,
secondAction,
thirdAction
}: HeaderSecondLevel) => {
const { isExperimental } = useIOExperimentalDesign();
const insets = useSafeAreaInsets();

Expand Down Expand Up @@ -176,7 +180,7 @@ export const HeaderSecondLevel = (props: HeaderSecondLevel) => {
</Animated.Text>
<View style={[IOStyles.row, { flexShrink: 0 }]}>
{type !== "base" ? (
<IconButton {...props.firstAction} color="neutral" />
<IconButton {...firstAction} color="neutral" />
) : (
<HSpacer size={iconBtnSizeSmall as IOSpacer} />
)}
Expand All @@ -185,14 +189,14 @@ export const HeaderSecondLevel = (props: HeaderSecondLevel) => {
{/* Ideally, with the "gap" flex property,
we can get rid of these ugly constructs */}
<HSpacer size={16} />
<IconButton {...props.secondAction} color="neutral" />
<IconButton {...secondAction} color="neutral" />
</>
)}
{type === "threeActions" && (
<>
{/* Same as above */}
<HSpacer size={16} />
<IconButton {...props.thirdAction} color="neutral" />
<IconButton {...thirdAction} color="neutral" />
</>
)}
</View>
Expand Down
7 changes: 7 additions & 0 deletions src/components/layout/common.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
import * as React from "react";
import { IconButton } from "../buttons";

export type ActionProp = Pick<
React.ComponentProps<typeof IconButton>,
"icon" | "onPress" | "accessibilityLabel" | "accessibilityHint" | "testID"
>;
1 change: 1 addition & 0 deletions src/components/layout/index.tsx
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
export * from "./common";
export * from "./GradientScrollView";
export * from "./GradientBottomActions";
export * from "./HeaderFirstLevel";
Expand Down

0 comments on commit 1db5437

Please sign in to comment.