mirror of
https://github.com/logto-io/logto.git
synced 2025-03-31 22:51:25 -05:00
refactor: add allowObjectInHTMLChildren
prop to resolve i18n type issues
This commit is contained in:
parent
e4629f2a5f
commit
fd9b8435ac
21 changed files with 42 additions and 37 deletions
|
@ -31,8 +31,8 @@ const Contact = ({ isOpen, onCancel }: Props) => {
|
|||
<ContactIcon />
|
||||
</div>
|
||||
<div className={styles.text}>
|
||||
<div className={styles.title}>{String(t(title))}</div>
|
||||
<div className={styles.description}>{String(t(description))}</div>
|
||||
<div className={styles.title}>{t(title)}</div>
|
||||
<div className={styles.description}>{t(description)}</div>
|
||||
</div>
|
||||
<div>
|
||||
<Button
|
||||
|
|
|
@ -82,7 +82,7 @@ const Button = ({
|
|||
>
|
||||
{showSpinner && <Spinner className={styles.spinner} />}
|
||||
{icon && <span className={styles.icon}>{icon}</span>}
|
||||
{title && (typeof title === 'string' ? <span>{String(t(title))}</span> : title)}
|
||||
{title && (typeof title === 'string' ? <span>{t(title)}</span> : title)}
|
||||
</button>
|
||||
);
|
||||
};
|
||||
|
|
|
@ -20,10 +20,10 @@ const CardTitle = ({ title, subtitle, size = 'large' }: Props) => {
|
|||
|
||||
return (
|
||||
<div className={classNames(styles.container, styles[size])}>
|
||||
<div className={styles.title}>{typeof title === 'string' ? String(t(title)) : title}</div>
|
||||
<div className={styles.title}>{typeof title === 'string' ? t(title) : title}</div>
|
||||
{subtitle && (
|
||||
<div className={styles.subtitle}>
|
||||
{typeof subtitle === 'string' ? String(t(subtitle)) : subtitle}
|
||||
{typeof subtitle === 'string' ? t(subtitle) : subtitle}
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
import classNames from 'classnames';
|
||||
import React, { MouseEvent, ReactNode } from 'react';
|
||||
import { MouseEvent, ReactNode } from 'react';
|
||||
|
||||
import * as styles from './DropdownItem.module.scss';
|
||||
|
||||
|
@ -22,7 +22,7 @@ const DropdownItem = ({
|
|||
}: Props) => (
|
||||
<li className={classNames(styles.item, styles[type], className)} onClick={onClick}>
|
||||
{icon && <span className={classNames(styles.icon, iconClassName)}>{icon}</span>}
|
||||
{React.isValidElement(children) ? children : String(children)}
|
||||
{children}
|
||||
</li>
|
||||
);
|
||||
|
||||
|
|
|
@ -25,7 +25,7 @@ const FormField = ({ title, children, isRequired, className, tooltip }: Props) =
|
|||
return (
|
||||
<div className={classNames(styles.field, className)}>
|
||||
<div className={styles.headline}>
|
||||
<div className={styles.title}>{typeof title === 'string' ? String(t(title)) : title}</div>
|
||||
<div className={styles.title}>{typeof title === 'string' ? t(title) : title}</div>
|
||||
{tooltip && (
|
||||
<div ref={tipRef} className={styles.icon}>
|
||||
<Tip />
|
||||
|
|
|
@ -20,7 +20,7 @@ const LinkButton = ({ to, title, icon, className }: Props) => {
|
|||
return (
|
||||
<Link to={to} className={classNames(styles.linkButton, className)}>
|
||||
{icon}
|
||||
{typeof title === 'string' ? <span>{String(t(title))}</span> : title}
|
||||
{typeof title === 'string' ? <span>{t(title)}</span> : title}
|
||||
</Link>
|
||||
);
|
||||
};
|
||||
|
|
|
@ -76,9 +76,9 @@ const Radio = ({
|
|||
{type === 'card' && <Check />}
|
||||
{children}
|
||||
{type === 'plain' && <div className={styles.indicator} />}
|
||||
{title && String(t(title))}
|
||||
{title && t(title)}
|
||||
{isDisabled && disabledLabel && (
|
||||
<div className={styles.disabledLabel}>{String(t(disabledLabel))}</div>
|
||||
<div className={styles.disabledLabel}>{t(disabledLabel)}</div>
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
import classNames from 'classnames';
|
||||
import React, { ReactNode, RefObject, useEffect, useLayoutEffect, useRef, useState } from 'react';
|
||||
import { ReactNode, RefObject, useEffect, useLayoutEffect, useRef, useState } from 'react';
|
||||
import { createPortal } from 'react-dom';
|
||||
|
||||
import usePosition, { HorizontalAlignment } from '@/hooks/use-position';
|
||||
|
@ -131,9 +131,7 @@ const Tooltip = ({
|
|||
)}
|
||||
style={{ ...position }}
|
||||
>
|
||||
<div className={styles.content}>
|
||||
{React.isValidElement(content) ? content : String(content)}
|
||||
</div>
|
||||
<div className={styles.content}>{content}</div>
|
||||
</div>,
|
||||
tooltipDom
|
||||
);
|
||||
|
|
|
@ -1,11 +1,14 @@
|
|||
// https://react.i18next.com/latest/typescript#create-a-declaration-file
|
||||
|
||||
// eslint-disable-next-line import/no-unassigned-import
|
||||
import 'react-i18next';
|
||||
import en from '@logto/phrases/lib/locales/en.js';
|
||||
import { Translation, Errors } from '@logto/phrases';
|
||||
import { CustomTypeOptions } from 'react-i18next';
|
||||
|
||||
declare module 'react-i18next' {
|
||||
interface CustomTypeOptions {
|
||||
resources: typeof en;
|
||||
allowObjectInHTMLChildren: true;
|
||||
resources: {
|
||||
translation: Translation;
|
||||
errors: Errors;
|
||||
};
|
||||
}
|
||||
}
|
||||
|
|
|
@ -48,7 +48,7 @@ const ConnectorTabs = ({ target, connectorId }: Props) => {
|
|||
<ConnectorPlatformIcon platform={connector.platform} />
|
||||
</div>
|
||||
)}
|
||||
{connector.platform && String(t(connectorPlatformLabel[connector.platform]))}
|
||||
{connector.platform && t(connectorPlatformLabel[connector.platform])}
|
||||
</Link>
|
||||
))}
|
||||
</div>
|
||||
|
|
|
@ -34,7 +34,7 @@ const ConnectorName = ({ type, connectors, onClickSetup }: Props) => {
|
|||
<ItemPreview
|
||||
title={
|
||||
<div className={styles.previewTitle}>
|
||||
<div>{String(t(connectorTitlePlaceHolder[type]))}</div>
|
||||
<div>{t(connectorTitlePlaceHolder[type])}</div>
|
||||
{type !== ConnectorType.Social && (
|
||||
<Button title="general.set_up" onClick={onClickSetup} />
|
||||
)}
|
||||
|
@ -63,7 +63,7 @@ const ConnectorName = ({ type, connectors, onClickSetup }: Props) => {
|
|||
platform && (
|
||||
<div key={id} className={styles.platform}>
|
||||
<ConnectorPlatformIcon platform={platform} />
|
||||
{String(t(`${connectorPlatformLabel[platform]}`))}
|
||||
{t(`${connectorPlatformLabel[platform]}`)}
|
||||
</div>
|
||||
)
|
||||
)}
|
||||
|
|
|
@ -36,7 +36,7 @@ const ConnectorRow = ({ type, connectors, onClickSetup }: Props) => {
|
|||
<td>
|
||||
<ConnectorName type={type} connectors={connectors} onClickSetup={onClickSetup} />
|
||||
</td>
|
||||
<td>{String(t(connectorTitlePlaceHolder[type]))}</td>
|
||||
<td>{t(connectorTitlePlaceHolder[type])}</td>
|
||||
<td>
|
||||
{inUse !== undefined && (
|
||||
<Status status={inUse ? 'enabled' : 'disabled'}>
|
||||
|
|
|
@ -29,7 +29,7 @@ const Block = ({ variant = 'default', count, delta, title, tooltip }: Props) =>
|
|||
return (
|
||||
<Card className={classNames(styles.block, styles[variant])}>
|
||||
<div className={styles.title}>
|
||||
{String(t(title))}
|
||||
{t(title)}
|
||||
{tooltip && (
|
||||
<div ref={tipRef} className={styles.icon}>
|
||||
<Tip />
|
||||
|
|
|
@ -53,8 +53,8 @@ const GetStarted = () => {
|
|||
{!isComplete && <CardIcon className={styles.icon} />}
|
||||
{isComplete && <CompleteIndicator className={styles.icon} />}
|
||||
<div className={styles.wrapper}>
|
||||
<div className={styles.title}>{String(t(title))}</div>
|
||||
<div className={styles.subtitle}>{String(t(subtitle))}</div>
|
||||
<div className={styles.title}>{t(title)}</div>
|
||||
<div className={styles.subtitle}>{t(subtitle)}</div>
|
||||
</div>
|
||||
<Button
|
||||
className={styles.button}
|
||||
|
|
|
@ -62,17 +62,17 @@ const Main = () => {
|
|||
<div className={styles.app}>
|
||||
<div className={styles.card}>
|
||||
{congratsIcon && <img src={congratsIcon} alt="Congrats" />}
|
||||
<div className={styles.title}>{String(t('title'))}</div>
|
||||
<div className={styles.text}>{String(t('subtitle'))}</div>
|
||||
<div className={styles.title}>{t('title')}</div>
|
||||
<div className={styles.text}>{t('subtitle')}</div>
|
||||
<div className={styles.infoCard}>
|
||||
{user.username && (
|
||||
<div>
|
||||
{String(t('username'))}
|
||||
{t('username')}
|
||||
<span>{user.username}</span>
|
||||
</div>
|
||||
)}
|
||||
<div>
|
||||
{String(t('user_id'))}
|
||||
{t('user_id')}
|
||||
<span>{user.sub}</span>
|
||||
</div>
|
||||
</div>
|
||||
|
@ -80,7 +80,7 @@ const Main = () => {
|
|||
className={styles.button}
|
||||
onClick={async () => signOut(`${window.location.origin}/demo-app`)}
|
||||
>
|
||||
{String(t('sign_out'))}
|
||||
{t('sign_out')}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
|
|
@ -1,8 +1,11 @@
|
|||
// https://react.i18next.com/latest/typescript#create-a-declaration-file
|
||||
|
||||
import { Translation, Errors } from '@logto/phrases';
|
||||
import { CustomTypeOptions } from 'react-i18next';
|
||||
|
||||
declare module 'react-i18next' {
|
||||
interface CustomTypeOptions {
|
||||
allowObjectInHTMLChildren: true;
|
||||
resources: {
|
||||
translation: Translation;
|
||||
errors: Errors;
|
|
@ -1,5 +1,5 @@
|
|||
import classNames from 'classnames';
|
||||
import React, { ReactNode } from 'react';
|
||||
import { ReactNode } from 'react';
|
||||
|
||||
import * as styles from './index.module.scss';
|
||||
|
||||
|
@ -34,7 +34,7 @@ const Button = ({
|
|||
type={htmlType}
|
||||
onClick={onClick}
|
||||
>
|
||||
{React.isValidElement(children) ? children : String(children)}
|
||||
{children}
|
||||
</button>
|
||||
);
|
||||
|
||||
|
|
|
@ -14,7 +14,7 @@ const Divider = ({ className, label }: Props) => {
|
|||
return (
|
||||
<div className={classNames(styles.divider, className)}>
|
||||
<i className={styles.line} />
|
||||
{label && String(t(label))}
|
||||
{label && t(label)}
|
||||
<i className={styles.line} />
|
||||
</div>
|
||||
);
|
||||
|
|
|
@ -19,14 +19,14 @@ const TextLink = ({ className, children, text, type = 'primary', to, ...rest }:
|
|||
if (to) {
|
||||
return (
|
||||
<Link className={classNames(styles.link, styles[type], className)} to={to}>
|
||||
{children ?? (text ? String(t(text)) : '')}
|
||||
{children ?? (text ? t(text) : '')}
|
||||
</Link>
|
||||
);
|
||||
}
|
||||
|
||||
return (
|
||||
<a className={classNames(styles.link, styles[type], className)} {...rest} rel="noreferrer">
|
||||
{children ?? (text ? String(t(text)) : '')}
|
||||
{children ?? (text ? t(text) : '')}
|
||||
</a>
|
||||
);
|
||||
};
|
||||
|
|
1
packages/ui/src/include.d/react-i18next.d.ts
vendored
1
packages/ui/src/include.d/react-i18next.d.ts
vendored
|
@ -6,6 +6,7 @@ import en from '@logto/phrases-ui/lib/locales/en.js';
|
|||
|
||||
declare module 'react-i18next' {
|
||||
interface CustomTypeOptions {
|
||||
allowObjectInHTMLChildren: true;
|
||||
resources: typeof en;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -24,7 +24,7 @@ const ErrorPage = ({ title = 'description.not_found', message, rawMessage }: Pro
|
|||
<NavBar />
|
||||
<div className={styles.container}>
|
||||
<ErrorImage />
|
||||
<div className={styles.title}>{String(t(title))}</div>
|
||||
<div className={styles.title}>{t(title)}</div>
|
||||
{errorMessage && <div className={styles.message}>{String(errorMessage)}</div>}
|
||||
</div>
|
||||
<Button
|
||||
|
|
Loading…
Add table
Reference in a new issue