Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[IOPLT-64] Migrating typography components from io-app #4

Merged
merged 4 commits into from
Jul 4, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .github/actions/setup/action.yml
Original file line number Diff line number Diff line change
Expand Up @@ -24,4 +24,4 @@ runs:
run: |
yarn install --cwd example --frozen-lockfile
yarn install --frozen-lockfile
shell: bash
shell: bash
4 changes: 3 additions & 1 deletion .github/workflows/checks.yml
Original file line number Diff line number Diff line change
Expand Up @@ -15,4 +15,6 @@ jobs:
- id: tsc
run: yarn typecheck
- id: lint
run: yarn lint
run: yarn lint
- name: tests
run: yarn test
2 changes: 2 additions & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -71,6 +71,7 @@
"react": "18.2.0",
"react-native": "0.71.8",
"react-native-builder-bob": "^0.20.0",
"react-test-renderer": "^18.2.0",
"release-it": "^15.0.0",
"typescript": "^4.9.5"
},
Expand Down Expand Up @@ -144,6 +145,7 @@
},
"dependencies": {
"@pagopa/ts-commons": "^12.0.0",
"@types/react-test-renderer": "^18.0.0",
"react-native-linear-gradient": "^2.7.3"
}
}
57 changes: 57 additions & 0 deletions src/components/typography/BaseTypography.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
import React, { useMemo } from "react";
import { StyleProp, Text, TextStyle } from "react-native";
import { IOColors } from "../../core/IOColors";
import { FontFamily, IOFontWeight, makeFontStyleObject } from "../../utils/fonts";

/**
* The specific properties needed to calculate the font style using {@link makeFontStyleObject} (these information
* cannot be included in the default StyleProp<TextStyle>
*/
type BaseTypographyProps = {
weight: IOFontWeight;
color: IOColors;
font?: FontFamily;
isItalic?: boolean;
};

/**
* Decorate the function {@link makeFontStyleObject} with the additional color calculation.
* @param color A value key from {@link IOColors}, transformed here in {@link ColorValue}
* @param args the args of the function {@link makeFontStyleObject}
*/
const calculateTextStyle = (
color: IOColors,
...args: Parameters<typeof makeFontStyleObject>
) => ({
...makeFontStyleObject(...args),
color: IOColors[color]
});

type OwnProps = BaseTypographyProps & {
fontStyle?: StyleProp<TextStyle>;
} & React.ComponentPropsWithRef<typeof Text>;

/**
* `BaseTypography` is the core Typography component used to render a text.
* It accepts all the default text style `StyleProp<TextStyle>` in addition with {@link BaseTypographyProps}
* used to calculate at runtime the platform-dependent styles.
* This component shouldn't be used in the application but only to compose others `Typography elements`.
* @param props
* @constructor
*/
export const BaseTypography: React.FC<OwnProps> = props => {
const fontStyle = useMemo(
() =>
calculateTextStyle(props.color, props.weight, props.isItalic, props.font),
[props.color, props.weight, props.isItalic, props.font]
);
const style = props.style
? [props.style, props.fontStyle, fontStyle]
: [props.fontStyle, fontStyle];

return (
<Text {...props} style={style}>
{props.children}
</Text>
);
};
37 changes: 37 additions & 0 deletions src/components/typography/Body.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
import React from "react";
import { IOColors, IOTheme } from "../../core";
import { FontFamily, IOFontWeight } from "../../utils/fonts";
import { useTypographyFactory } from "./Factory";
import { ExternalTypographyProps, TypographyProps } from "./common";

type PartialAllowedColors = Extract<
IOColors,
"bluegreyDark" | "white" | "blue" | "bluegrey" | "bluegreyLight"
>;
type AllowedColors = PartialAllowedColors | IOTheme["textBody-default"];
type AllowedWeight = IOFontWeight | "Regular" | "SemiBold";

type OwnProps = ExternalTypographyProps<
TypographyProps<AllowedWeight, AllowedColors>
>;

const fontName: FontFamily = "TitilliumWeb";
export const bodyFontSize = 16;
export const bodyLineHeight = 24;
export const bodyDefaultColor: AllowedColors = "bluegrey";
export const bodyDefaultWeight: AllowedWeight = "Regular";

/**
* Typography component to render `Body` text with font size {@link fontSize} and fontFamily {@link fontName}.
* default values (if not defined) are weight: `Regular`, color: `bluegrey`
* @param props`
* @constructor
*/
export const Body: React.FunctionComponent<OwnProps> = props =>
useTypographyFactory<AllowedWeight, AllowedColors>({
...props,
defaultWeight: bodyDefaultWeight,
defaultColor: bodyDefaultColor,
font: fontName,
fontStyle: { fontSize: bodyFontSize, lineHeight: bodyLineHeight }
});
93 changes: 93 additions & 0 deletions src/components/typography/Factory.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,93 @@
import React, { useMemo } from "react";
import { IOColors } from "../../core";
import { IOFontWeight } from "../../utils/fonts";
import { XOR } from "../../utils/types";
import { BaseTypography } from "./BaseTypography";
import {
calculateWeightColor,
RequiredTypographyProps,
TypographyProps
} from "./common";

/**
* Using the DefaultArgumentProps is possible to define a default fallback value for weight and color
* that will be used when the fields weight and color will be undefined.
*/
type DefaultArgumentProps<WeightPropsType, ColorsPropsType> = {
defaultWeight: WeightPropsType;
defaultColor: ColorsPropsType;
};

/**
* Using the DefaultFactoryProps is possible to define a custom factory to calculate the default value for
* weight and color, thus allowing to implement more sophisticated strategies.
*/
type DefaultFactoryProps<WeightPropsType, ColorsPropsType> = {
weightColorFactory: (
weight?: WeightPropsType,
color?: ColorsPropsType
) => RequiredTypographyProps<WeightPropsType, ColorsPropsType>;
};

/**
* Only one type of default props strategy is allowed
*/
type DefaultProps<WeightPropsType, ColorsPropsType> = XOR<
DefaultArgumentProps<WeightPropsType, ColorsPropsType>,
DefaultFactoryProps<WeightPropsType, ColorsPropsType>
>;

/**
* The factory props will include:
* - One of the two DefaultProps
* - The props of {@link BaseTypographyProps} without weight and color
* - The default {@link TypographyProps}
*/
type FactoryProps<WeightPropsType, ColorsPropsType> = TypographyProps<
WeightPropsType,
ColorsPropsType
> &
DefaultProps<WeightPropsType, ColorsPropsType> &
Omit<React.ComponentProps<typeof BaseTypography>, "weight" | "color">;

/**
* Calculate if the props is of type {@link DefaultFactoryProps}
* @param props
*/
function isDefaultFactoryProps<WeightPropsType, ColorsPropsType>(
props:
| DefaultFactoryProps<WeightPropsType, ColorsPropsType>
| DefaultArgumentProps<WeightPropsType, ColorsPropsType>
): props is DefaultFactoryProps<WeightPropsType, ColorsPropsType> {
return (
(props as DefaultFactoryProps<WeightPropsType, ColorsPropsType>)
.weightColorFactory !== undefined
);
}

/**
* Build a {@link BaseTypography} component, calculating the default values for weight and color if undefined.
* The default values can be calculated specifying some fallback values using {@link DefaultArgumentProps}
* or with a factory function to define some custom behaviour using {@link DefaultFactoryProps}
* @param props
*/
export function useTypographyFactory<
WeightPropsType extends IOFontWeight,
ColorsPropsType extends IOColors
>(props: FactoryProps<WeightPropsType, ColorsPropsType>) {
// Use different strategy to calculate the default values, based on DefaultProps
const { weight, color } = useMemo(
() =>
isDefaultFactoryProps(props)
? props.weightColorFactory(props.weight, props.color)
: calculateWeightColor(
props.defaultWeight,
props.defaultColor,
props.weight,
props.color
),
[props]
);

return <BaseTypography weight={weight} color={color} {...props} />;
}
45 changes: 45 additions & 0 deletions src/components/typography/H1.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
import React from "react";
import {
IOColors,
IOColorsStatusForeground,
IOTheme
} from "../../core/IOColors";
import { FontFamily, IOFontWeight } from "../../utils/fonts";
import { useTypographyFactory } from "./Factory";
import { ExternalTypographyProps, TypographyProps } from "./common";

type PartialAllowedColors = Extract<
IOColors,
"bluegreyDark" | "white" | "blue"
>;
type AllowedColors =
| PartialAllowedColors
| IOColorsStatusForeground
| IOTheme["textHeading-default"];
type AllowedWeight = Extract<IOFontWeight, "Bold">;

type OwnProps = ExternalTypographyProps<
TypographyProps<AllowedWeight, AllowedColors>
>;

export const h1FontSize = 26;
export const h1LineHeight = 32;
export const h1DefaultColor: AllowedColors = "bluegreyDark";
export const h1DefaultWeight: AllowedWeight = "Bold";

/**
* Typography component to render H1 text with font size {@link fontSize} and fontFamily {@link fontName}.
* default values(if not defined) are weight: `Bold`, color: `bluegreyDark`
* @param props
* @constructor
*/
export const H1: React.FC<OwnProps> = (props) => {
const fontName: FontFamily = "TitilliumWeb";
return useTypographyFactory<AllowedWeight, AllowedColors>({
...props,
defaultWeight: h1DefaultWeight,
defaultColor: h1DefaultColor,
font: fontName,
fontStyle: { fontSize: h1FontSize, lineHeight: h1LineHeight }
});
};
45 changes: 45 additions & 0 deletions src/components/typography/H2.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
import React from "react";
import {
IOColors,
IOColorsStatusForeground,
IOTheme
} from "../../core/IOColors";
import { FontFamily, IOFontWeight } from "../../utils/fonts";
import { useTypographyFactory } from "./Factory";
import { ExternalTypographyProps, TypographyProps } from "./common";

type PartialAllowedColors = Extract<
IOColors,
"bluegreyDark" | "white" | "blue" | "bluegrey"
>;
type AllowedColors =
| PartialAllowedColors
| IOColorsStatusForeground
| IOTheme["textHeading-default"];
type AllowedWeight = Extract<IOFontWeight, "Bold" | "SemiBold">;

type OwnProps = ExternalTypographyProps<
TypographyProps<AllowedWeight, AllowedColors>
>;

export const h2FontSize = 20;
export const h2LineHeight = 24;
export const h2DefaultColor: AllowedColors = "bluegreyDark";
export const h2DefaultWeight: AllowedWeight = "Bold";

/**
* Typography component to render `H2` text with font size {@link fontSize} and fontFamily {@link fontName}.
* default values(if not defined) are weight: `Bold`, color: `bluegreyDark`
* @param props
* @constructor
*/
export const H2: React.FC<OwnProps> = (props) => {
const fontName: FontFamily = "TitilliumWeb";
return useTypographyFactory<AllowedWeight, AllowedColors>({
...props,
defaultWeight: h2DefaultWeight,
defaultColor: h2DefaultColor,
font: fontName,
fontStyle: { fontSize: h2FontSize, lineHeight: h2LineHeight }
});
};
Loading