mirror of
https://github.com/logto-io/logto.git
synced 2025-04-07 23:01:25 -05:00
refactor(console): provide default rel
value for external links (#5000)
This commit is contained in:
parent
e49e92fb6f
commit
152d2815c7
44 changed files with 175 additions and 104 deletions
|
@ -42,9 +42,9 @@ function BillInfo({ cost, isManagePaymentVisible }: Props) {
|
|||
components={{
|
||||
a: (
|
||||
<TextLink
|
||||
href="https://blog.logto.io/logto-pricing-model"
|
||||
target="_blank"
|
||||
className={styles.articleLink}
|
||||
href="https://blog.logto.io/logto-pricing-model"
|
||||
targetBlank="noopener"
|
||||
/>
|
||||
),
|
||||
}}
|
||||
|
|
|
@ -114,7 +114,7 @@ function BasicForm({
|
|||
a: (
|
||||
<TextLink
|
||||
href={getDocumentationUrl('/docs/references/connectors/#target')}
|
||||
target="_blank"
|
||||
targetBlank="noopener"
|
||||
onClick={closeTipHandler}
|
||||
/>
|
||||
),
|
||||
|
|
|
@ -103,9 +103,8 @@ function PlanCardItem({ plan, onSelect }: Props) {
|
|||
<TextLink
|
||||
isTrailingIcon
|
||||
href={pricingLink}
|
||||
targetBlank="noopener"
|
||||
icon={<ArrowRight className={styles.linkIcon} />}
|
||||
target="_blank"
|
||||
rel="noopener"
|
||||
className={styles.link}
|
||||
>
|
||||
<DynamicT forKey="upsell.create_tenant.view_all_features" />
|
||||
|
|
|
@ -67,9 +67,7 @@ function SelectTenantPlanModal({ tenantData, onClose }: Props) {
|
|||
title="upsell.create_tenant.title"
|
||||
subtitle={
|
||||
<DangerousRaw>
|
||||
<Trans
|
||||
components={{ a: <TextLink href={pricingLink} target="_blank" rel="noopener" /> }}
|
||||
>
|
||||
<Trans components={{ a: <TextLink href={pricingLink} targetBlank="noopener" /> }}>
|
||||
{t('upsell.create_tenant.description')}
|
||||
</Trans>
|
||||
</DangerousRaw>
|
||||
|
|
|
@ -3,6 +3,7 @@ import type { ReactNode } from 'react';
|
|||
|
||||
import DynamicT from '@/ds-components/DynamicT';
|
||||
import TextLink from '@/ds-components/TextLink';
|
||||
import type { Props as TextLinkProps } from '@/ds-components/TextLink';
|
||||
|
||||
import FormCardLayout from './FormCardLayout';
|
||||
import * as styles from './index.module.scss';
|
||||
|
@ -12,8 +13,7 @@ type Props = {
|
|||
tag?: ReactNode;
|
||||
description?: AdminConsoleKey;
|
||||
descriptionInterpolation?: Record<string, unknown>;
|
||||
learnMoreLink?: string;
|
||||
learnMoreLinkText?: AdminConsoleKey;
|
||||
learnMoreLink?: Pick<TextLinkProps, 'href' | 'targetBlank'> & { linkText?: AdminConsoleKey };
|
||||
children: ReactNode;
|
||||
};
|
||||
|
||||
|
@ -23,7 +23,6 @@ function FormCard({
|
|||
description,
|
||||
descriptionInterpolation,
|
||||
learnMoreLink,
|
||||
learnMoreLinkText,
|
||||
children,
|
||||
}: Props) {
|
||||
return (
|
||||
|
@ -37,11 +36,11 @@ function FormCard({
|
|||
{description && (
|
||||
<div className={styles.description}>
|
||||
<DynamicT forKey={description} interpolation={descriptionInterpolation} />
|
||||
{learnMoreLink && (
|
||||
{learnMoreLink?.href && (
|
||||
<>
|
||||
{' '}
|
||||
<TextLink href={learnMoreLink} target="_blank" rel="noopener">
|
||||
<DynamicT forKey={learnMoreLinkText ?? 'general.learn_more'} />
|
||||
<TextLink href={learnMoreLink.href} targetBlank={learnMoreLink.targetBlank}>
|
||||
<DynamicT forKey={learnMoreLink.linkText ?? 'general.learn_more'} />
|
||||
</TextLink>
|
||||
</>
|
||||
)}
|
||||
|
|
|
@ -83,7 +83,7 @@ function Guide({ className, guideId, isEmpty, isLoading, onClose }: Props) {
|
|||
);
|
||||
},
|
||||
a: ({ children, ...props }) => (
|
||||
<TextLink {...props} target="_blank" rel="noopener noreferrer">
|
||||
<TextLink {...props} targetBlank>
|
||||
{children}
|
||||
</TextLink>
|
||||
),
|
||||
|
|
|
@ -165,7 +165,10 @@ function PermissionsTable({
|
|||
imageDark={<PermissionsEmptyDark />}
|
||||
title="permissions.placeholder_title"
|
||||
description="permissions.placeholder_description"
|
||||
learnMoreLink={getDocumentationUrl('/docs/recipes/rbac/manage-permissions-and-roles')}
|
||||
learnMoreLink={{
|
||||
href: getDocumentationUrl('/docs/recipes/rbac/manage-permissions-and-roles'),
|
||||
targetBlank: 'noopener',
|
||||
}}
|
||||
action={
|
||||
<Button
|
||||
title={createButtonTitle}
|
||||
|
|
|
@ -12,7 +12,7 @@ function DocumentNavButton() {
|
|||
return (
|
||||
<TextLink
|
||||
href={documentationSiteUrl}
|
||||
target="_blank"
|
||||
targetBlank="noopener"
|
||||
className={classNames(styles.textLink, styles.documentNavButton)}
|
||||
icon={<DocumentIcon className={styles.icon} />}
|
||||
>
|
||||
|
|
|
@ -66,10 +66,10 @@ function TenantEnvMigrationModal() {
|
|||
footer={
|
||||
<>
|
||||
<LinkButton
|
||||
targetBlank
|
||||
title="tenants.dev_tenant_migration.about_tenant_type"
|
||||
size="large"
|
||||
href={getDocumentationUrl(envTagsFeatureLink)}
|
||||
targetBlank="noopener"
|
||||
/>
|
||||
<Button title="general.got_it" size="large" type="primary" onClick={onClose} />
|
||||
</>
|
||||
|
|
|
@ -113,6 +113,8 @@ type LinkProps = Omit<HTMLProps<HTMLAnchorElement>, 'type' | 'size' | 'title' |
|
|||
*
|
||||
* - When it's `true`, the `rel` attribute will be set to `noopener noreferrer`.
|
||||
* - When it's `noopener`, the `rel` attribute will be set to `noopener`.
|
||||
*
|
||||
* Typically, when navigating to Logto's website (official site, blog, documentation, etc.), use 'noopener'.
|
||||
*/
|
||||
targetBlank?: boolean | 'noopener';
|
||||
type?: ButtonType;
|
||||
|
|
|
@ -5,6 +5,7 @@ import type { ReactElement } from 'react';
|
|||
import { useTranslation } from 'react-i18next';
|
||||
|
||||
import FeatureTag from '@/components/FeatureTag';
|
||||
import type { Props as TextLinkProps } from '@/ds-components/TextLink';
|
||||
|
||||
import type DangerousRaw from '../DangerousRaw';
|
||||
import DynamicT from '../DynamicT';
|
||||
|
@ -16,7 +17,7 @@ export type Props = {
|
|||
title: AdminConsoleKey | ReactElement<typeof DangerousRaw>;
|
||||
subtitle?: AdminConsoleKey | ReactElement<typeof DangerousRaw>;
|
||||
size?: 'small' | 'medium' | 'large';
|
||||
learnMoreLink?: string;
|
||||
learnMoreLink?: Pick<TextLinkProps, 'href' | 'targetBlank'>;
|
||||
isWordWrapEnabled?: boolean;
|
||||
className?: string;
|
||||
/** If a beta tag should be shown next to the title. */
|
||||
|
@ -56,8 +57,12 @@ function CardTitle({
|
|||
{subtitle && (
|
||||
<span>{typeof subtitle === 'string' ? <DynamicT forKey={subtitle} /> : subtitle}</span>
|
||||
)}
|
||||
{learnMoreLink && (
|
||||
<TextLink href={learnMoreLink} target="_blank" className={styles.learnMore}>
|
||||
{learnMoreLink?.href && (
|
||||
<TextLink
|
||||
href={learnMoreLink.href}
|
||||
targetBlank={learnMoreLink.targetBlank}
|
||||
className={styles.learnMore}
|
||||
>
|
||||
{t('general.learn_more')}
|
||||
</TextLink>
|
||||
)}
|
||||
|
|
|
@ -6,6 +6,7 @@ import type { ReactNode, Ref } from 'react';
|
|||
import Info from '@/assets/icons/info.svg';
|
||||
import Error from '@/assets/icons/toast-error.svg';
|
||||
import Success from '@/assets/icons/toast-success.svg';
|
||||
import type { Props as TextLinkProps } from '@/ds-components/TextLink';
|
||||
|
||||
import Button from '../Button';
|
||||
import DynamicT from '../DynamicT';
|
||||
|
@ -18,6 +19,7 @@ type Props = {
|
|||
children?: ReactNode;
|
||||
action?: AdminConsoleKey;
|
||||
href?: string;
|
||||
hrefTargetBlank?: TextLinkProps['targetBlank'];
|
||||
onClick?: () => void;
|
||||
variant?: 'plain' | 'shadow';
|
||||
hasIcon?: boolean;
|
||||
|
@ -48,6 +50,7 @@ function InlineNotification(
|
|||
children,
|
||||
action,
|
||||
href,
|
||||
hrefTargetBlank,
|
||||
onClick,
|
||||
severity = 'info',
|
||||
variant = 'plain',
|
||||
|
@ -74,7 +77,7 @@ function InlineNotification(
|
|||
)}
|
||||
<div className={styles.content}>{children}</div>
|
||||
{action && href && (
|
||||
<TextLink to={href}>
|
||||
<TextLink to={href} targetBlank={hrefTargetBlank}>
|
||||
<DynamicT forKey={action} />
|
||||
</TextLink>
|
||||
)}
|
||||
|
|
|
@ -3,6 +3,7 @@ import { Theme } from '@logto/schemas';
|
|||
import type { ReactNode } from 'react';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
|
||||
import type { Props as TextLinkProps } from '@/ds-components/TextLink';
|
||||
import useTheme from '@/hooks/use-theme';
|
||||
|
||||
import DynamicT from '../DynamicT';
|
||||
|
@ -15,7 +16,7 @@ type Props = {
|
|||
imageDark: ReactNode;
|
||||
title: AdminConsoleKey;
|
||||
description: AdminConsoleKey;
|
||||
learnMoreLink?: string;
|
||||
learnMoreLink?: Pick<TextLinkProps, 'href' | 'targetBlank'>;
|
||||
action: ReactNode;
|
||||
};
|
||||
|
||||
|
@ -31,10 +32,10 @@ function TablePlaceholder({ image, imageDark, title, description, learnMoreLink,
|
|||
</div>
|
||||
<div className={styles.description}>
|
||||
<DynamicT forKey={description} />
|
||||
{learnMoreLink && (
|
||||
{learnMoreLink?.href && (
|
||||
<>
|
||||
{' '}
|
||||
<TextLink href={learnMoreLink} target="_blank" rel="noopener">
|
||||
<TextLink href={learnMoreLink.href} targetBlank={learnMoreLink.targetBlank}>
|
||||
{t('general.learn_more')}
|
||||
</TextLink>
|
||||
</>
|
||||
|
|
|
@ -1,26 +1,59 @@
|
|||
import classNames from 'classnames';
|
||||
import type { AnchorHTMLAttributes, ReactNode } from 'react';
|
||||
import { useMemo, type AnchorHTMLAttributes, type ReactNode } from 'react';
|
||||
import type { LinkProps } from 'react-router-dom';
|
||||
import { Link } from 'react-router-dom';
|
||||
|
||||
// Used in the docs
|
||||
// eslint-disable-next-line unused-imports/no-unused-imports
|
||||
import { LinkButton } from '@/ds-components/Button';
|
||||
import useTenantPathname from '@/hooks/use-tenant-pathname';
|
||||
|
||||
import * as styles from './index.module.scss';
|
||||
|
||||
type Props = AnchorHTMLAttributes<HTMLAnchorElement> &
|
||||
export type Props = AnchorHTMLAttributes<HTMLAnchorElement> &
|
||||
Partial<LinkProps> & {
|
||||
icon?: ReactNode;
|
||||
isTrailingIcon?: boolean;
|
||||
/**
|
||||
* If the link will be opened in a new tab. This prop will override the `target`
|
||||
* and `rel` attributes.
|
||||
*
|
||||
* - When it's `true`, the `rel` attribute will be set to `noopener noreferrer`.
|
||||
* - When it's `noopener`, the `rel` attribute will be set to `noopener`.
|
||||
*
|
||||
* Typically, when navigating to Logto's website (official site, blog, documentation, etc.), use 'noopener'.
|
||||
*
|
||||
* Note: This prop is align with the `targetBlank` prop of {@link LinkButton}, they share the same logic.
|
||||
*/
|
||||
targetBlank?: boolean | 'noopener';
|
||||
};
|
||||
|
||||
function TextLink({ to, children, icon, isTrailingIcon = false, className, ...rest }: Props) {
|
||||
function TextLink({
|
||||
to,
|
||||
children,
|
||||
icon,
|
||||
isTrailingIcon = false,
|
||||
className,
|
||||
targetBlank,
|
||||
...rest
|
||||
}: Props) {
|
||||
const { getTo } = useTenantPathname();
|
||||
|
||||
const styleClassNames = classNames(styles.link, isTrailingIcon && styles.trailingIcon, className);
|
||||
const props = useMemo(
|
||||
() => ({
|
||||
...rest,
|
||||
className: classNames(styles.link, isTrailingIcon && styles.trailingIcon, className),
|
||||
...(Boolean(targetBlank) && {
|
||||
rel: typeof targetBlank === 'string' ? targetBlank : 'noopener noreferrer',
|
||||
target: '_blank',
|
||||
}),
|
||||
}),
|
||||
[className, isTrailingIcon, rest, targetBlank]
|
||||
);
|
||||
|
||||
if (to) {
|
||||
return (
|
||||
<Link to={getTo(to)} className={styleClassNames} {...rest}>
|
||||
<Link to={getTo(to)} {...props}>
|
||||
{icon}
|
||||
{children}
|
||||
</Link>
|
||||
|
@ -28,7 +61,7 @@ function TextLink({ to, children, icon, isTrailingIcon = false, className, ...re
|
|||
}
|
||||
|
||||
return (
|
||||
<a className={styleClassNames} {...rest}>
|
||||
<a {...props}>
|
||||
{icon}
|
||||
{children}
|
||||
</a>
|
||||
|
|
|
@ -23,8 +23,8 @@ function ApplicationCredentials() {
|
|||
components={{
|
||||
a: (
|
||||
<TextLink
|
||||
targetBlank
|
||||
href="https://openid.net/specs/openid-connect-core-1_0.html"
|
||||
target="_blank"
|
||||
onClick={closeTipHandler}
|
||||
/>
|
||||
),
|
||||
|
|
|
@ -29,10 +29,10 @@ export default function Sample() {
|
|||
</hgroup>
|
||||
<Spacer />
|
||||
<LinkButton
|
||||
targetBlank
|
||||
type="outline"
|
||||
href={appendPath(new URL(githubOrgLink), sample.repo, 'tree/HEAD', sample.path).href}
|
||||
title={<DangerousRaw>Check out sample</DangerousRaw>}
|
||||
targetBlank="noopener"
|
||||
/>
|
||||
</aside>
|
||||
);
|
||||
|
|
|
@ -11,19 +11,14 @@ function FurtherReadings(props: Props, ref?: Ref<HTMLDivElement>) {
|
|||
<Step ref={ref} {...props}>
|
||||
<ul>
|
||||
<li>
|
||||
<TextLink
|
||||
target="blank"
|
||||
rel="noopener"
|
||||
href="https://docs.logto.io/docs/recipes/customize-sie/"
|
||||
>
|
||||
<TextLink href="https://docs.logto.io/docs/recipes/customize-sie/" targetBlank="noopener">
|
||||
Customize sign-in experience
|
||||
</TextLink>
|
||||
</li>
|
||||
<li>
|
||||
<TextLink
|
||||
target="blank"
|
||||
rel="noopener"
|
||||
href="https://docs.logto.io/docs/recipes/protect-your-api/"
|
||||
targetBlank="noopener"
|
||||
>
|
||||
Protect your API
|
||||
</TextLink>
|
||||
|
|
|
@ -72,7 +72,10 @@ function CreatePermissionModal({ resourceId, totalResourceCount, onClose }: Prop
|
|||
<ModalLayout
|
||||
title="api_resource_details.permission.create_title"
|
||||
subtitle="api_resource_details.permission.create_subtitle"
|
||||
learnMoreLink="https://docs.logto.io/docs/recipes/rbac/manage-permissions-and-roles#manage-role-permissions"
|
||||
learnMoreLink={{
|
||||
href: 'https://docs.logto.io/docs/recipes/rbac/manage-permissions-and-roles#manage-role-permissions',
|
||||
targetBlank: 'noopener',
|
||||
}}
|
||||
footer={
|
||||
isScopesPerResourceReachLimit && currentPlan ? (
|
||||
<QuotaGuardFooter>
|
||||
|
|
|
@ -71,11 +71,14 @@ function ApiResourceSettings() {
|
|||
? 'api_resource_details.management_api_settings_description'
|
||||
: 'api_resource_details.settings_description'
|
||||
}
|
||||
learnMoreLink={getDocumentationUrl(
|
||||
isLogtoManagementApiResource
|
||||
? '/docs/recipes/interact-with-management-api/'
|
||||
: '/docs/recipes/protect-your-api/'
|
||||
)}
|
||||
learnMoreLink={{
|
||||
href: getDocumentationUrl(
|
||||
isLogtoManagementApiResource
|
||||
? '/docs/recipes/interact-with-management-api/'
|
||||
: '/docs/recipes/protect-your-api/'
|
||||
),
|
||||
targetBlank: 'noopener',
|
||||
}}
|
||||
>
|
||||
<FormField isRequired title="api_resources.api_name">
|
||||
<TextInput
|
||||
|
@ -106,7 +109,7 @@ function ApiResourceSettings() {
|
|||
a: (
|
||||
<TextLink
|
||||
href="https://docs.logto.io/docs/references/resources/#default-api"
|
||||
target="_blank"
|
||||
targetBlank="noopener"
|
||||
/>
|
||||
),
|
||||
}}
|
||||
|
|
|
@ -26,7 +26,7 @@ function ManagementApiNotice() {
|
|||
>
|
||||
<Trans
|
||||
components={{
|
||||
a: <TextLink href={learnMoreLink} target="_blank" rel="noopener" />,
|
||||
a: <TextLink href={learnMoreLink} targetBlank="noopener" />,
|
||||
}}
|
||||
>
|
||||
{t('api_resource_details.management_api_notice')}
|
||||
|
|
|
@ -123,8 +123,8 @@ function CreateForm({ onClose }: Props) {
|
|||
components={{
|
||||
a: (
|
||||
<TextLink
|
||||
targetBlank
|
||||
href="https://datatracker.ietf.org/doc/html/rfc8707#section-2"
|
||||
target="_blank"
|
||||
onClick={closeTipHandler}
|
||||
/>
|
||||
),
|
||||
|
|
|
@ -49,7 +49,10 @@ function AdvancedSettings({ app: { type }, oidcConfig }: Props) {
|
|||
<FormCard
|
||||
title="application_details.advanced_settings"
|
||||
description="application_details.advanced_settings_description"
|
||||
learnMoreLink="https://openid.net/specs/openid-connect-core-1_0.html#TokenEndpoint"
|
||||
learnMoreLink={{
|
||||
href: 'https://openid.net/specs/openid-connect-core-1_0.html#TokenEndpoint',
|
||||
targetBlank: true,
|
||||
}}
|
||||
>
|
||||
{tenantEndpoint && (
|
||||
<FormField title="application_details.config_endpoint">
|
||||
|
@ -67,8 +70,8 @@ function AdvancedSettings({ app: { type }, oidcConfig }: Props) {
|
|||
components={{
|
||||
a: (
|
||||
<TextLink
|
||||
targetBlank
|
||||
href="https://openid.net/specs/openid-connect-core-1_0.html#Authentication"
|
||||
target="_blank"
|
||||
onClick={closeTipHandler}
|
||||
/>
|
||||
),
|
||||
|
@ -127,7 +130,7 @@ function AdvancedSettings({ app: { type }, oidcConfig }: Props) {
|
|||
a: (
|
||||
<TextLink
|
||||
href="https://docs.logto.io/docs/references/applications/#rotate-refresh-token"
|
||||
target="_blank"
|
||||
targetBlank="noopener"
|
||||
/>
|
||||
),
|
||||
}}
|
||||
|
|
|
@ -52,7 +52,10 @@ function Settings({ data }: Props) {
|
|||
<FormCard
|
||||
title="application_details.settings"
|
||||
description="application_details.settings_description"
|
||||
learnMoreLink={getDocumentationUrl('/docs/references/applications')}
|
||||
learnMoreLink={{
|
||||
href: getDocumentationUrl('/docs/references/applications'),
|
||||
targetBlank: 'noopener',
|
||||
}}
|
||||
>
|
||||
<FormField isRequired title="application_details.application_name">
|
||||
<TextInput
|
||||
|
@ -108,8 +111,8 @@ function Settings({ data }: Props) {
|
|||
components={{
|
||||
a: (
|
||||
<TextLink
|
||||
targetBlank
|
||||
href="https://openid.net/specs/openid-connect-core-1_0.html#AuthRequest"
|
||||
target="_blank"
|
||||
onClick={closeTipHandler}
|
||||
/>
|
||||
),
|
||||
|
@ -163,8 +166,8 @@ function Settings({ data }: Props) {
|
|||
components={{
|
||||
a: (
|
||||
<TextLink
|
||||
targetBlank
|
||||
href="https://developer.mozilla.org/en-US/docs/Web/HTTP/CORS"
|
||||
target="_blank"
|
||||
onClick={closeTipHandler}
|
||||
/>
|
||||
),
|
||||
|
|
|
@ -53,10 +53,13 @@ function EmailServiceConnectorForm({ extraInfo }: Props) {
|
|||
<FormCard
|
||||
title="connector_details.logto_email.email_template_title"
|
||||
description="connector_details.logto_email.template_description"
|
||||
learnMoreLink={getDocumentationUrl(
|
||||
'/docs/recipes/configure-connectors/email-connector/configure-logto-email-service/#unified-email-templates'
|
||||
)}
|
||||
learnMoreLinkText="connector_details.logto_email.template_description_link_text"
|
||||
learnMoreLink={{
|
||||
href: getDocumentationUrl(
|
||||
'/docs/recipes/configure-connectors/email-connector/configure-logto-email-service/#unified-email-templates'
|
||||
),
|
||||
targetBlank: 'noopener',
|
||||
linkText: 'connector_details.logto_email.template_description_link_text',
|
||||
}}
|
||||
>
|
||||
<FormField title="connector_details.logto_email.from_email_field">
|
||||
<TextInput
|
||||
|
|
|
@ -134,7 +134,10 @@ function ConnectorContent({ isDeleted, connectorData, onConnectorUpdated }: Prop
|
|||
<FormCard
|
||||
title="connector_details.settings"
|
||||
description="connector_details.settings_description"
|
||||
learnMoreLink={getDocumentationUrl('/docs/references/connectors')}
|
||||
learnMoreLink={{
|
||||
href: getDocumentationUrl('/docs/references/connectors'),
|
||||
targetBlank: 'noopener',
|
||||
}}
|
||||
>
|
||||
<BasicForm isStandard={isStandardConnector} isDarkDefaultVisible={Boolean(logoDark)} />
|
||||
</FormCard>
|
||||
|
@ -148,7 +151,10 @@ function ConnectorContent({ isDeleted, connectorData, onConnectorUpdated }: Prop
|
|||
!isSocialConnector && 'connector_details.settings_description'
|
||||
)}
|
||||
learnMoreLink={conditional(
|
||||
!isSocialConnector && getDocumentationUrl('/docs/references/connectors')
|
||||
!isSocialConnector && {
|
||||
href: getDocumentationUrl('/docs/references/connectors'),
|
||||
targetBlank: 'noopener',
|
||||
}
|
||||
)}
|
||||
>
|
||||
<ConfigForm formItems={formItems} connectorId={id} connectorType={connectorType} />
|
||||
|
|
|
@ -42,7 +42,7 @@ function EmailUsage({ usage, isCompact }: Props) {
|
|||
href={getDocumentationUrl(
|
||||
'docs/recipes/configure-connectors/email-connector/configure-logto-email-service'
|
||||
)}
|
||||
target="_blank"
|
||||
targetBlank="noopener"
|
||||
onClick={closeTipHandler}
|
||||
/>
|
||||
),
|
||||
|
|
|
@ -183,9 +183,12 @@ function Connectors() {
|
|||
imageDark={<SocialConnectorEmptyDark />}
|
||||
title="connectors.placeholder_title"
|
||||
description="connectors.placeholder_description"
|
||||
learnMoreLink={getDocumentationUrl(
|
||||
'/docs/recipes/configure-connectors/configure-social-connector'
|
||||
)}
|
||||
learnMoreLink={{
|
||||
href: getDocumentationUrl(
|
||||
'/docs/recipes/configure-connectors/configure-social-connector'
|
||||
),
|
||||
targetBlank: 'noopener',
|
||||
}}
|
||||
action={
|
||||
<Button
|
||||
title="connectors.create"
|
||||
|
|
|
@ -35,7 +35,7 @@ function SsoGuide({ ssoConnector, className }: Props) {
|
|||
<MDXProvider
|
||||
components={{
|
||||
a: ({ children, ...props }) => (
|
||||
<TextLink {...props} target="_blank" rel="noopener noreferrer">
|
||||
<TextLink {...props} targetBlank>
|
||||
{children}
|
||||
</TextLink>
|
||||
),
|
||||
|
|
|
@ -25,14 +25,7 @@ function DevelopmentTenantNotification() {
|
|||
<div className={styles.title}>
|
||||
<Trans
|
||||
components={{
|
||||
a: (
|
||||
<TextLink
|
||||
href={pricingLink}
|
||||
target="_blank"
|
||||
rel="noopener"
|
||||
className={styles.link}
|
||||
/>
|
||||
),
|
||||
a: <TextLink href={pricingLink} targetBlank="noopener" className={styles.link} />,
|
||||
}}
|
||||
>
|
||||
{t('tenants.dev_tenant_notification.title')}
|
||||
|
@ -43,11 +36,11 @@ function DevelopmentTenantNotification() {
|
|||
</div>
|
||||
</div>
|
||||
<LinkButton
|
||||
targetBlank
|
||||
title="general.learn_more"
|
||||
type="outline"
|
||||
size="large"
|
||||
href={getDocumentationUrl(envTagsFeatureLink)}
|
||||
targetBlank="noopener"
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
|
|
|
@ -150,7 +150,7 @@ function GetStarted() {
|
|||
<LinkButton
|
||||
title="get_started.customize.try_now"
|
||||
href={new URL('/demo-app', tenantEndpoint).href}
|
||||
target="_blank"
|
||||
targetBlank="noopener"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
|
|
|
@ -93,7 +93,10 @@ function MfaForm({ data, onMfaUpdated }: Props) {
|
|||
<FormCard
|
||||
title="mfa.factors"
|
||||
description="mfa.multi_factors_description"
|
||||
learnMoreLink={getDocumentationUrl('/docs/recipes/multi-factor-auth/config-mfa')}
|
||||
learnMoreLink={{
|
||||
href: getDocumentationUrl('/docs/recipes/multi-factor-auth/configure-mfa'),
|
||||
targetBlank: 'noopener',
|
||||
}}
|
||||
>
|
||||
<FormField title="mfa.multi_factors" headlineSpacing="large">
|
||||
<div className={styles.factorField}>
|
||||
|
|
|
@ -83,11 +83,7 @@ function OrganizationInfo() {
|
|||
{/* TODO: @charles Documentation links will be updated later */}
|
||||
<ul>
|
||||
<li>
|
||||
<TextLink
|
||||
target="blank"
|
||||
rel="noopener"
|
||||
href="https://docs.logto.io/docs/tutorials/"
|
||||
>
|
||||
<TextLink href="https://docs.logto.io/docs/tutorials/" targetBlank="noopener">
|
||||
{t('guide.add_members_action')}
|
||||
</TextLink>
|
||||
</li>
|
||||
|
|
|
@ -76,7 +76,10 @@ function AssignPermissionsModal({ roleId, roleType, totalRoleScopeCount, onClose
|
|||
<ModalLayout
|
||||
title="role_details.permission.assign_title"
|
||||
subtitle="role_details.permission.assign_subtitle"
|
||||
learnMoreLink="https://docs.logto.io/docs/recipes/rbac/manage-permissions-and-roles#manage-role-permissions"
|
||||
learnMoreLink={{
|
||||
href: 'https://docs.logto.io/docs/recipes/rbac/manage-permissions-and-roles#manage-role-permissions',
|
||||
targetBlank: 'noopener',
|
||||
}}
|
||||
size="large"
|
||||
footer={
|
||||
shouldBlockScopeAssignment && currentPlan ? (
|
||||
|
|
|
@ -52,7 +52,10 @@ function RoleSettings() {
|
|||
<FormCard
|
||||
title="role_details.settings"
|
||||
description="role_details.settings_description"
|
||||
learnMoreLink="https://docs.logto.io/docs/recipes/rbac/manage-permissions-and-roles#manage-roles"
|
||||
learnMoreLink={{
|
||||
href: 'https://docs.logto.io/docs/recipes/rbac/manage-permissions-and-roles#manage-roles',
|
||||
targetBlank: 'noopener',
|
||||
}}
|
||||
>
|
||||
<FormField isRequired title="role_details.field_name">
|
||||
<TextInput {...register('name', { required: true })} error={Boolean(errors.name)} />
|
||||
|
|
|
@ -104,7 +104,10 @@ function CreateRoleForm({ totalRoleCount, onClose }: Props) {
|
|||
<ModalLayout
|
||||
title="roles.create_role_title"
|
||||
subtitle="roles.create_role_description"
|
||||
learnMoreLink="https://docs.logto.io/docs/recipes/rbac/manage-permissions-and-roles#manage-roles"
|
||||
learnMoreLink={{
|
||||
href: 'https://docs.logto.io/docs/recipes/rbac/manage-permissions-and-roles#manage-roles',
|
||||
targetBlank: 'noopener',
|
||||
}}
|
||||
size="large"
|
||||
footer={(() => {
|
||||
if (
|
||||
|
|
|
@ -74,8 +74,10 @@ function Roles() {
|
|||
title={{
|
||||
title: 'roles.title',
|
||||
subtitle: 'roles.subtitle',
|
||||
learnMoreLink:
|
||||
'https://docs.logto.io/docs/recipes/rbac/manage-permissions-and-roles#manage-roles',
|
||||
learnMoreLink: {
|
||||
href: 'https://docs.logto.io/docs/recipes/rbac/manage-permissions-and-roles#manage-roles',
|
||||
targetBlank: 'noopener',
|
||||
},
|
||||
}}
|
||||
pageMeta={{ titleKey: 'roles.page_title' }}
|
||||
createButton={{
|
||||
|
@ -172,9 +174,12 @@ function Roles() {
|
|||
imageDark={<RolesEmptyDark />}
|
||||
title="roles.placeholder_title"
|
||||
description="roles.placeholder_description"
|
||||
learnMoreLink={getDocumentationUrl(
|
||||
'/docs/recipes/rbac/manage-permissions-and-roles#manage-roles'
|
||||
)}
|
||||
learnMoreLink={{
|
||||
href: getDocumentationUrl(
|
||||
'/docs/recipes/rbac/manage-permissions-and-roles#manage-roles'
|
||||
),
|
||||
targetBlank: 'noopener',
|
||||
}}
|
||||
action={
|
||||
<Button
|
||||
title="roles.create"
|
||||
|
|
|
@ -30,8 +30,8 @@ function CustomCssForm() {
|
|||
components={{
|
||||
a: (
|
||||
<TextLink
|
||||
targetBlank="noopener"
|
||||
href={getDocumentationUrl('/docs/recipes/customize-sie/custom-css')}
|
||||
target="_blank"
|
||||
onClick={closeTipHandler}
|
||||
/>
|
||||
),
|
||||
|
|
|
@ -81,9 +81,8 @@ function PasswordPolicy({ isActive }: Props) {
|
|||
components={{
|
||||
a: (
|
||||
<TextLink
|
||||
targetBlank
|
||||
href="https://pages.nist.gov/800-63-3/sp800-63b.html#sec5"
|
||||
target="_blank"
|
||||
rel="noopener"
|
||||
/>
|
||||
),
|
||||
}}
|
||||
|
|
|
@ -22,13 +22,7 @@ function TenantEnvironment({ tag }: Props) {
|
|||
<div className={styles.description}>
|
||||
<Trans
|
||||
components={{
|
||||
a: (
|
||||
<TextLink
|
||||
href={getDocumentationUrl(envTagsFeatureLink)}
|
||||
target="_blank"
|
||||
rel="noopener"
|
||||
/>
|
||||
),
|
||||
a: <TextLink targetBlank="noopener" href={getDocumentationUrl(envTagsFeatureLink)} />,
|
||||
}}
|
||||
>
|
||||
{t(
|
||||
|
|
|
@ -16,7 +16,7 @@ function TenantRegion() {
|
|||
<div className={styles.regionTip}>
|
||||
<Trans
|
||||
components={{
|
||||
a: <TextLink href={trustAndSecurityLink} target="_blank" rel="noopener" />,
|
||||
a: <TextLink targetBlank="noopener" href={trustAndSecurityLink} />,
|
||||
}}
|
||||
>
|
||||
{t('tenants.settings.tenant_region_tip', { region: 'EU' })}
|
||||
|
|
|
@ -34,6 +34,7 @@ function CustomDomain({ customDomain, onDeleteCustomDomain }: Props) {
|
|||
components={{
|
||||
a: (
|
||||
<TextLink
|
||||
targetBlank="noopener"
|
||||
to={getDocumentationUrl('docs/recipes/custom-domain/use-custom-domain')}
|
||||
/>
|
||||
),
|
||||
|
|
|
@ -27,7 +27,10 @@ function TenantDomainSettings() {
|
|||
<FormCard
|
||||
title="domain.custom.custom_domain"
|
||||
description="domain.custom.custom_domain_description"
|
||||
learnMoreLink={getDocumentationUrl('docs/recipes/custom-domain')}
|
||||
learnMoreLink={{
|
||||
href: getDocumentationUrl('docs/recipes/custom-domain'),
|
||||
targetBlank: 'noopener',
|
||||
}}
|
||||
>
|
||||
<FormField title="domain.custom.custom_domain_field">
|
||||
{customDomain ? (
|
||||
|
|
|
@ -102,7 +102,10 @@ function UserSettings() {
|
|||
<FormCard
|
||||
title="user_details.authentication"
|
||||
description="user_details.authentication_description"
|
||||
learnMoreLink={getDocumentationUrl('/docs/references/users')}
|
||||
learnMoreLink={{
|
||||
href: getDocumentationUrl('/docs/references/users'),
|
||||
targetBlank: 'noopener',
|
||||
}}
|
||||
>
|
||||
<FormField title="user_details.field_email">
|
||||
<TextInput
|
||||
|
|
|
@ -58,7 +58,10 @@ function WebhookSettings() {
|
|||
<FormCard
|
||||
title="webhook_details.settings.settings"
|
||||
description="webhook_details.settings.settings_description"
|
||||
learnMoreLink={getDocumentationUrl('/docs/recipes/webhooks')}
|
||||
learnMoreLink={{
|
||||
href: getDocumentationUrl('/docs/recipes/webhooks'),
|
||||
targetBlank: 'noopener',
|
||||
}}
|
||||
>
|
||||
<BasicWebhookForm />
|
||||
<SigningKeyField
|
||||
|
|
Loading…
Add table
Reference in a new issue