Update in-product doc links to point to docs.overleaf.com (#35184)

* update doc links to point to docs.overleaf.com instead of the old learn wiki

* fix broken link to LaTeX positioning wiki article in figure modal help

* point figure positioning help link to the docs.overleaf.com page

* point figure/table label help links to specific anchors on the captioning-and-referencing-figures doc page

* point figure modal error-help link to the graphicx-loading doc page

* normalize target/rel on docs.overleaf.com links opened in a new tab

* open figure modal and SSO help links in a new tab now that they point off-site

* add eslint rule enforcing rel=noopener noreferrer on docs.overleaf.com links

* extend require-rel-for-docs-links to also check OLButton

* add lint script catching missing rel on docs.overleaf.com links in pug templates

* fix require-rel-for-docs-links autofix crash on valueless rel attribute

Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com>

* add regression test for require-rel-for-docs-links valueless-rel autofix

* revert `target=_blank` on figure modal and SSO help links

---------

Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com>
GitOrigin-RevId: b6111341e1080fd7e4b42f6a1a18c27ea0fd7d8c
This commit is contained in:
Rebeka Dekany
2026-09-17 11:18:47 +00:00
committed by Copybot
co-authored by Copilot Autofix powered by AI
parent 5698ef16b3
commit f30c23cacc
37 changed files with 260 additions and 62 deletions
+1
View File
@@ -13,6 +13,7 @@ module.exports = {
'require-script-runner': require('./require-script-runner'),
'require-vi-doMock-valid-path': require('./require-vi-doMock-valid-path'),
'require-loading-label': require('./require-loading-label'),
'require-rel-for-docs-links': require('./require-rel-for-docs-links'),
'require-cio-snake-case-properties': require('./require-cio-snake-case-properties'),
'no-throw-in-callback': require('./no-throw-in-callback'),
'no-consecutive-spaces-in-locales': require('./no-consecutive-spaces-in-locales'),
@@ -0,0 +1,87 @@
const DOCS_HOST = 'docs.overleaf.com'
const REQUIRED_REL_TOKENS = ['noopener', 'noreferrer']
// Elements that forward href/target/rel straight onto a rendered <a>.
const LINK_ELEMENT_NAMES = ['a', 'OLButton']
function getStringLiteralValue(node) {
if (!node) return undefined
if (node.type === 'Literal' && typeof node.value === 'string') {
return node.value
}
if (
node.type === 'JSXExpressionContainer' &&
node.expression.type === 'Literal' &&
typeof node.expression.value === 'string'
) {
return node.expression.value
}
return undefined
}
module.exports = {
meta: {
type: 'problem',
fixable: 'code',
docs: {
description:
'Require rel="noopener noreferrer" on links to docs.overleaf.com that open in a new tab, to prevent reverse-tabnabbing',
},
schema: [],
},
create(context) {
const selector = `JSXOpeningElement[name.name=/^(${LINK_ELEMENT_NAMES.join('|')})$/]`
return {
[selector](node) {
const attributes = new Map(
node.attributes
.filter(attr => attr.type === 'JSXAttribute')
.map(attr => [attr.name.name, attr])
)
const hrefAttr = attributes.get('href')
const href = getStringLiteralValue(hrefAttr?.value)
if (!href || !href.includes(DOCS_HOST)) {
return
}
const targetAttr = attributes.get('target')
const target = getStringLiteralValue(targetAttr?.value)
if (!target || target.toLowerCase() !== '_blank') {
return
}
const relAttr = attributes.get('rel')
const rel = getStringLiteralValue(relAttr?.value) || ''
const relTokens = rel.toLowerCase().split(/\s+/).filter(Boolean)
const missingTokens = REQUIRED_REL_TOKENS.filter(
token => !relTokens.includes(token)
)
if (missingTokens.length === 0) {
return
}
context.report({
node: relAttr || node,
message:
'Links to docs.overleaf.com with target="_blank" must have rel="noopener noreferrer" to prevent reverse-tabnabbing.',
fix(fixer) {
const newRel = Array.from(
new Set([...relTokens, ...REQUIRED_REL_TOKENS])
).join(' ')
if (relAttr) {
if (!relAttr.value) {
return fixer.replaceText(relAttr, `rel="${newRel}"`)
}
return fixer.replaceText(relAttr.value, `"${newRel}"`)
}
return fixer.insertTextAfter(targetAttr, ` rel="${newRel}"`)
},
})
},
}
},
}
+82
View File
@@ -8,6 +8,7 @@ const shouldUnescapeTrans = require('./should-unescape-trans')
const noGeneratedEditorThemes = require('./no-generated-editor-themes')
const viDoMockValidPath = require('./require-vi-doMock-valid-path')
const requireCioSnakeCaseProperties = require('./require-cio-snake-case-properties')
const requireRelForDocsLinks = require('./require-rel-for-docs-links')
const noConsecutiveSpacesInLocales = require('./no-consecutive-spaces-in-locales')
const noStraightApostrophesInLocales = require('./no-straight-apostrophes-in-locales')
const frenchTypographyInLocales = require('./french-typography-in-locales')
@@ -340,6 +341,87 @@ ruleTester.run('no-throw-in-callback', noThrowInCallback, {
],
})
ruleTester.run('require-rel-for-docs-links', requireRelForDocsLinks, {
valid: [
{
code: `<a href="https://docs.overleaf.com/foo" target="_blank" rel="noopener noreferrer">x</a>`,
},
{
code: `<a href="https://docs.overleaf.com/foo" target="_blank" rel="noreferrer noopener">x</a>`,
},
{ code: `<a href="/learn/foo" target="_blank">x</a>` },
{ code: `<a href="https://docs.overleaf.com/foo">x</a>` },
{ code: `<a href="https://docs.overleaf.com/foo" target="_self">x</a>` },
{
code: `<OLButton href="https://docs.overleaf.com/foo" target="_blank" rel="noopener noreferrer">x</OLButton>`,
},
{ code: `<OLButton href="https://docs.overleaf.com/foo">x</OLButton>` },
],
invalid: [
{
code: `<a href="https://docs.overleaf.com/foo" target="_blank">x</a>`,
errors: [
{
message:
'Links to docs.overleaf.com with target="_blank" must have rel="noopener noreferrer" to prevent reverse-tabnabbing.',
},
],
output: `<a href="https://docs.overleaf.com/foo" target="_blank" rel="noopener noreferrer">x</a>`,
},
{
code: `<a href="https://docs.overleaf.com/foo" target="_BLANK">x</a>`,
errors: [
{
message:
'Links to docs.overleaf.com with target="_blank" must have rel="noopener noreferrer" to prevent reverse-tabnabbing.',
},
],
output: `<a href="https://docs.overleaf.com/foo" target="_BLANK" rel="noopener noreferrer">x</a>`,
},
{
code: `<a href="https://docs.overleaf.com/foo" target="_blank" rel="noopener">x</a>`,
errors: [
{
message:
'Links to docs.overleaf.com with target="_blank" must have rel="noopener noreferrer" to prevent reverse-tabnabbing.',
},
],
output: `<a href="https://docs.overleaf.com/foo" target="_blank" rel="noopener noreferrer">x</a>`,
},
{
code: `<a href="https://docs.overleaf.com/foo" target="_blank" rel="noreferrer">x</a>`,
errors: [
{
message:
'Links to docs.overleaf.com with target="_blank" must have rel="noopener noreferrer" to prevent reverse-tabnabbing.',
},
],
output: `<a href="https://docs.overleaf.com/foo" target="_blank" rel="noreferrer noopener">x</a>`,
},
{
code: `<OLButton href="https://docs.overleaf.com/foo" target="_blank">x</OLButton>`,
errors: [
{
message:
'Links to docs.overleaf.com with target="_blank" must have rel="noopener noreferrer" to prevent reverse-tabnabbing.',
},
],
output: `<OLButton href="https://docs.overleaf.com/foo" target="_blank" rel="noopener noreferrer">x</OLButton>`,
},
{
// Boolean attribute (no value) - relAttr.value is null, must not crash the fixer.
code: `<a href="https://docs.overleaf.com/foo" target="_blank" rel />`,
errors: [
{
message:
'Links to docs.overleaf.com with target="_blank" must have rel="noopener noreferrer" to prevent reverse-tabnabbing.',
},
],
output: `<a href="https://docs.overleaf.com/foo" target="_blank" rel="noopener noreferrer" />`,
},
],
})
jsonRuleTester.run(
'no-consecutive-spaces-in-locales',
noConsecutiveSpacesInLocales,
@@ -29,7 +29,11 @@ mixin reconfirmAffiliationNotification-marketing(userEmail, location)
| !{translate("please_reconfirm_institutional_email_2")}
| &nbsp;
a(href='/learn/how-to/Institutional_Email_Reconfirmation' target='_blank') #{translate("learn_more_about_email_reconfirmation")}
a(
href='https://docs.overleaf.com/accounts-and-security/email-address-and-login-options/logging-in-with-institutional-sso/institutional-email-reconfirmation'
target='_blank'
rel='noopener noreferrer'
) #{translate("learn_more_about_email_reconfirmation")}
mixin reconfirmedAffiliationNotification-marketing(userEmail)
| !{translate("your_affiliation_is_confirmed", {institutionName: userEmail.affiliation.institution.name}, ['strong'])}
@@ -34,4 +34,4 @@ block content
p.small.mb-2
| #{translate("keep_your_email_updated")}
p.small
| !{translate("learn_more_about_emails", {}, [{name: 'a', attrs: {href: '/learn/how-to/Keeping_your_account_secure', 'event-tracking': 'primary-email-check-learn-more', 'event-tracking-mb': 'true', 'event-tracking-trigger': 'click' }}])}
| !{translate("learn_more_about_emails", {}, [{name: 'a', attrs: {href: 'https://docs.overleaf.com/accounts-and-security/keeping-your-account-secure', 'event-tracking': 'primary-email-check-learn-more', 'event-tracking-mb': 'true', 'event-tracking-trigger': 'click' }}])}
@@ -93,7 +93,11 @@ export default function OffboardManagedUserModal({
<p>
<span>{t('this_action_cannot_be_reversed')}</span>
&nbsp;
<a href="/learn/how-to/User_Management_in_Overleaf" target="_blank">
<a
href="https://docs.overleaf.com/groups/user-management/managed-user-accounts"
target="_blank"
rel="noopener noreferrer"
>
{t('learn_more_about_managed_users')}
</a>
</p>
@@ -50,7 +50,7 @@ function OutOfSyncModal({ editorContent, show, onHide }: OutOfSyncModalProps) {
<a
target="_blank"
rel="noopener noreferrer"
href="/learn/Kb/Editor_out_of_sync_problems"
href="https://docs.overleaf.com/troubleshooting-and-support/resolving-pdf-rendering-and-project-loading-problems"
/>,
]}
/>
@@ -31,7 +31,7 @@ const RollingCompileImageChangedAlert = () => {
href="https://docs.overleaf.com/troubleshooting-and-support/tex-live#How_do_I_change_a_project%E2%80%99s_TeX_Live_version"
target="_blank"
key="getting-started-link"
rel="noopener"
rel="noopener noreferrer"
/>,
]}
/>
@@ -14,8 +14,9 @@ function PdfPreviewErrorBoundaryFallback({
components={[
// eslint-disable-next-line jsx-a11y/anchor-has-content
<a
href="/learn/how-to/Resolving_access%2C_loading%2C_and_display_problems"
href="https://docs.overleaf.com/troubleshooting-and-support/resolving-pdf-rendering-and-project-loading-problems"
target="_blank"
rel="noopener noreferrer"
key="troubleshooting-link"
/>,
]}
@@ -46,8 +46,9 @@ function PdfPreviewError({
components={[
// eslint-disable-next-line jsx-a11y/anchor-has-content
<a
href="/learn/how-to/Resolving_access%2C_loading%2C_and_display_problems"
href="https://docs.overleaf.com/troubleshooting-and-support/resolving-pdf-rendering-and-project-loading-problems"
target="_blank"
rel="noopener noreferrer"
key="troubleshooting-link"
/>,
]}
@@ -70,8 +71,9 @@ function PdfPreviewError({
components={[
// eslint-disable-next-line jsx-a11y/anchor-has-content
<a
href="/learn/how-to/Resolving_access%2C_loading%2C_and_display_problems"
href="https://docs.overleaf.com/troubleshooting-and-support/resolving-pdf-rendering-and-project-loading-problems"
target="_blank"
rel="noopener noreferrer"
key="troubleshooting-link"
/>,
]}
@@ -93,8 +95,9 @@ function PdfPreviewError({
<code key="domain" />,
/* eslint-disable-next-line jsx-a11y/anchor-has-content */
<a
href="/learn/how-to/Resolving_access%2C_loading%2C_and_display_problems"
href="https://docs.overleaf.com/troubleshooting-and-support/resolving-pdf-rendering-and-project-loading-problems"
target="_blank"
rel="noopener noreferrer"
key="troubleshooting-link"
/>,
]}
@@ -224,8 +227,9 @@ function PdfPreviewError({
<strong key="strong-" />,
// eslint-disable-next-line jsx-a11y/anchor-has-content
<a
href="/learn/how-to/Resolving_access%2C_loading%2C_and_display_problems"
href="https://docs.overleaf.com/troubleshooting-and-support/resolving-pdf-rendering-and-project-loading-problems"
target="_blank"
rel="noopener noreferrer"
key="troubleshooting-link"
/>,
// eslint-disable-next-line jsx-a11y/anchor-has-content
@@ -299,7 +303,7 @@ function TimedOutLogEntry() {
components={[
// eslint-disable-next-line jsx-a11y/anchor-has-content, react/jsx-key
<a
href="https://www.overleaf.com/learn/how-to/Optimising_very_large_image_files"
href="https://docs.overleaf.com/troubleshooting-and-support/fixing-and-preventing-compile-timeouts/optimising-very-large-image-files"
onClick={() => {
sendMB('paywall-info-click', {
'paywall-type': 'compile-timeout',
@@ -317,7 +321,7 @@ function TimedOutLogEntry() {
components={[
// eslint-disable-next-line jsx-a11y/anchor-has-content, react/jsx-key
<a
href="https://www.overleaf.com/learn/how-to/Why_do_I_keep_getting_the_compile_timeout_error_message%3F#Fatal_compile_errors_blocking_the_compilation"
href="https://docs.overleaf.com/troubleshooting-and-support/fixing-and-preventing-compile-timeouts"
onClick={() => {
sendMB('paywall-info-click', {
'paywall-type': 'compile-timeout',
@@ -352,7 +356,7 @@ function TimedOutLogEntry() {
components={[
// eslint-disable-next-line jsx-a11y/anchor-has-content, react/jsx-key
<a
href="https://www.overleaf.com/learn/how-to/Why_do_I_keep_getting_the_compile_timeout_error_message%3F"
href="https://docs.overleaf.com/troubleshooting-and-support/fixing-and-preventing-compile-timeouts"
onClick={() => {
sendMB('paywall-info-click', {
'paywall-type': 'compile-timeout',
@@ -10,8 +10,9 @@ export const SynctexFileErrorToast = () => {
<span>{t('synctex_failed')}</span>
<OLButton
href="/learn/how-to/SyncTeX_Errors"
href="https://docs.overleaf.com/navigating-in-the-editor/working-with-the-pdf-viewer/moving-between-the-editor-and-pdf#an-error-couldnt-find-the-corresponding-source-file-was-shown"
target="_blank"
rel="noopener noreferrer"
variant="secondary"
size="sm"
>
@@ -71,7 +71,7 @@ function PreviewLogsPaneMaxEntriesContent({
// eslint-disable-next-line jsx-a11y/anchor-has-content
<a
key="learn-more"
href="https://www.overleaf.com/learn/latex/Questions/Tips_and_Tricks_for_Troubleshooting_LaTeX"
href="https://docs.overleaf.com/troubleshooting-and-support/fixing-latex-errors"
/>,
]}
/>{' '}
@@ -26,7 +26,8 @@ function CurrentPlanWidget() {
isIndividualPlan &&
getUserSubscriptionState(usersBestSubscription) === 'paused'
const featuresPageURL = '/learn/how-to/Overleaf_premium_features'
const featuresPageURL =
'https://docs.overleaf.com/getting-started/free-and-premium-plans/premium-features'
const subscriptionPageUrl = '/user/subscription'
let currentPlan
@@ -43,9 +43,9 @@ function ArchiveProjectModal({
<p>
{t('archiving_projects_wont_affect_collaborators')}{' '}
<a
href="https://www.overleaf.com/learn/how-to/How_do_I_archive_and_unarchive_projects%3F"
href="https://docs.overleaf.com/managing-projects-and-files/archiving-deleting-and-leaving-projects"
target="_blank"
rel="noreferrer"
rel="noopener noreferrer"
>
{t('find_out_more_nt')}
</a>
@@ -43,9 +43,9 @@ function TrashProjectModal({
<p>
{t('trashing_projects_wont_affect_collaborators')}{' '}
<a
href="https://www.overleaf.com/learn/how-to/How_do_I_remove_or_delete_a_project%3F"
href="https://docs.overleaf.com/managing-projects-and-files/archiving-deleting-and-leaving-projects/deleting-a-project"
target="_blank"
rel="noreferrer"
rel="noopener noreferrer"
>
{t('find_out_more_nt')}
</a>
@@ -46,8 +46,9 @@ function ReconfirmAffiliation({
/>
&nbsp;
<a
href="/learn/how-to/Institutional_Email_Reconfirmation"
href="https://docs.overleaf.com/accounts-and-security/email-address-and-login-options/logging-in-with-institutional-sso/institutional-email-reconfirmation"
target="_blank"
rel="noopener noreferrer"
>
{t('learn_more_about_email_reconfirmation')}
</a>
@@ -152,7 +152,7 @@ function CommonNotification({ notification }: CommonNotificationProps) {
<a
href={
notification.messageOpts.portalPath ||
'https://www.overleaf.com/learn/how-to/Institutional_Login'
'https://docs.overleaf.com/accounts-and-security/email-address-and-login-options/logging-in-with-institutional-sso'
}
>
{t('find_out_more_nt')}
@@ -234,8 +234,9 @@ function CommonNotification({ notification }: CommonNotificationProps) {
components={[<b />]} // eslint-disable-line react/jsx-key
/>{' '}
<a
href="/learn/how-to/Dropbox_Synchronization#Troubleshooting"
href="https://docs.overleaf.com/integrations-and-add-ons/dropbox"
target="_blank"
rel="noopener noreferrer"
>
{t('learn_more')}
</a>
@@ -265,8 +266,9 @@ function CommonNotification({ notification }: CommonNotificationProps) {
t('confirm_affiliation_to_relink_dropbox')
)}{' '}
<a
href="/learn/how-to/Institutional_Email_Reconfirmation"
href="https://docs.overleaf.com/accounts-and-security/email-address-and-login-options/logging-in-with-institutional-sso/institutional-email-reconfirmation"
target="_blank"
rel="noopener noreferrer"
>
{t('learn_more')}
</a>
@@ -55,8 +55,9 @@ function Institution() {
tOptions={{ interpolation: { escapeValue: true } }}
/>{' '}
<a
href="/learn/how-to/Institutional_Login"
href="https://docs.overleaf.com/accounts-and-security/email-address-and-login-options/logging-in-with-institutional-sso"
target="_blank"
rel="noopener noreferrer"
>
{t('learn_more')}
</a>
@@ -122,8 +123,9 @@ function Institution() {
/>
&nbsp;
<a
href="/learn/how-to/Understanding_Managed_Overleaf_Accounts"
href="https://docs.overleaf.com/accounts-and-security/understanding-managed-accounts"
target="_blank"
rel="noopener noreferrer"
>
{t('understand_managed_user_accounts')}
</a>
@@ -175,8 +177,9 @@ function Institution() {
action={
<OLButton
variant="secondary"
href="/learn/how-to/Institutional_Login"
href="https://docs.overleaf.com/accounts-and-security/email-address-and-login-options/logging-in-with-institutional-sso"
target="_blank"
rel="noopener noreferrer"
>
{t('find_out_more')}
</OLButton>
@@ -51,8 +51,9 @@ function EmailsSectionContent() {
<strong />,
// eslint-disable-next-line jsx-a11y/anchor-has-content, react/jsx-key
<a
href="/learn/how-to/Managing_your_Overleaf_emails"
href="https://docs.overleaf.com/accounts-and-security/email-address-and-login-options"
target="_blank"
rel="noopener noreferrer"
/>,
]}
/>
@@ -46,7 +46,7 @@ function SsoLinkingInfo({ domainInfo, email }: SSOLinkingInfoProps) {
tOptions={{ interpolation: { escapeValue: true } }}
/>{' '}
<a
href="/learn/how-to/Institutional_Login"
href="https://docs.overleaf.com/accounts-and-security/email-address-and-login-options/logging-in-with-institutional-sso"
target="_blank"
rel="noopener noreferrer"
>
@@ -111,7 +111,7 @@ export function AddSecondaryEmailPrompt() {
i18nKey="learn_more_about_account"
components={[
// eslint-disable-next-line react/jsx-key, jsx-a11y/anchor-has-content
<a href="/learn/how-to/Keeping_your_account_secure" />,
<a href="https://docs.overleaf.com/accounts-and-security/keeping-your-account-secure" />,
]}
/>
</p>
@@ -34,8 +34,9 @@ function ReconfirmationInfoPromptText({
}
/>{' '}
<a
href="/learn/how-to/Institutional_Email_Reconfirmation"
href="https://docs.overleaf.com/accounts-and-security/email-address-and-login-options/logging-in-with-institutional-sso/institutional-email-reconfirmation"
target="_blank"
rel="noopener noreferrer"
>
{t('learn_more_about_email_reconfirmation')}
</a>
@@ -186,7 +186,11 @@ function SSOAffiliationInfo({ userEmailData }: SSOAffiliationInfoProps) {
[<strong />]
}
/>{' '}
<a href="/learn/how-to/Institutional_Login" target="_blank">
<a
href="https://docs.overleaf.com/accounts-and-security/email-address-and-login-options/logging-in-with-institutional-sso"
target="_blank"
rel="noopener noreferrer"
>
{t('find_out_more_about_institution_login')}
</a>
</p>
@@ -73,7 +73,7 @@ export function IntegrationLinkingWidget({
</div>
<p className="small">
{description}{' '}
<a href={helpPath} target="_blank" rel="noreferrer">
<a href={helpPath} target="_blank" rel="noopener noreferrer">
{t('learn_more_about', { appName: title })}
</a>
</p>
@@ -30,8 +30,9 @@ export default function ManagedAccountAlert() {
components={[
// eslint-disable-next-line jsx-a11y/anchor-has-content, react/jsx-key
<a
href="/learn/how-to/Understanding_Managed_Overleaf_Accounts"
href="https://docs.overleaf.com/accounts-and-security/understanding-managed-accounts"
target="_blank"
rel="noopener noreferrer"
/>,
]}
/>
@@ -42,7 +42,7 @@ function CanOnlyLogInThroughSSO() {
i18nKey="you_cant_add_or_change_password_due_to_sso"
components={[
// eslint-disable-next-line react/jsx-key, jsx-a11y/anchor-has-content
<a href="/learn/how-to/Logging_in_with_Group_single_sign-on" />,
<a href="https://docs.overleaf.com/accounts-and-security/email-address-and-login-options/logging-in-with-group-sso" />,
]}
/>
</p>
@@ -318,9 +318,9 @@ function LinkSharingInfo() {
description={t('learn_more_about_link_sharing')}
>
<a
href="/learn/how-to/What_is_Link_Sharing%3F"
href="https://docs.overleaf.com/collaborating/sharing-a-project"
target="_blank"
rel="noopener"
rel="noopener noreferrer"
>
<MaterialIcon type="help" className="align-middle" />
</a>
@@ -1,11 +1,11 @@
import { FC } from 'react'
import { Trans, useTranslation } from 'react-i18next'
const LearnWikiLink: FC<React.PropsWithChildren<{ article: string }>> = ({
article,
const LearnWikiLink: FC<React.PropsWithChildren<{ href: string }>> = ({
href,
children,
}) => {
return <a href={`/learn/latex/${article}`}>{children}</a>
return <a href={href}>{children}</a>
}
export const FigureModalHelp = () => {
@@ -24,7 +24,7 @@ export const FigureModalHelp = () => {
// eslint-disable-next-line react/jsx-key
<code />,
// eslint-disable-next-line react/jsx-key
<LearnWikiLink article="Inserting_Images#Labels_and_cross-references" />,
<LearnWikiLink href="https://docs.overleaf.com/writing-and-editing/inserting-images/captioning-and-referencing-figures#captioning-labeling-and-referencing" />,
]}
/>
</p>
@@ -35,7 +35,7 @@ export const FigureModalHelp = () => {
i18nKey="there_are_lots_of_options_to_edit_and_customize_your_figures"
components={[
// eslint-disable-next-line react/jsx-key
<LearnWikiLink article="Inserting_Images" />,
<LearnWikiLink href="https://docs.overleaf.com/writing-and-editing/inserting-images" />,
]}
/>
</p>
@@ -46,7 +46,7 @@ export const FigureModalHelp = () => {
i18nKey="latex_places_figures_according_to_a_special_algorithm"
components={[
// eslint-disable-next-line react/jsx-key
<LearnWikiLink article="Positioning_images_and_tables" />,
<LearnWikiLink href="https://docs.overleaf.com/writing-and-editing/inserting-images/positioning-figures" />,
]}
/>
</p>
@@ -59,7 +59,7 @@ export const FigureModalHelp = () => {
// eslint-disable-next-line react/jsx-key
<code />,
// eslint-disable-next-line react/jsx-key
<LearnWikiLink article="Inserting_Images" />,
<LearnWikiLink href="https://docs.overleaf.com/writing-and-editing/inserting-images/latex-code-for-including-images-in-your-document" />,
]}
/>
</p>
@@ -74,9 +74,9 @@ export const TableGeneratorHelpModal = () => {
<code />,
// eslint-disable-next-line react/jsx-key, jsx-a11y/anchor-has-content
<a
href="https://www.overleaf.com/learn/latex/Inserting_Images#Labels_and_cross-references"
href="https://docs.overleaf.com/writing-and-editing/inserting-images/captioning-and-referencing-figures#labels-and-cross-references"
target="_blank"
rel="noopener"
rel="noopener noreferrer"
/>,
]}
/>
@@ -19,7 +19,7 @@ function PremiumFeaturesLink({
i18nKey="get_most_subscription_by_checking_ai_writefull"
components={[
// eslint-disable-next-line react/jsx-key, jsx-a11y/anchor-has-content
<a href="/learn/latex/Error_Assist" />,
<a href="https://docs.overleaf.com/integrations-and-add-ons/ai-features/error-assist" />,
// eslint-disable-next-line react/jsx-key, jsx-a11y/anchor-has-content
<a
href="https://help.writefull.com/writefull-for-overleaf--user-guide"
@@ -39,9 +39,9 @@ function PremiumFeaturesLink({
i18nKey="get_most_subscription_by_checking_overleaf_ai_writefull"
components={[
// eslint-disable-next-line react/jsx-key, jsx-a11y/anchor-has-content
<a href="/learn/how-to/Overleaf_premium_features" />,
<a href="https://docs.overleaf.com/getting-started/free-and-premium-plans/premium-features" />,
// eslint-disable-next-line react/jsx-key, jsx-a11y/anchor-has-content
<a href="/learn/latex/Error_Assist" />,
<a href="https://docs.overleaf.com/integrations-and-add-ons/ai-features/error-assist" />,
// eslint-disable-next-line react/jsx-key, jsx-a11y/anchor-has-content
<a
href="https://help.writefull.com/writefull-for-overleaf--user-guide"
@@ -60,7 +60,7 @@ function PremiumFeaturesLink({
i18nKey="get_most_subscription_by_checking_overleaf"
components={[
// eslint-disable-next-line react/jsx-key, jsx-a11y/anchor-has-content
<a href="/learn/how-to/Overleaf_premium_features" />,
<a href="https://docs.overleaf.com/getting-started/free-and-premium-plans/premium-features" />,
]}
/>
</p>
@@ -21,7 +21,7 @@ export default function ManagedUserCannotJoin() {
tOptions={{ interpolation: { escapeValue: true } }}
components={[
/* eslint-disable-next-line jsx-a11y/anchor-has-content, react/jsx-key */
<a href="/learn/how-to/Understanding_Managed_Overleaf_Accounts" />,
<a href="https://docs.overleaf.com/accounts-and-security/understanding-managed-accounts" />,
]}
/>
</p>
@@ -39,7 +39,7 @@ export const AiConsentPromptMessageBody = ({
<a
href="https://docs.overleaf.com/integrations-and-add-ons/ai-features#data-privacy-and-responsible-use"
target="_blank"
rel="noreferrer"
rel="noopener noreferrer"
>
our docs
</a>
@@ -22,7 +22,7 @@ export const DeprecatedBrowser: FC = () => {
i18nKey="to_continue_using_upgrade_or_change_your_browser"
components={[
// eslint-disable-next-line jsx-a11y/anchor-has-content,react/jsx-key
<a href="/learn/how-to/Which_browsers_does_Overleaf_support%3F" />,
<a href="https://docs.overleaf.com/troubleshooting-and-support/supported-browsers" />,
]}
/>
}
@@ -135,7 +135,7 @@ describe('<DomainCapture />', function () {
}).should(
'have.attr',
'href',
'/learn/how-to/How_to_Transfer_Project_Ownership'
'https://docs.overleaf.com/collaborating/transferring-project-ownership'
)
})
@@ -337,7 +337,7 @@ describe('<CurrentPlanWidget />', function () {
const links = screen.getAllByRole('link')
expect(links[0].getAttribute('href')).to.equal(
'/learn/how-to/Overleaf_premium_features'
'https://docs.overleaf.com/getting-started/free-and-premium-plans/premium-features'
)
window.metaAttributesCache.delete('ol-usersBestSubscription')
@@ -239,7 +239,7 @@ describe('<UserNotifications />', function () {
const findOutMore = screen.getByRole('link', { name: /find out more/i })
expect(findOutMore.getAttribute('href')).to.equal(
'https://www.overleaf.com/learn/how-to/Institutional_Login'
'https://docs.overleaf.com/accounts-and-security/email-address-and-login-options/logging-in-with-institutional-sso'
)
const linkAccount = screen.getByRole('link', { name: /link account/i })
expect(linkAccount.getAttribute('href')).to.equal(
@@ -334,7 +334,7 @@ describe('<UserNotifications />', function () {
const learnMore = screen.getByRole('link', { name: /learn more/i })
expect(learnMore.getAttribute('href')).to.equal(
'/learn/how-to/Dropbox_Synchronization#Troubleshooting'
'https://docs.overleaf.com/integrations-and-add-ons/dropbox'
)
const closeBtn = screen.getByRole('button', { name: /close/i })
fireEvent.click(closeBtn)
@@ -368,7 +368,7 @@ describe('<UserNotifications />', function () {
const learnMore = screen.getByRole('link', { name: /learn more/i })
expect(learnMore.getAttribute('href')).to.equal(
'/learn/how-to/Institutional_Email_Reconfirmation'
'https://docs.overleaf.com/accounts-and-security/email-address-and-login-options/logging-in-with-institutional-sso/institutional-email-reconfirmation'
)
const closeBtn = screen.getByRole('button', { name: /close/i })
fireEvent.click(closeBtn)
@@ -489,7 +489,7 @@ describe('<UserNotifications />', function () {
const learnMore = screen.getByRole('link', { name: /learn more/i })
expect(learnMore.getAttribute('href')).to.equal(
'/learn/how-to/Institutional_Login'
'https://docs.overleaf.com/accounts-and-security/email-address-and-login-options/logging-in-with-institutional-sso'
)
const action = screen.getByRole('link', { name: /link account/i })
@@ -594,7 +594,7 @@ describe('<UserNotifications />', function () {
const action = screen.getByRole('link', { name: /find out more/i })
expect(action.getAttribute('href')).to.equal(
'/learn/how-to/Institutional_Login'
'https://docs.overleaf.com/accounts-and-security/email-address-and-login-options/logging-in-with-institutional-sso'
)
const closeBtn = screen.getByRole('button', { name: /close/i })
@@ -899,7 +899,7 @@ describe('<UserNotifications />', function () {
)
const learnMore = screen.getByRole('link', { name: /learn more/i })
expect(learnMore.getAttribute('href')).to.equal(
'/learn/how-to/Institutional_Email_Reconfirmation'
'https://docs.overleaf.com/accounts-and-security/email-address-and-login-options/logging-in-with-institutional-sso/institutional-email-reconfirmation'
)
const sendReconfirmationMock = fetchMock.post(
@@ -80,7 +80,7 @@ describe('successful subscription page', function () {
name: /Overleaf’s features/i,
})
expect(helpLink.getAttribute('href')).to.equal(
'/learn/how-to/Overleaf_premium_features'
'https://docs.overleaf.com/getting-started/free-and-premium-plans/premium-features'
)
const backToYourProjectsLink = screen.getByRole('link', {