diff --git a/libraries/eslint-plugin/index.js b/libraries/eslint-plugin/index.js index 2d41474c51..6119735c05 100644 --- a/libraries/eslint-plugin/index.js +++ b/libraries/eslint-plugin/index.js @@ -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'), diff --git a/libraries/eslint-plugin/require-rel-for-docs-links.js b/libraries/eslint-plugin/require-rel-for-docs-links.js new file mode 100644 index 0000000000..726a977370 --- /dev/null +++ b/libraries/eslint-plugin/require-rel-for-docs-links.js @@ -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 . +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}"`) + }, + }) + }, + } + }, +} diff --git a/libraries/eslint-plugin/rules.test.js b/libraries/eslint-plugin/rules.test.js index 8d9c41470e..02178362be 100644 --- a/libraries/eslint-plugin/rules.test.js +++ b/libraries/eslint-plugin/rules.test.js @@ -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: `x`, + }, + { + code: `x`, + }, + { code: `x` }, + { code: `x` }, + { code: `x` }, + { + code: `x`, + }, + { code: `x` }, + ], + invalid: [ + { + code: `x`, + errors: [ + { + message: + 'Links to docs.overleaf.com with target="_blank" must have rel="noopener noreferrer" to prevent reverse-tabnabbing.', + }, + ], + output: `x`, + }, + { + code: `x`, + errors: [ + { + message: + 'Links to docs.overleaf.com with target="_blank" must have rel="noopener noreferrer" to prevent reverse-tabnabbing.', + }, + ], + output: `x`, + }, + { + code: `x`, + errors: [ + { + message: + 'Links to docs.overleaf.com with target="_blank" must have rel="noopener noreferrer" to prevent reverse-tabnabbing.', + }, + ], + output: `x`, + }, + { + code: `x`, + errors: [ + { + message: + 'Links to docs.overleaf.com with target="_blank" must have rel="noopener noreferrer" to prevent reverse-tabnabbing.', + }, + ], + output: `x`, + }, + { + code: `x`, + errors: [ + { + message: + 'Links to docs.overleaf.com with target="_blank" must have rel="noopener noreferrer" to prevent reverse-tabnabbing.', + }, + ], + output: `x`, + }, + { + // Boolean attribute (no value) - relAttr.value is null, must not crash the fixer. + code: ``, + errors: [ + { + message: + 'Links to docs.overleaf.com with target="_blank" must have rel="noopener noreferrer" to prevent reverse-tabnabbing.', + }, + ], + output: ``, + }, + ], +}) + jsonRuleTester.run( 'no-consecutive-spaces-in-locales', noConsecutiveSpacesInLocales, diff --git a/services/web/app/views/_mixins/reconfirm_affiliation-marketing.pug b/services/web/app/views/_mixins/reconfirm_affiliation-marketing.pug index d668ce2b8a..1dfadd063c 100644 --- a/services/web/app/views/_mixins/reconfirm_affiliation-marketing.pug +++ b/services/web/app/views/_mixins/reconfirm_affiliation-marketing.pug @@ -29,7 +29,11 @@ mixin reconfirmAffiliationNotification-marketing(userEmail, location) | !{translate("please_reconfirm_institutional_email_2")} |   - 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'])} diff --git a/services/web/app/views/user/primaryEmailCheck.pug b/services/web/app/views/user/primaryEmailCheck.pug index efbf503e33..68e2772469 100644 --- a/services/web/app/views/user/primaryEmailCheck.pug +++ b/services/web/app/views/user/primaryEmailCheck.pug @@ -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' }}])} diff --git a/services/web/frontend/js/features/group-management/components/members-table/offboard-managed-user-modal.tsx b/services/web/frontend/js/features/group-management/components/members-table/offboard-managed-user-modal.tsx index 5e2da7ed79..c5288abc0c 100644 --- a/services/web/frontend/js/features/group-management/components/members-table/offboard-managed-user-modal.tsx +++ b/services/web/frontend/js/features/group-management/components/members-table/offboard-managed-user-modal.tsx @@ -93,7 +93,11 @@ export default function OffboardManagedUserModal({

{t('this_action_cannot_be_reversed')}   - + {t('learn_more_about_managed_users')}

diff --git a/services/web/frontend/js/features/ide-react/components/modals/out-of-sync-modal.tsx b/services/web/frontend/js/features/ide-react/components/modals/out-of-sync-modal.tsx index bceb0d5f6a..5909f5eef2 100644 --- a/services/web/frontend/js/features/ide-react/components/modals/out-of-sync-modal.tsx +++ b/services/web/frontend/js/features/ide-react/components/modals/out-of-sync-modal.tsx @@ -50,7 +50,7 @@ function OutOfSyncModal({ editorContent, show, onHide }: OutOfSyncModalProps) { , ]} /> diff --git a/services/web/frontend/js/features/monthly-texlive/rolling-compile-image-changed-alert.tsx b/services/web/frontend/js/features/monthly-texlive/rolling-compile-image-changed-alert.tsx index e04632e009..6b5e6bbdd8 100644 --- a/services/web/frontend/js/features/monthly-texlive/rolling-compile-image-changed-alert.tsx +++ b/services/web/frontend/js/features/monthly-texlive/rolling-compile-image-changed-alert.tsx @@ -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" />, ]} /> diff --git a/services/web/frontend/js/features/pdf-preview/components/pdf-preview-error-boundary-fallback.tsx b/services/web/frontend/js/features/pdf-preview/components/pdf-preview-error-boundary-fallback.tsx index 8e374deab3..cea207290b 100644 --- a/services/web/frontend/js/features/pdf-preview/components/pdf-preview-error-boundary-fallback.tsx +++ b/services/web/frontend/js/features/pdf-preview/components/pdf-preview-error-boundary-fallback.tsx @@ -14,8 +14,9 @@ function PdfPreviewErrorBoundaryFallback({ components={[ // eslint-disable-next-line jsx-a11y/anchor-has-content , ]} diff --git a/services/web/frontend/js/features/pdf-preview/components/pdf-preview-error.tsx b/services/web/frontend/js/features/pdf-preview/components/pdf-preview-error.tsx index cb6be2f36f..d73eb8ec7f 100644 --- a/services/web/frontend/js/features/pdf-preview/components/pdf-preview-error.tsx +++ b/services/web/frontend/js/features/pdf-preview/components/pdf-preview-error.tsx @@ -46,8 +46,9 @@ function PdfPreviewError({ components={[ // eslint-disable-next-line jsx-a11y/anchor-has-content , ]} @@ -70,8 +71,9 @@ function PdfPreviewError({ components={[ // eslint-disable-next-line jsx-a11y/anchor-has-content , ]} @@ -93,8 +95,9 @@ function PdfPreviewError({ , /* eslint-disable-next-line jsx-a11y/anchor-has-content */ , ]} @@ -224,8 +227,9 @@ function PdfPreviewError({ , // eslint-disable-next-line jsx-a11y/anchor-has-content , // 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 { 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 { 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 { sendMB('paywall-info-click', { 'paywall-type': 'compile-timeout', diff --git a/services/web/frontend/js/features/pdf-preview/components/synctex-toasts.tsx b/services/web/frontend/js/features/pdf-preview/components/synctex-toasts.tsx index c45d27b92f..ad0a7f53b9 100644 --- a/services/web/frontend/js/features/pdf-preview/components/synctex-toasts.tsx +++ b/services/web/frontend/js/features/pdf-preview/components/synctex-toasts.tsx @@ -10,8 +10,9 @@ export const SynctexFileErrorToast = () => { {t('synctex_failed')} diff --git a/services/web/frontend/js/features/preview/components/preview-logs-pane-max-entries.tsx b/services/web/frontend/js/features/preview/components/preview-logs-pane-max-entries.tsx index 1ec82cbda3..cd7ac714ad 100644 --- a/services/web/frontend/js/features/preview/components/preview-logs-pane-max-entries.tsx +++ b/services/web/frontend/js/features/preview/components/preview-logs-pane-max-entries.tsx @@ -71,7 +71,7 @@ function PreviewLogsPaneMaxEntriesContent({ // eslint-disable-next-line jsx-a11y/anchor-has-content , ]} />{' '} diff --git a/services/web/frontend/js/features/project-list/components/current-plan-widget/current-plan-widget.tsx b/services/web/frontend/js/features/project-list/components/current-plan-widget/current-plan-widget.tsx index 9aabc9fd16..081263769f 100644 --- a/services/web/frontend/js/features/project-list/components/current-plan-widget/current-plan-widget.tsx +++ b/services/web/frontend/js/features/project-list/components/current-plan-widget/current-plan-widget.tsx @@ -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 diff --git a/services/web/frontend/js/features/project-list/components/modals/archive-project-modal.tsx b/services/web/frontend/js/features/project-list/components/modals/archive-project-modal.tsx index ede1ee95b7..87e43e7ac7 100644 --- a/services/web/frontend/js/features/project-list/components/modals/archive-project-modal.tsx +++ b/services/web/frontend/js/features/project-list/components/modals/archive-project-modal.tsx @@ -43,9 +43,9 @@ function ArchiveProjectModal({

{t('archiving_projects_wont_affect_collaborators')}{' '} {t('find_out_more_nt')} diff --git a/services/web/frontend/js/features/project-list/components/modals/trash-project-modal.tsx b/services/web/frontend/js/features/project-list/components/modals/trash-project-modal.tsx index 7c4837f86d..8b43af1f0d 100644 --- a/services/web/frontend/js/features/project-list/components/modals/trash-project-modal.tsx +++ b/services/web/frontend/js/features/project-list/components/modals/trash-project-modal.tsx @@ -43,9 +43,9 @@ function TrashProjectModal({

{t('trashing_projects_wont_affect_collaborators')}{' '} {t('find_out_more_nt')} diff --git a/services/web/frontend/js/features/project-list/components/notifications/groups/affiliation/reconfirm-affiliation.tsx b/services/web/frontend/js/features/project-list/components/notifications/groups/affiliation/reconfirm-affiliation.tsx index dc5e908c0b..d7cc9bb602 100644 --- a/services/web/frontend/js/features/project-list/components/notifications/groups/affiliation/reconfirm-affiliation.tsx +++ b/services/web/frontend/js/features/project-list/components/notifications/groups/affiliation/reconfirm-affiliation.tsx @@ -46,8 +46,9 @@ function ReconfirmAffiliation({ />   {t('learn_more_about_email_reconfirmation')} diff --git a/services/web/frontend/js/features/project-list/components/notifications/groups/common.tsx b/services/web/frontend/js/features/project-list/components/notifications/groups/common.tsx index d4f6a3f368..bf22d7c60b 100644 --- a/services/web/frontend/js/features/project-list/components/notifications/groups/common.tsx +++ b/services/web/frontend/js/features/project-list/components/notifications/groups/common.tsx @@ -152,7 +152,7 @@ function CommonNotification({ notification }: CommonNotificationProps) { {t('find_out_more_nt')} @@ -234,8 +234,9 @@ function CommonNotification({ notification }: CommonNotificationProps) { components={[]} // eslint-disable-line react/jsx-key />{' '} {t('learn_more')} @@ -265,8 +266,9 @@ function CommonNotification({ notification }: CommonNotificationProps) { t('confirm_affiliation_to_relink_dropbox') )}{' '} {t('learn_more')} diff --git a/services/web/frontend/js/features/project-list/components/notifications/groups/institution.tsx b/services/web/frontend/js/features/project-list/components/notifications/groups/institution.tsx index 87b0433270..117e17d6f5 100644 --- a/services/web/frontend/js/features/project-list/components/notifications/groups/institution.tsx +++ b/services/web/frontend/js/features/project-list/components/notifications/groups/institution.tsx @@ -55,8 +55,9 @@ function Institution() { tOptions={{ interpolation: { escapeValue: true } }} />{' '} {t('learn_more')} @@ -122,8 +123,9 @@ function Institution() { />   {t('understand_managed_user_accounts')} @@ -175,8 +177,9 @@ function Institution() { action={ {t('find_out_more')} diff --git a/services/web/frontend/js/features/settings/components/emails-section.tsx b/services/web/frontend/js/features/settings/components/emails-section.tsx index cefe225b00..e80230c62f 100644 --- a/services/web/frontend/js/features/settings/components/emails-section.tsx +++ b/services/web/frontend/js/features/settings/components/emails-section.tsx @@ -51,8 +51,9 @@ function EmailsSectionContent() { , // eslint-disable-next-line jsx-a11y/anchor-has-content, react/jsx-key , ]} /> diff --git a/services/web/frontend/js/features/settings/components/emails/add-email/sso-linking-info.tsx b/services/web/frontend/js/features/settings/components/emails/add-email/sso-linking-info.tsx index be82b8674e..f9f0c7fbed 100644 --- a/services/web/frontend/js/features/settings/components/emails/add-email/sso-linking-info.tsx +++ b/services/web/frontend/js/features/settings/components/emails/add-email/sso-linking-info.tsx @@ -46,7 +46,7 @@ function SsoLinkingInfo({ domainInfo, email }: SSOLinkingInfoProps) { tOptions={{ interpolation: { escapeValue: true } }} />{' '} diff --git a/services/web/frontend/js/features/settings/components/emails/add-secondary-email-prompt.tsx b/services/web/frontend/js/features/settings/components/emails/add-secondary-email-prompt.tsx index 2744457b6e..60d3421de6 100644 --- a/services/web/frontend/js/features/settings/components/emails/add-secondary-email-prompt.tsx +++ b/services/web/frontend/js/features/settings/components/emails/add-secondary-email-prompt.tsx @@ -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 - , + , ]} />

diff --git a/services/web/frontend/js/features/settings/components/emails/reconfirmation-info/reconfirmation-info-prompt-text.tsx b/services/web/frontend/js/features/settings/components/emails/reconfirmation-info/reconfirmation-info-prompt-text.tsx index 20a9705e60..9a4b9977a0 100644 --- a/services/web/frontend/js/features/settings/components/emails/reconfirmation-info/reconfirmation-info-prompt-text.tsx +++ b/services/web/frontend/js/features/settings/components/emails/reconfirmation-info/reconfirmation-info-prompt-text.tsx @@ -34,8 +34,9 @@ function ReconfirmationInfoPromptText({ } />{' '} {t('learn_more_about_email_reconfirmation')} diff --git a/services/web/frontend/js/features/settings/components/emails/row.tsx b/services/web/frontend/js/features/settings/components/emails/row.tsx index 2ba0fd5a58..592162c1e8 100644 --- a/services/web/frontend/js/features/settings/components/emails/row.tsx +++ b/services/web/frontend/js/features/settings/components/emails/row.tsx @@ -186,7 +186,11 @@ function SSOAffiliationInfo({ userEmailData }: SSOAffiliationInfoProps) { [] } />{' '} - + {t('find_out_more_about_institution_login')}

diff --git a/services/web/frontend/js/features/settings/components/linking/integration-widget.tsx b/services/web/frontend/js/features/settings/components/linking/integration-widget.tsx index 90edda8de4..12a11fa527 100644 --- a/services/web/frontend/js/features/settings/components/linking/integration-widget.tsx +++ b/services/web/frontend/js/features/settings/components/linking/integration-widget.tsx @@ -73,7 +73,7 @@ export function IntegrationLinkingWidget({

{description}{' '} - + {t('learn_more_about', { appName: title })}

diff --git a/services/web/frontend/js/features/settings/components/managed-account-alert.tsx b/services/web/frontend/js/features/settings/components/managed-account-alert.tsx index 02afa8d4c8..c4ea786b2e 100644 --- a/services/web/frontend/js/features/settings/components/managed-account-alert.tsx +++ b/services/web/frontend/js/features/settings/components/managed-account-alert.tsx @@ -30,8 +30,9 @@ export default function ManagedAccountAlert() { components={[ // eslint-disable-next-line jsx-a11y/anchor-has-content, react/jsx-key , ]} /> diff --git a/services/web/frontend/js/features/settings/components/password-section.tsx b/services/web/frontend/js/features/settings/components/password-section.tsx index 9128119dc3..7dacfc468a 100644 --- a/services/web/frontend/js/features/settings/components/password-section.tsx +++ b/services/web/frontend/js/features/settings/components/password-section.tsx @@ -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 - , + , ]} />

diff --git a/services/web/frontend/js/features/share-project-modal/components/link-sharing.tsx b/services/web/frontend/js/features/share-project-modal/components/link-sharing.tsx index 5c5a619156..3735765bf7 100644 --- a/services/web/frontend/js/features/share-project-modal/components/link-sharing.tsx +++ b/services/web/frontend/js/features/share-project-modal/components/link-sharing.tsx @@ -318,9 +318,9 @@ function LinkSharingInfo() { description={t('learn_more_about_link_sharing')} >
diff --git a/services/web/frontend/js/features/source-editor/components/figure-modal/figure-modal-help.tsx b/services/web/frontend/js/features/source-editor/components/figure-modal/figure-modal-help.tsx index d5f8f0f1fe..4634a30cf6 100644 --- a/services/web/frontend/js/features/source-editor/components/figure-modal/figure-modal-help.tsx +++ b/services/web/frontend/js/features/source-editor/components/figure-modal/figure-modal-help.tsx @@ -1,11 +1,11 @@ import { FC } from 'react' import { Trans, useTranslation } from 'react-i18next' -const LearnWikiLink: FC> = ({ - article, +const LearnWikiLink: FC> = ({ + href, children, }) => { - return {children} + return {children} } export const FigureModalHelp = () => { @@ -24,7 +24,7 @@ export const FigureModalHelp = () => { // eslint-disable-next-line react/jsx-key , // eslint-disable-next-line react/jsx-key - , + , ]} />

@@ -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 - , + , ]} />

@@ -46,7 +46,7 @@ export const FigureModalHelp = () => { i18nKey="latex_places_figures_according_to_a_special_algorithm" components={[ // eslint-disable-next-line react/jsx-key - , + , ]} />

@@ -59,7 +59,7 @@ export const FigureModalHelp = () => { // eslint-disable-next-line react/jsx-key , // eslint-disable-next-line react/jsx-key - , + , ]} />

diff --git a/services/web/frontend/js/features/source-editor/components/table-generator/help-modal.tsx b/services/web/frontend/js/features/source-editor/components/table-generator/help-modal.tsx index c5769a055a..7469dd262a 100644 --- a/services/web/frontend/js/features/source-editor/components/table-generator/help-modal.tsx +++ b/services/web/frontend/js/features/source-editor/components/table-generator/help-modal.tsx @@ -74,9 +74,9 @@ export const TableGeneratorHelpModal = () => { , // eslint-disable-next-line react/jsx-key, jsx-a11y/anchor-has-content , ]} /> diff --git a/services/web/frontend/js/features/subscription/components/dashboard/premium-features-link.tsx b/services/web/frontend/js/features/subscription/components/dashboard/premium-features-link.tsx index 14d9c8682a..104c8ea518 100644 --- a/services/web/frontend/js/features/subscription/components/dashboard/premium-features-link.tsx +++ b/services/web/frontend/js/features/subscription/components/dashboard/premium-features-link.tsx @@ -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 - , + , // eslint-disable-next-line react/jsx-key, jsx-a11y/anchor-has-content , + , // eslint-disable-next-line react/jsx-key, jsx-a11y/anchor-has-content - , + , // eslint-disable-next-line react/jsx-key, jsx-a11y/anchor-has-content , + , ]} />

diff --git a/services/web/frontend/js/features/subscription/components/group-invite/managed-user-cannot-join.tsx b/services/web/frontend/js/features/subscription/components/group-invite/managed-user-cannot-join.tsx index 03e3a757ba..ca844ac648 100644 --- a/services/web/frontend/js/features/subscription/components/group-invite/managed-user-cannot-join.tsx +++ b/services/web/frontend/js/features/subscription/components/group-invite/managed-user-cannot-join.tsx @@ -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 */ -
, + , ]} />

diff --git a/services/web/frontend/js/shared/components/ai-consent-prompt-message.tsx b/services/web/frontend/js/shared/components/ai-consent-prompt-message.tsx index 50ecc4f4b2..f72b00da79 100644 --- a/services/web/frontend/js/shared/components/ai-consent-prompt-message.tsx +++ b/services/web/frontend/js/shared/components/ai-consent-prompt-message.tsx @@ -39,7 +39,7 @@ export const AiConsentPromptMessageBody = ({
our docs diff --git a/services/web/frontend/js/shared/components/deprecated-browser.tsx b/services/web/frontend/js/shared/components/deprecated-browser.tsx index 38a9efe136..d614b717a3 100644 --- a/services/web/frontend/js/shared/components/deprecated-browser.tsx +++ b/services/web/frontend/js/shared/components/deprecated-browser.tsx @@ -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 - , + , ]} /> } diff --git a/services/web/test/frontend/features/group-management/components/domain-capture.spec.tsx b/services/web/test/frontend/features/group-management/components/domain-capture.spec.tsx index 8ae9716235..09d0286cb2 100644 --- a/services/web/test/frontend/features/group-management/components/domain-capture.spec.tsx +++ b/services/web/test/frontend/features/group-management/components/domain-capture.spec.tsx @@ -135,7 +135,7 @@ describe('', function () { }).should( 'have.attr', 'href', - '/learn/how-to/How_to_Transfer_Project_Ownership' + 'https://docs.overleaf.com/collaborating/transferring-project-ownership' ) }) diff --git a/services/web/test/frontend/features/project-list/components/current-plan-widget.test.tsx b/services/web/test/frontend/features/project-list/components/current-plan-widget.test.tsx index edfe1b4864..53910062d2 100644 --- a/services/web/test/frontend/features/project-list/components/current-plan-widget.test.tsx +++ b/services/web/test/frontend/features/project-list/components/current-plan-widget.test.tsx @@ -337,7 +337,7 @@ describe('', 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') diff --git a/services/web/test/frontend/features/project-list/components/notifications.test.tsx b/services/web/test/frontend/features/project-list/components/notifications.test.tsx index 3dfc27b51e..d295c47337 100644 --- a/services/web/test/frontend/features/project-list/components/notifications.test.tsx +++ b/services/web/test/frontend/features/project-list/components/notifications.test.tsx @@ -239,7 +239,7 @@ describe('', 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('', 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('', 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('', 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('', 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('', 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( diff --git a/services/web/test/frontend/features/subscription/components/successful-subscription/successful-subscription.test.tsx b/services/web/test/frontend/features/subscription/components/successful-subscription/successful-subscription.test.tsx index ed6fef8aba..3d6a5784af 100644 --- a/services/web/test/frontend/features/subscription/components/successful-subscription/successful-subscription.test.tsx +++ b/services/web/test/frontend/features/subscription/components/successful-subscription/successful-subscription.test.tsx @@ -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', {