mirror of
https://github.com/overleaf/overleaf.git
synced 2025-04-16 13:29:37 +00:00
Implement grammarly warning alert on cm6 users
GitOrigin-RevId: ed272bbc385faa69811ec1891075906cdca1c984
This commit is contained in:
parent
87b844aa15
commit
d70e0b1c0e
6 changed files with 290 additions and 0 deletions
|
@ -65,6 +65,8 @@ block content
|
|||
span.sr-only #{translate("close")}
|
||||
.system-message-content(ng-bind-html="htmlContent")
|
||||
|
||||
grammarly-warning()
|
||||
|
||||
include ./editor/main
|
||||
|
||||
script(type="text/ng-template", id="genericMessageModalTemplate")
|
||||
|
|
|
@ -0,0 +1,65 @@
|
|||
import { useCallback, useEffect, useState } from 'react'
|
||||
import { Button } from 'react-bootstrap'
|
||||
import customLocalStorage from '../../../infrastructure/local-storage'
|
||||
import useScopeValue from '../../../shared/hooks/use-scope-value'
|
||||
import grammarlyExtensionPresent from '../../../shared/utils/grammarly'
|
||||
import getMeta from '../../../utils/meta'
|
||||
|
||||
export default function GrammarlyWarning() {
|
||||
const [show, setShow] = useState(false)
|
||||
const [newSourceEditor] = useScopeValue('editor.newSourceEditor')
|
||||
const [showRichText] = useScopeValue('editor.showRichText')
|
||||
const grammarly = grammarlyExtensionPresent()
|
||||
const hasDismissedGrammarlyWarning = customLocalStorage.getItem(
|
||||
'editor.has_dismissed_grammarly_warning'
|
||||
)
|
||||
|
||||
useEffect(() => {
|
||||
const showGrammarlyWarning =
|
||||
!hasDismissedGrammarlyWarning &&
|
||||
grammarly &&
|
||||
newSourceEditor &&
|
||||
!showRichText
|
||||
|
||||
if (showGrammarlyWarning) {
|
||||
setShow(true)
|
||||
}
|
||||
}, [grammarly, hasDismissedGrammarlyWarning, newSourceEditor, showRichText])
|
||||
|
||||
const handleClose = useCallback(() => {
|
||||
setShow(false)
|
||||
customLocalStorage.setItem('editor.has_dismissed_grammarly_warning', true)
|
||||
}, [])
|
||||
|
||||
if (!getMeta('ol-showNewSourceEditorOption')) {
|
||||
return null
|
||||
}
|
||||
|
||||
if (!show) {
|
||||
return null
|
||||
}
|
||||
|
||||
return (
|
||||
<div className="alert alert-info grammarly-warning" role="alert">
|
||||
<Button
|
||||
className="close"
|
||||
data-dismiss="alert"
|
||||
aria-label="Close"
|
||||
onClick={handleClose}
|
||||
>
|
||||
<span aria-hidden="true">×</span>
|
||||
</Button>
|
||||
<div className="warning-content">
|
||||
A browser extension, for example Grammarly, may be slowing down
|
||||
Overleaf.{' '}
|
||||
<a
|
||||
className="warning-link"
|
||||
href="/learn/how-to/Use_Grammarly_with_Overleaf#Performance_issues"
|
||||
target="_blank"
|
||||
>
|
||||
Find out how to avoid this
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
}
|
|
@ -0,0 +1,9 @@
|
|||
import App from '../../../base'
|
||||
import { react2angular } from 'react2angular'
|
||||
import { rootContext } from '../../../shared/context/root-context'
|
||||
import GrammarlyWarning from '../components/grammarly-warning'
|
||||
|
||||
App.component(
|
||||
'grammarlyWarning',
|
||||
react2angular(rootContext.use(GrammarlyWarning))
|
||||
)
|
|
@ -65,6 +65,7 @@ import './features/pdf-preview/controllers/pdf-preview-controller'
|
|||
import './features/share-project-modal/controllers/react-share-project-modal-controller'
|
||||
import './features/source-editor/controllers/editor-switch-controller'
|
||||
import './features/source-editor/controllers/cm6-switch-away-survey-controller'
|
||||
import './features/source-editor/controllers/grammarly-warning-controller'
|
||||
import { cleanupServiceWorker } from './utils/service-worker-cleanup'
|
||||
import { reportCM6Perf } from './infrastructure/cm6-performance'
|
||||
|
||||
|
|
|
@ -827,3 +827,32 @@ CodeMirror
|
|||
font-size: @font-size-small;
|
||||
}
|
||||
}
|
||||
|
||||
.grammarly-warning {
|
||||
width: 500px;
|
||||
|
||||
&.alert.alert-info {
|
||||
padding: @padding-sm;
|
||||
}
|
||||
|
||||
.btn.close {
|
||||
background-color: transparent;
|
||||
color: @white;
|
||||
opacity: 1;
|
||||
}
|
||||
|
||||
.warning-content {
|
||||
padding-right: @alert-padding;
|
||||
font-size: @font-size-small;
|
||||
margin-right: 32px;
|
||||
|
||||
.warning-link {
|
||||
font-weight: 700;
|
||||
text-decoration: none;
|
||||
|
||||
&:hover {
|
||||
text-decoration: underline;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,184 @@
|
|||
import sinon from 'sinon'
|
||||
import fetchMock from 'fetch-mock'
|
||||
import { expect } from 'chai'
|
||||
import { fireEvent, screen, waitFor } from '@testing-library/react'
|
||||
import { renderWithEditorContext } from '../../../helpers/render-with-context'
|
||||
import GrammarlyWarning from '../../../../../frontend/js/features/source-editor/components/grammarly-warning'
|
||||
import * as grammarlyModule from '../../../../../frontend/js/shared/utils/grammarly'
|
||||
import localStorage from '../../../../../frontend/js/infrastructure/local-storage'
|
||||
|
||||
describe('<GrammarlyWarning />', function () {
|
||||
let grammarlyStub
|
||||
|
||||
before(function () {
|
||||
window.metaAttributesCache = new Map()
|
||||
})
|
||||
|
||||
beforeEach(function () {
|
||||
grammarlyStub = sinon.stub(grammarlyModule, 'default')
|
||||
})
|
||||
|
||||
afterEach(function () {
|
||||
window.metaAttributesCache = new Map()
|
||||
grammarlyStub.restore()
|
||||
fetchMock.reset()
|
||||
localStorage.clear()
|
||||
})
|
||||
|
||||
it('shows warning when grammarly is available', async function () {
|
||||
grammarlyStub.returns(true)
|
||||
window.metaAttributesCache.set('ol-showNewSourceEditorOption', true)
|
||||
|
||||
renderWithEditorContext(<GrammarlyWarning />, {
|
||||
scope: {
|
||||
editor: {
|
||||
newSourceEditor: true,
|
||||
},
|
||||
},
|
||||
})
|
||||
|
||||
await screen.findByText(
|
||||
'A browser extension, for example Grammarly, may be slowing down Overleaf.'
|
||||
)
|
||||
await screen.findByRole('button', { name: 'Close' })
|
||||
await screen.findByRole('link', { name: 'Find out how to avoid this' })
|
||||
})
|
||||
|
||||
it('does not show warning when grammarly is not available', async function () {
|
||||
grammarlyStub.returns(false)
|
||||
window.metaAttributesCache.set('ol-showNewSourceEditorOption', true)
|
||||
|
||||
renderWithEditorContext(<GrammarlyWarning />, {
|
||||
scope: {
|
||||
editor: {
|
||||
newSourceEditor: true,
|
||||
},
|
||||
},
|
||||
})
|
||||
|
||||
await waitFor(() => {
|
||||
expect(
|
||||
screen.queryByText(
|
||||
'A browser extension, for example Grammarly, may be slowing down Overleaf.'
|
||||
)
|
||||
).to.not.exist
|
||||
})
|
||||
})
|
||||
|
||||
it('does not show warning when user has dismissed the warning', async function () {
|
||||
grammarlyStub.returns(true)
|
||||
localStorage.setItem('editor.has_dismissed_grammarly_warning', true)
|
||||
window.metaAttributesCache.set('ol-showNewSourceEditorOption', true)
|
||||
|
||||
renderWithEditorContext(<GrammarlyWarning />, {
|
||||
scope: {
|
||||
editor: {
|
||||
newSourceEditor: true,
|
||||
},
|
||||
},
|
||||
})
|
||||
|
||||
await waitFor(() => {
|
||||
expect(
|
||||
screen.queryByText(
|
||||
'A browser extension, for example Grammarly, may be slowing down Overleaf.'
|
||||
)
|
||||
).to.not.exist
|
||||
})
|
||||
})
|
||||
|
||||
it('does not show warning when user does not have CM6', async function () {
|
||||
grammarlyStub.returns(true)
|
||||
window.metaAttributesCache.set('ol-showNewSourceEditorOption', false)
|
||||
|
||||
renderWithEditorContext(<GrammarlyWarning />)
|
||||
|
||||
await waitFor(() => {
|
||||
expect(
|
||||
screen.queryByText(
|
||||
'A browser extension, for example Grammarly, may be slowing down Overleaf.'
|
||||
)
|
||||
).to.not.exist
|
||||
})
|
||||
})
|
||||
|
||||
it('does not show warning when user have ace as their preference', async function () {
|
||||
grammarlyStub.returns(true)
|
||||
window.metaAttributesCache.set('ol-showNewSourceEditorOption', true)
|
||||
|
||||
renderWithEditorContext(<GrammarlyWarning />, {
|
||||
scope: {
|
||||
editor: {
|
||||
newSourceEditor: false,
|
||||
},
|
||||
},
|
||||
})
|
||||
|
||||
await waitFor(() => {
|
||||
expect(
|
||||
screen.queryByText(
|
||||
'A browser extension, for example Grammarly, may be slowing down Overleaf.'
|
||||
)
|
||||
).to.not.exist
|
||||
})
|
||||
})
|
||||
|
||||
it('does not show warning when user have rich text as their preference', async function () {
|
||||
grammarlyStub.returns(true)
|
||||
window.metaAttributesCache.set('ol-showNewSourceEditorOption', true)
|
||||
|
||||
renderWithEditorContext(<GrammarlyWarning />, {
|
||||
scope: {
|
||||
editor: {
|
||||
newSourceEditor: true,
|
||||
showRichText: true,
|
||||
},
|
||||
},
|
||||
})
|
||||
|
||||
await waitFor(() => {
|
||||
expect(
|
||||
screen.queryByText(
|
||||
'A browser extension, for example Grammarly, may be slowing down Overleaf.'
|
||||
)
|
||||
).to.not.exist
|
||||
})
|
||||
})
|
||||
|
||||
it('hides warning if close button is pressed', async function () {
|
||||
grammarlyStub.returns(true)
|
||||
window.metaAttributesCache.set('ol-showNewSourceEditorOption', true)
|
||||
|
||||
renderWithEditorContext(<GrammarlyWarning />, {
|
||||
scope: {
|
||||
editor: {
|
||||
newSourceEditor: true,
|
||||
},
|
||||
},
|
||||
})
|
||||
|
||||
const warningText =
|
||||
'A browser extension, for example Grammarly, may be slowing down Overleaf.'
|
||||
|
||||
await screen.findByText(warningText)
|
||||
|
||||
const hasDismissedGrammarlyWarning = localStorage.getItem(
|
||||
'editor.has_dismissed_grammarly_warning'
|
||||
)
|
||||
|
||||
expect(hasDismissedGrammarlyWarning).to.equal(null)
|
||||
|
||||
const closeButton = screen.getByRole('button', { name: 'Close' })
|
||||
fireEvent.click(closeButton)
|
||||
|
||||
expect(screen.queryByText(warningText)).to.not.exist
|
||||
|
||||
await waitFor(() => {
|
||||
const hasDismissedGrammarlyWarning = localStorage.getItem(
|
||||
'editor.has_dismissed_grammarly_warning'
|
||||
)
|
||||
|
||||
expect(hasDismissedGrammarlyWarning).to.equal(true)
|
||||
})
|
||||
})
|
||||
})
|
Loading…
Add table
Reference in a new issue