Enforce Text suffix for Text-rendering components (#3407)

* Rm unused

* Add Text suffix to Title/Description

* Add Text suffix to text components

* Add Text suffix to props

* Validate Text components returns
This commit is contained in:
dan 2024-04-04 21:34:55 +01:00 committed by GitHub
parent c190fd58ec
commit 3915bb4316
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
43 changed files with 453 additions and 366 deletions

View file

@ -58,11 +58,11 @@ export const ChooseAccountForm = ({
return (
<FormContainer
testID="chooseAccountForm"
title={<Trans>Select account</Trans>}>
titleText={<Trans>Select account</Trans>}>
<View>
<TextField.Label>
<TextField.LabelText>
<Trans>Sign in as...</Trans>
</TextField.Label>
</TextField.LabelText>
<AccountList
onSelectAccount={onSelect}
onSelectOther={() => onSelectAccount()}

View file

@ -83,11 +83,11 @@ export const ForgotPasswordForm = ({
return (
<FormContainer
testID="forgotPasswordForm"
title={<Trans>Reset password</Trans>}>
titleText={<Trans>Reset password</Trans>}>
<View>
<TextField.Label>
<TextField.LabelText>
<Trans>Hosting provider</Trans>
</TextField.Label>
</TextField.LabelText>
<HostingProvider
serviceUrl={serviceUrl}
onSelectServiceUrl={setServiceUrl}
@ -95,9 +95,9 @@ export const ForgotPasswordForm = ({
/>
</View>
<View>
<TextField.Label>
<TextField.LabelText>
<Trans>Email address</Trans>
</TextField.Label>
</TextField.LabelText>
<TextField.Root>
<TextField.Icon icon={At} />
<TextField.Input

View file

@ -6,12 +6,12 @@ import {Text} from '#/components/Typography'
export function FormContainer({
testID,
title,
titleText,
children,
style,
}: {
testID?: string
title?: React.ReactNode
titleText?: React.ReactNode
children: React.ReactNode
style?: StyleProp<ViewStyle>
}) {
@ -21,9 +21,9 @@ export function FormContainer({
<View
testID={testID}
style={[a.gap_md, a.flex_1, !gtMobile && [a.px_lg, a.py_md], style]}>
{title && !gtMobile && (
{titleText && !gtMobile && (
<Text style={[a.text_xl, a.font_bold, t.atoms.text_contrast_high]}>
{title}
{titleText}
</Text>
)}
{children}

View file

@ -128,11 +128,11 @@ export const LoginForm = ({
const isReady = !!serviceDescription && !!identifier && !!password
return (
<FormContainer testID="loginForm" title={<Trans>Sign in</Trans>}>
<FormContainer testID="loginForm" titleText={<Trans>Sign in</Trans>}>
<View>
<TextField.Label>
<TextField.LabelText>
<Trans>Hosting provider</Trans>
</TextField.Label>
</TextField.LabelText>
<HostingProvider
serviceUrl={serviceUrl}
onSelectServiceUrl={setServiceUrl}
@ -140,9 +140,9 @@ export const LoginForm = ({
/>
</View>
<View>
<TextField.Label>
<TextField.LabelText>
<Trans>Account</Trans>
</TextField.Label>
</TextField.LabelText>
<View style={[a.gap_sm]}>
<TextField.Root>
<TextField.Icon icon={At} />

View file

@ -99,7 +99,7 @@ export const SetNewPasswordForm = ({
return (
<FormContainer
testID="setNewPasswordForm"
title={<Trans>Set new password</Trans>}>
titleText={<Trans>Set new password</Trans>}>
<Text style={[a.leading_snug, a.mb_sm]}>
<Trans>
You will receive an email with a "reset code." Enter that code here,
@ -108,7 +108,7 @@ export const SetNewPasswordForm = ({
</Text>
<View>
<TextField.Label>Reset code</TextField.Label>
<TextField.LabelText>Reset code</TextField.LabelText>
<TextField.Root>
<TextField.Icon icon={Ticket} />
<TextField.Input
@ -131,7 +131,7 @@ export const SetNewPasswordForm = ({
</View>
<View>
<TextField.Label>New password</TextField.Label>
<TextField.LabelText>New password</TextField.LabelText>
<TextField.Root>
<TextField.Icon icon={Lock} />
<TextField.Input

View file

@ -40,7 +40,7 @@ import {Filter_Stroke2_Corner0_Rounded as Filter} from '#/components/icons/Filte
import {Group3_Stroke2_Corner0_Rounded as Group} from '#/components/icons/Group'
import {Person_Stroke2_Corner0_Rounded as Person} from '#/components/icons/Person'
import * as LabelingService from '#/components/LabelingServiceCard'
import {InlineLink, Link} from '#/components/Link'
import {InlineLinkText, Link} from '#/components/Link'
import {Loader} from '#/components/Loader'
import {GlobalLabelPreference} from '#/components/moderation/LabelPreference'
import {Text} from '#/components/Typography'
@ -518,11 +518,11 @@ function PwiOptOut() {
msg`Discourage apps from showing my account to logged-out users`,
)}>
<Toggle.Switch />
<Toggle.Label style={[a.text_md, a.flex_1]}>
<Toggle.LabelText style={[a.text_md, a.flex_1]}>
<Trans>
Discourage apps from showing my account to logged-out users
</Trans>
</Toggle.Label>
</Toggle.LabelText>
</Toggle.Item>
{updateProfile.isPending && <Loader />}
@ -545,9 +545,9 @@ function PwiOptOut() {
</Trans>
</Text>
<InlineLink to="https://blueskyweb.zendesk.com/hc/en-us/articles/15835264007693-Data-Privacy">
<InlineLinkText to="https://blueskyweb.zendesk.com/hc/en-us/articles/15835264007693-Data-Privacy">
<Trans>Learn more about what is public on Bluesky.</Trans>
</InlineLink>
</InlineLinkText>
</View>
</View>
)

View file

@ -1,29 +1,27 @@
import React from 'react'
import {View} from 'react-native'
import {useSafeAreaInsets} from 'react-native-safe-area-context'
import {useLingui} from '@lingui/react'
import {msg} from '@lingui/macro'
import {useLingui} from '@lingui/react'
import {IS_DEV} from '#/env'
import {isWeb} from '#/platform/detection'
import {useOnboardingDispatch} from '#/state/shell'
import {
useTheme,
atoms as a,
useBreakpoints,
web,
native,
flatten,
TextStyleProp,
} from '#/alf'
import {P, leading, Text} from '#/components/Typography'
import {ChevronLeft_Stroke2_Corner0_Rounded as ChevronLeft} from '#/components/icons/Chevron'
import {Button, ButtonIcon} from '#/components/Button'
import {ScrollView} from '#/view/com/util/Views'
import {createPortalGroup} from '#/components/Portal'
import {Context} from '#/screens/Onboarding/state'
import {
atoms as a,
flatten,
native,
TextStyleProp,
useBreakpoints,
useTheme,
web,
} from '#/alf'
import {Button, ButtonIcon} from '#/components/Button'
import {ChevronLeft_Stroke2_Corner0_Rounded as ChevronLeft} from '#/components/icons/Chevron'
import {createPortalGroup} from '#/components/Portal'
import {leading, P, Text} from '#/components/Typography'
import {IS_DEV} from '#/env'
const COL_WIDTH = 500
@ -204,7 +202,7 @@ export function Layout({children}: React.PropsWithChildren<{}>) {
)
}
export function Title({
export function TitleText({
children,
style,
}: React.PropsWithChildren<TextStyleProp>) {
@ -224,7 +222,7 @@ export function Title({
)
}
export function Description({
export function DescriptionText({
children,
style,
}: React.PropsWithChildren<TextStyleProp>) {

View file

@ -6,9 +6,9 @@ import {useLingui} from '@lingui/react'
import {useAnalytics} from '#/lib/analytics/analytics'
import {logEvent} from '#/lib/statsig/statsig'
import {
Description,
DescriptionText,
OnboardingControls,
Title,
TitleText,
} from '#/screens/Onboarding/Layout'
import {Context} from '#/screens/Onboarding/state'
import {FeedCard} from '#/screens/Onboarding/StepAlgoFeeds/FeedCard'
@ -105,15 +105,15 @@ export function StepAlgoFeeds() {
<View style={[a.align_start]}>
<IconCircle icon={ListSparkle} style={[a.mb_2xl]} />
<Title>
<TitleText>
<Trans>Choose your main feeds</Trans>
</Title>
<Description>
</TitleText>
<DescriptionText>
<Trans>
Custom feeds built by the community bring you new experiences and help
you find the content you love.
</Trans>
</Description>
</DescriptionText>
<View style={[a.w_full, a.pb_2xl]}>
<Toggle.Group

View file

@ -10,9 +10,9 @@ import {useSetSaveFeedsMutation} from '#/state/queries/preferences'
import {getAgent} from '#/state/session'
import {useOnboardingDispatch} from '#/state/shell'
import {
Description,
DescriptionText,
OnboardingControls,
Title,
TitleText,
} from '#/screens/Onboarding/Layout'
import {Context} from '#/screens/Onboarding/state'
import {
@ -87,12 +87,12 @@ export function StepFinished() {
<View style={[a.align_start]}>
<IconCircle icon={Check} style={[a.mb_2xl]} />
<Title>
<TitleText>
<Trans>You're ready to go!</Trans>
</Title>
<Description>
</TitleText>
<DescriptionText>
<Trans>We hope you have a wonderful time. Remember, Bluesky is:</Trans>
</Description>
</DescriptionText>
<View style={[a.pt_5xl, a.gap_3xl]}>
<View style={[a.flex_row, a.align_center, a.w_full, a.gap_lg]}>

View file

@ -10,9 +10,9 @@ import {
useSetFeedViewPreferencesMutation,
} from 'state/queries/preferences'
import {
Description,
DescriptionText,
OnboardingControls,
Title,
TitleText,
} from '#/screens/Onboarding/Layout'
import {Context} from '#/screens/Onboarding/state'
import {atoms as a} from '#/alf'
@ -58,12 +58,12 @@ export function StepFollowingFeed() {
<View style={[a.align_start]}>
<IconCircle icon={FilterTimeline} style={[a.mb_2xl]} />
<Title>
<TitleText>
<Trans>Your default feed is "Following"</Trans>
</Title>
<Description style={[a.mb_md]}>
</TitleText>
<DescriptionText style={[a.mb_md]}>
<Trans>It shows posts from the people you follow as they happen.</Trans>
</Description>
</DescriptionText>
<View style={[a.w_full]}>
<Toggle.Item
@ -139,9 +139,9 @@ export function StepFollowingFeed() {
</Toggle.Item>
</View>
<Description style={[a.mt_lg]}>
<DescriptionText style={[a.mt_lg]}>
<Trans>You can change these settings later.</Trans>
</Description>
</DescriptionText>
<OnboardingControls.Portal>
<Button

View file

@ -11,9 +11,9 @@ import {logger} from '#/logger'
import {getAgent} from '#/state/session'
import {useOnboardingDispatch} from '#/state/shell'
import {
Description,
DescriptionText,
OnboardingControls,
Title,
TitleText,
} from '#/screens/Onboarding/Layout'
import {ApiResponseMap, Context} from '#/screens/Onboarding/state'
import {InterestButton} from '#/screens/Onboarding/StepInterests/InterestButton'
@ -163,8 +163,8 @@ export function StepInterests() {
]}
/>
<Title>{title}</Title>
<Description>{description}</Description>
<TitleText>{title}</TitleText>
<DescriptionText>{description}</DescriptionText>
<View style={[a.w_full, a.pt_2xl]}>
{isLoading ? (

View file

@ -113,15 +113,15 @@ export function AdultContentEnabledPref({
)}
<Prompt.Outer control={prompt}>
<Prompt.Title>
<Prompt.TitleText>
<Trans>Adult Content</Trans>
</Prompt.Title>
<Prompt.Description>
</Prompt.TitleText>
<Prompt.DescriptionText>
<Trans>
Due to Apple policies, adult content can only be enabled on the web
after completing sign up.
</Trans>
</Prompt.Description>
</Prompt.DescriptionText>
<Prompt.Actions>
<Prompt.Action onPress={() => prompt.close()} cta={_(msg`OK`)} />
</Prompt.Actions>

View file

@ -9,9 +9,9 @@ import {logEvent} from '#/lib/statsig/statsig'
import {usePreferencesQuery} from '#/state/queries/preferences'
import {usePreferencesSetAdultContentMutation} from 'state/queries/preferences'
import {
Description,
DescriptionText,
OnboardingControls,
Title,
TitleText,
} from '#/screens/Onboarding/Layout'
import {Context} from '#/screens/Onboarding/state'
import {AdultContentEnabledPref} from '#/screens/Onboarding/StepModeration/AdultContentEnabledPref'
@ -56,14 +56,14 @@ export function StepModeration() {
<View style={[a.align_start]}>
<IconCircle icon={EyeSlash} style={[a.mb_2xl]} />
<Title>
<TitleText>
<Trans>You're in control</Trans>
</Title>
<Description style={[a.mb_xl]}>
</TitleText>
<DescriptionText style={[a.mb_xl]}>
<Trans>
Select what you want to see (or not see), and well handle the rest.
</Trans>
</Description>
</DescriptionText>
{!preferences ? (
<View style={[a.pt_md]}>

View file

@ -10,9 +10,9 @@ import {capitalize} from '#/lib/strings/capitalize'
import {useModerationOpts} from '#/state/queries/preferences'
import {useProfilesQuery} from '#/state/queries/profile'
import {
Description,
DescriptionText,
OnboardingControls,
Title,
TitleText,
} from '#/screens/Onboarding/Layout'
import {Context} from '#/screens/Onboarding/state'
import {
@ -136,16 +136,16 @@ export function StepSuggestedAccounts() {
<View style={[a.align_start]}>
<IconCircle icon={At} style={[a.mb_2xl]} />
<Title>
<TitleText>
<Trans>Here are some accounts for you to follow</Trans>
</Title>
<Description>
</TitleText>
<DescriptionText>
{state.interestsStepResults.selectedInterests.length ? (
<Trans>Based on your interest in {interestsText}</Trans>
) : (
<Trans>These are popular accounts you might like:</Trans>
)}
</Description>
</DescriptionText>
<View style={[a.w_full, a.pt_xl]}>
{isLoading ? (

View file

@ -9,9 +9,9 @@ import {capitalize} from '#/lib/strings/capitalize'
import {IS_TEST_USER} from 'lib/constants'
import {useSession} from 'state/session'
import {
Description,
DescriptionText,
OnboardingControls,
Title,
TitleText,
} from '#/screens/Onboarding/Layout'
import {Context} from '#/screens/Onboarding/state'
import {FeedCard} from '#/screens/Onboarding/StepAlgoFeeds/FeedCard'
@ -76,10 +76,10 @@ export function StepTopicalFeeds() {
<View style={[a.align_start]}>
<IconCircle icon={ListMagnifyingGlass} style={[a.mb_2xl]} />
<Title>
<TitleText>
<Trans>Feeds can be topical as well!</Trans>
</Title>
<Description>
</TitleText>
<DescriptionText>
{state.interestsStepResults.selectedInterests.length ? (
<Trans>
Here are some topical feeds based on your interests: {interestsText}
@ -91,7 +91,7 @@ export function StepTopicalFeeds() {
many as you like.
</Trans>
)}
</Description>
</DescriptionText>
<View style={[a.w_full, a.pb_2xl, a.pt_2xl]}>
<Toggle.Group

View file

@ -1,17 +1,16 @@
import React from 'react'
import {View} from 'react-native'
import {AppBskyActorDefs} from '@atproto/api'
import {Trans, msg} from '@lingui/macro'
import {msg, Trans} from '@lingui/macro'
import {useLingui} from '@lingui/react'
import {Shadow} from '#/state/cache/types'
import {pluralize} from '#/lib/strings/helpers'
import {Shadow} from '#/state/cache/types'
import {makeProfileLink} from 'lib/routes/links'
import {formatCount} from 'view/com/util/numeric/format'
import {atoms as a, useTheme} from '#/alf'
import {InlineLinkText} from '#/components/Link'
import {Text} from '#/components/Typography'
import {InlineLink} from '#/components/Link'
export function ProfileHeaderMetrics({
profile,
@ -28,7 +27,7 @@ export function ProfileHeaderMetrics({
<View
style={[a.flex_row, a.gap_sm, a.align_center, a.pb_md]}
pointerEvents="box-none">
<InlineLink
<InlineLinkText
testID="profileHeaderFollowersButton"
style={[a.flex_row, t.atoms.text]}
to={makeProfileLink(profile, 'followers')}
@ -37,8 +36,8 @@ export function ProfileHeaderMetrics({
<Text style={[t.atoms.text_contrast_medium, a.text_md]}>
{pluralizedFollowers}
</Text>
</InlineLink>
<InlineLink
</InlineLinkText>
<InlineLinkText
testID="profileHeaderFollowsButton"
style={[a.flex_row, t.atoms.text]}
to={makeProfileLink(profile, 'follows')}
@ -49,7 +48,7 @@ export function ProfileHeaderMetrics({
following
</Text>
</Trans>
</InlineLink>
</InlineLinkText>
<Text style={[a.font_bold, t.atoms.text, a.text_md]}>
{formatCount(profile.postsCount || 0)}{' '}
<Text style={[t.atoms.text_contrast_medium, a.font_normal, a.text_md]}>

View file

@ -316,13 +316,13 @@ function CantSubscribePrompt({
const {_} = useLingui()
return (
<Prompt.Outer control={control}>
<Prompt.Title>Unable to subscribe</Prompt.Title>
<Prompt.Description>
<Prompt.TitleText>Unable to subscribe</Prompt.TitleText>
<Prompt.DescriptionText>
<Trans>
We're sorry! You can only subscribe to ten labelers, and you've
reached your limit of ten.
</Trans>
</Prompt.Description>
</Prompt.DescriptionText>
<Prompt.Actions>
<Prompt.Action onPress={control.close} cta={_(msg`OK`)} />
</Prompt.Actions>

View file

@ -6,7 +6,7 @@ import {useLingui} from '@lingui/react'
import {atoms as a, useTheme} from '#/alf'
import {CircleInfo_Stroke2_Corner0_Rounded as CircleInfo} from '#/components/icons/CircleInfo'
import {InlineLink} from '#/components/Link'
import {InlineLinkText} from '#/components/Link'
import {Text} from '#/components/Typography'
export const Policies = ({
@ -45,16 +45,16 @@ export const Policies = ({
const els = []
if (tos) {
els.push(
<InlineLink key="tos" to={tos}>
<InlineLinkText key="tos" to={tos}>
{_(msg`Terms of Service`)}
</InlineLink>,
</InlineLinkText>,
)
}
if (pp) {
els.push(
<InlineLink key="pp" to={pp}>
<InlineLinkText key="pp" to={pp}>
{_(msg`Privacy Policy`)}
</InlineLink>,
</InlineLinkText>,
)
}
if (els.length === 2) {

View file

@ -36,9 +36,9 @@ export function StepInfo() {
<View style={[a.gap_md]}>
<FormError error={state.error} />
<View>
<TextField.Label>
<TextField.LabelText>
<Trans>Hosting provider</Trans>
</TextField.Label>
</TextField.LabelText>
<HostingProvider
serviceUrl={state.serviceUrl}
onSelectServiceUrl={v =>
@ -54,9 +54,9 @@ export function StepInfo() {
<>
{state.serviceDescription.inviteCodeRequired && (
<View>
<TextField.Label>
<TextField.LabelText>
<Trans>Invite code</Trans>
</TextField.Label>
</TextField.LabelText>
<TextField.Root>
<TextField.Icon icon={Ticket} />
<TextField.Input
@ -76,9 +76,9 @@ export function StepInfo() {
</View>
)}
<View>
<TextField.Label>
<TextField.LabelText>
<Trans>Email</Trans>
</TextField.Label>
</TextField.LabelText>
<TextField.Root>
<TextField.Icon icon={Envelope} />
<TextField.Input
@ -97,9 +97,9 @@ export function StepInfo() {
</TextField.Root>
</View>
<View>
<TextField.Label>
<TextField.LabelText>
<Trans>Password</Trans>
</TextField.Label>
</TextField.LabelText>
<TextField.Root>
<TextField.Icon icon={Lock} />
<TextField.Input
@ -117,9 +117,9 @@ export function StepInfo() {
</TextField.Root>
</View>
<View>
<DateField.Label>
<DateField.LabelText>
<Trans>Your birth date</Trans>
</DateField.Label>
</DateField.LabelText>
<DateField.DateField
testID="date"
value={DateField.utils.toSimpleDateString(state.dateOfBirth)}

View file

@ -24,7 +24,7 @@ import {StepInfo} from '#/screens/Signup/StepInfo'
import {atoms as a, useBreakpoints, useTheme} from '#/alf'
import {Button, ButtonText} from '#/components/Button'
import {Divider} from '#/components/Divider'
import {InlineLink} from '#/components/Link'
import {InlineLinkText} from '#/components/Link'
import {Text} from '#/components/Typography'
export function Signup({onPressBack}: {onPressBack: () => void}) {
@ -215,9 +215,9 @@ export function Signup({onPressBack}: {onPressBack: () => void}) {
<View style={[a.w_full, a.py_lg]}>
<Text style={[t.atoms.text_contrast_medium]}>
<Trans>Having trouble?</Trans>{' '}
<InlineLink to={FEEDBACK_FORM_URL({email: state.email})}>
<InlineLinkText to={FEEDBACK_FORM_URL({email: state.email})}>
<Trans>Contact support</Trans>
</InlineLink>
</InlineLinkText>
</Text>
</View>
</View>