mirror of
https://github.com/yu-i-i/overleaf-cep.git
synced 2026-05-23 17:19:37 +02:00
Upgrade React to v17 (#4201)
* Upgrade react and react-dom * Fix test * Ensure that the "history:toggle" event is broadcast when switching in or out of history view * Add ControlledDropdown * Remove DropdownButton stories GitOrigin-RevId: 3810f6986bb60e59af31f960f431c31be16554f5
This commit is contained in:
@@ -4,6 +4,7 @@ import { useTranslation } from 'react-i18next'
|
||||
import { Dropdown, MenuItem, OverlayTrigger, Tooltip } from 'react-bootstrap'
|
||||
import Icon from '../../../shared/components/icon'
|
||||
import { getHueForUserId } from '../../../shared/utils/colors'
|
||||
import ControlledDropdown from '../../../shared/components/controlled-dropdown'
|
||||
|
||||
function OnlineUsersWidget({ onlineUsers, goToUser }) {
|
||||
const { t } = useTranslation()
|
||||
@@ -12,7 +13,7 @@ function OnlineUsersWidget({ onlineUsers, goToUser }) {
|
||||
|
||||
if (shouldDisplayDropdown) {
|
||||
return (
|
||||
<Dropdown id="online-users" className="online-users" pullRight>
|
||||
<ControlledDropdown id="online-users" className="online-users" pullRight>
|
||||
<DropDownToggleButton
|
||||
bsRole="toggle"
|
||||
onlineUserCount={onlineUsers.length}
|
||||
@@ -30,7 +31,7 @@ function OnlineUsersWidget({ onlineUsers, goToUser }) {
|
||||
</MenuItem>
|
||||
))}
|
||||
</Dropdown.Menu>
|
||||
</Dropdown>
|
||||
</ControlledDropdown>
|
||||
)
|
||||
} else {
|
||||
return (
|
||||
|
||||
@@ -4,6 +4,7 @@ import { Dropdown, OverlayTrigger, Tooltip } from 'react-bootstrap'
|
||||
import { useTranslation } from 'react-i18next'
|
||||
import PreviewDownloadFileList from './preview-download-file-list'
|
||||
import Icon from '../../../shared/components/icon'
|
||||
import ControlledDropdown from '../../../shared/components/controlled-dropdown'
|
||||
|
||||
function PreviewDownloadButton({
|
||||
isCompiling,
|
||||
@@ -40,7 +41,7 @@ function PreviewDownloadButton({
|
||||
const hideTooltip = showText && pdfDownloadUrl
|
||||
|
||||
return (
|
||||
<Dropdown
|
||||
<ControlledDropdown
|
||||
id="download-dropdown"
|
||||
className="toolbar-item"
|
||||
disabled={isCompiling}
|
||||
@@ -69,7 +70,7 @@ function PreviewDownloadButton({
|
||||
<Dropdown.Menu id="download-dropdown-list">
|
||||
<PreviewDownloadFileList fileList={outputFiles} />
|
||||
</Dropdown.Menu>
|
||||
</Dropdown>
|
||||
</ControlledDropdown>
|
||||
)
|
||||
}
|
||||
|
||||
|
||||
@@ -8,6 +8,7 @@ import PreviewDownloadFileList from './preview-download-file-list'
|
||||
import PreviewError from './preview-error'
|
||||
import Icon from '../../../shared/components/icon'
|
||||
import usePersistedState from '../../../shared/hooks/use-persisted-state'
|
||||
import ControlledDropdown from '../../../shared/components/controlled-dropdown'
|
||||
|
||||
function PreviewLogsPane({
|
||||
logEntries = { all: [], errors: [], warnings: [], typesetting: [] },
|
||||
@@ -84,7 +85,7 @@ function PreviewLogsPane({
|
||||
|
||||
<span>{t('clear_cached_files')}</span>
|
||||
</button>
|
||||
<Dropdown
|
||||
<ControlledDropdown
|
||||
id="dropdown-files-logs-pane"
|
||||
dropup
|
||||
pullRight
|
||||
@@ -98,7 +99,7 @@ function PreviewLogsPane({
|
||||
<Dropdown.Menu id="dropdown-files-logs-pane-list">
|
||||
<PreviewDownloadFileList fileList={outputFiles} />
|
||||
</Dropdown.Menu>
|
||||
</Dropdown>
|
||||
</ControlledDropdown>
|
||||
</div>
|
||||
)
|
||||
|
||||
|
||||
@@ -4,6 +4,7 @@ import { Dropdown, MenuItem, OverlayTrigger, Tooltip } from 'react-bootstrap'
|
||||
import { useTranslation } from 'react-i18next'
|
||||
import classNames from 'classnames'
|
||||
import Icon from '../../../shared/components/icon'
|
||||
import ControlledDropdown from '../../../shared/components/controlled-dropdown'
|
||||
|
||||
function PreviewRecompileButton({
|
||||
compilerState: {
|
||||
@@ -80,7 +81,7 @@ function PreviewRecompileButton({
|
||||
)
|
||||
|
||||
const buttonElement = (
|
||||
<Dropdown
|
||||
<ControlledDropdown
|
||||
id="pdf-recompile-dropdown"
|
||||
className={recompileButtonGroupClasses}
|
||||
>
|
||||
@@ -151,7 +152,7 @@ function PreviewRecompileButton({
|
||||
{t('recompile_from_scratch')}
|
||||
</MenuItem>
|
||||
</Dropdown.Menu>
|
||||
</Dropdown>
|
||||
</ControlledDropdown>
|
||||
)
|
||||
|
||||
return showText ? (
|
||||
|
||||
@@ -0,0 +1,47 @@
|
||||
import React, { useCallback, useState } from 'react'
|
||||
import { Dropdown } from 'react-bootstrap'
|
||||
import PropTypes from 'prop-types'
|
||||
|
||||
export default function ControlledDropdown(props) {
|
||||
const [open, setOpen] = useState(Boolean(props.defaultOpen))
|
||||
|
||||
const handleClick = useCallback(event => {
|
||||
event.stopPropagation()
|
||||
}, [])
|
||||
|
||||
const handleToggle = useCallback(value => {
|
||||
setOpen(value)
|
||||
}, [])
|
||||
|
||||
return (
|
||||
<Dropdown
|
||||
{...props}
|
||||
open={open}
|
||||
onToggle={handleToggle}
|
||||
onClick={handleClick}
|
||||
>
|
||||
{React.Children.map(props.children, child => {
|
||||
if (!React.isValidElement(child)) {
|
||||
return child
|
||||
}
|
||||
|
||||
// Dropdown.Menu
|
||||
if ('open' in child.props) {
|
||||
return React.cloneElement(child, { open })
|
||||
}
|
||||
|
||||
// Overlay
|
||||
if ('show' in child.props) {
|
||||
return React.cloneElement(child, { show: open })
|
||||
}
|
||||
|
||||
// anything else
|
||||
return React.cloneElement(child)
|
||||
})}
|
||||
</Dropdown>
|
||||
)
|
||||
}
|
||||
ControlledDropdown.propTypes = {
|
||||
children: PropTypes.any,
|
||||
defaultOpen: PropTypes.bool,
|
||||
}
|
||||
@@ -26,10 +26,14 @@ export function LayoutProvider({ children }) {
|
||||
|
||||
const setView = useCallback(
|
||||
value => {
|
||||
_setView(value)
|
||||
if (value === 'history') {
|
||||
$scope.toggleHistory()
|
||||
}
|
||||
_setView(oldValue => {
|
||||
// ensure that the "history:toggle" event is broadcast when switching in or out of history view
|
||||
if (value === 'history' || oldValue === 'history') {
|
||||
$scope.toggleHistory()
|
||||
}
|
||||
|
||||
return value
|
||||
})
|
||||
},
|
||||
[$scope, _setView]
|
||||
)
|
||||
|
||||
@@ -1,77 +1,10 @@
|
||||
import React from 'react'
|
||||
import { Dropdown, DropdownButton, MenuItem } from 'react-bootstrap'
|
||||
import Icon from '../js/shared/components/icon'
|
||||
|
||||
const MenuItems = () => (
|
||||
<>
|
||||
<MenuItem eventKey="1">Action</MenuItem>
|
||||
<MenuItem eventKey="2">Another action</MenuItem>
|
||||
<MenuItem eventKey="3" active>
|
||||
Active Item
|
||||
</MenuItem>
|
||||
<MenuItem divider />
|
||||
<MenuItem eventKey="4">Separated link</MenuItem>
|
||||
</>
|
||||
)
|
||||
|
||||
const defaultArgs = {
|
||||
bsStyle: 'default',
|
||||
title: 'Dropdown',
|
||||
pullRight: false,
|
||||
noCaret: false,
|
||||
className: '',
|
||||
defaultOpen: true,
|
||||
}
|
||||
|
||||
export const Default = args => {
|
||||
return (
|
||||
<DropdownButton {...args}>
|
||||
<MenuItems />
|
||||
</DropdownButton>
|
||||
)
|
||||
}
|
||||
Default.args = { ...defaultArgs }
|
||||
|
||||
export const Primary = args => {
|
||||
return (
|
||||
<DropdownButton {...args}>
|
||||
<MenuItems />
|
||||
</DropdownButton>
|
||||
)
|
||||
}
|
||||
Primary.args = { ...defaultArgs, bsStyle: 'primary' }
|
||||
|
||||
export const RightAligned = args => {
|
||||
return (
|
||||
<div style={{ textAlign: 'right' }}>
|
||||
<DropdownButton {...args}>
|
||||
<MenuItems />
|
||||
</DropdownButton>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
RightAligned.args = { ...defaultArgs, pullRight: true }
|
||||
|
||||
export const SingleIconTransparent = args => {
|
||||
return (
|
||||
<div style={{ textAlign: 'right' }}>
|
||||
<DropdownButton {...args}>
|
||||
<MenuItems />
|
||||
</DropdownButton>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
SingleIconTransparent.args = {
|
||||
...defaultArgs,
|
||||
pullRight: true,
|
||||
title: <Icon type="ellipsis-v" />,
|
||||
noCaret: true,
|
||||
className: 'dropdown-toggle-no-background',
|
||||
}
|
||||
import { Dropdown, MenuItem } from 'react-bootstrap'
|
||||
import ControlledDropdown from '../js/shared/components/controlled-dropdown'
|
||||
|
||||
export const Customized = args => {
|
||||
return (
|
||||
<Dropdown
|
||||
<ControlledDropdown
|
||||
pullRight={args.pullRight}
|
||||
defaultOpen={args.defaultOpen}
|
||||
id="dropdown-story"
|
||||
@@ -84,18 +17,30 @@ export const Customized = args => {
|
||||
{args.title}
|
||||
</Dropdown.Toggle>
|
||||
<Dropdown.Menu>
|
||||
<MenuItems />
|
||||
<MenuItem eventKey="1">Action</MenuItem>
|
||||
<MenuItem eventKey="2">Another action</MenuItem>
|
||||
<MenuItem eventKey="3" active>
|
||||
Active Item
|
||||
</MenuItem>
|
||||
<MenuItem divider />
|
||||
<MenuItem eventKey="4">Separated link</MenuItem>
|
||||
</Dropdown.Menu>
|
||||
</Dropdown>
|
||||
</ControlledDropdown>
|
||||
)
|
||||
}
|
||||
Customized.args = {
|
||||
...defaultArgs,
|
||||
component: Dropdown,
|
||||
title: 'Toggle & Menu used separately',
|
||||
}
|
||||
|
||||
export default {
|
||||
title: 'Dropdown',
|
||||
component: DropdownButton,
|
||||
component: ControlledDropdown,
|
||||
args: {
|
||||
bsStyle: 'default',
|
||||
title: 'Dropdown',
|
||||
pullRight: false,
|
||||
noCaret: false,
|
||||
className: '',
|
||||
defaultOpen: true,
|
||||
},
|
||||
}
|
||||
|
||||
36
services/web/package-lock.json
generated
36
services/web/package-lock.json
generated
@@ -21275,7 +21275,17 @@
|
||||
"string_decoder": {
|
||||
"version": "1.3.0",
|
||||
"resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.3.0.tgz",
|
||||
"integrity": "sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA=="
|
||||
"integrity": "sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA==",
|
||||
"requires": {
|
||||
"safe-buffer": "~5.2.0"
|
||||
},
|
||||
"dependencies": {
|
||||
"safe-buffer": {
|
||||
"version": "5.2.1",
|
||||
"resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz",
|
||||
"integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ=="
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
@@ -30243,13 +30253,12 @@
|
||||
}
|
||||
},
|
||||
"react": {
|
||||
"version": "16.13.1",
|
||||
"resolved": "https://registry.npmjs.org/react/-/react-16.13.1.tgz",
|
||||
"integrity": "sha512-YMZQQq32xHLX0bz5Mnibv1/LHb3Sqzngu7xstSM+vrkE5Kzr9xE0yMByK5kMoTK30YVJE61WfbxIFFvfeDKT1w==",
|
||||
"version": "17.0.2",
|
||||
"resolved": "https://registry.npmjs.org/react/-/react-17.0.2.tgz",
|
||||
"integrity": "sha512-gnhPt75i/dq/z3/6q/0asP78D0u592D5L1pd7M8P+dck6Fu/jJeL6iVVK23fptSUZj8Vjf++7wXA8UNclGQcbA==",
|
||||
"requires": {
|
||||
"loose-envify": "^1.1.0",
|
||||
"object-assign": "^4.1.1",
|
||||
"prop-types": "^15.6.2"
|
||||
"object-assign": "^4.1.1"
|
||||
}
|
||||
},
|
||||
"react-bootstrap": {
|
||||
@@ -30677,14 +30686,13 @@
|
||||
}
|
||||
},
|
||||
"react-dom": {
|
||||
"version": "16.13.1",
|
||||
"resolved": "https://registry.npmjs.org/react-dom/-/react-dom-16.13.1.tgz",
|
||||
"integrity": "sha512-81PIMmVLnCNLO/fFOQxdQkvEq/+Hfpv24XNJfpyZhTRfO0QcmQIF/PgCa1zCOj2w1hrn12MFLyaJ/G0+Mxtfag==",
|
||||
"version": "17.0.2",
|
||||
"resolved": "https://registry.npmjs.org/react-dom/-/react-dom-17.0.2.tgz",
|
||||
"integrity": "sha512-s4h96KtLDUQlsENhMn1ar8t2bEa+q/YAtj8pPPdIjPDGBDIVNsrD9aXNWqspUe6AzKCIG0C1HZZLqLV7qpOBGA==",
|
||||
"requires": {
|
||||
"loose-envify": "^1.1.0",
|
||||
"object-assign": "^4.1.1",
|
||||
"prop-types": "^15.6.2",
|
||||
"scheduler": "^0.19.1"
|
||||
"scheduler": "^0.20.2"
|
||||
}
|
||||
},
|
||||
"react-draggable": {
|
||||
@@ -32423,9 +32431,9 @@
|
||||
}
|
||||
},
|
||||
"scheduler": {
|
||||
"version": "0.19.1",
|
||||
"resolved": "https://registry.npmjs.org/scheduler/-/scheduler-0.19.1.tgz",
|
||||
"integrity": "sha512-n/zwRWRYSUj0/3g/otKDRPMh6qv2SYMWNq85IEa8iZyAv8od9zDYpGSnpBEjNgcMNq6Scbu5KfIPxNF72R/2EA==",
|
||||
"version": "0.20.2",
|
||||
"resolved": "https://registry.npmjs.org/scheduler/-/scheduler-0.20.2.tgz",
|
||||
"integrity": "sha512-2eWfGgAqqWFGqtdMmcL5zCMK1U8KlXv8SQFGglL3CEtd0aDVDWgeF/YoCmvln55m5zSk3J/20hTaSBeSObsQDQ==",
|
||||
"requires": {
|
||||
"loose-envify": "^1.1.0",
|
||||
"object-assign": "^4.1.1"
|
||||
|
||||
@@ -141,11 +141,11 @@
|
||||
"pug": "^3.0.1",
|
||||
"pug-runtime": "^3.0.1",
|
||||
"qrcode": "^1.4.4",
|
||||
"react": "^16.13.1",
|
||||
"react": "^17.0.2",
|
||||
"react-bootstrap": "^0.33.1",
|
||||
"react-dnd": "^11.1.3",
|
||||
"react-dnd-html5-backend": "^11.1.3",
|
||||
"react-dom": "^16.13.1",
|
||||
"react-dom": "^17.0.2",
|
||||
"react-error-boundary": "^2.3.1",
|
||||
"react-i18next": "^11.7.1",
|
||||
"react-linkify": "^1.0.0-alpha",
|
||||
|
||||
@@ -658,11 +658,11 @@ describe('<ShareProjectModal/>', function () {
|
||||
const submitButton = screen.getByRole('button', { name: 'Share' })
|
||||
|
||||
const respondWithError = async function (errorReason) {
|
||||
inputElement.focus()
|
||||
fireEvent.focus(inputElement)
|
||||
fireEvent.change(inputElement, {
|
||||
target: { value: 'invited-author-1@example.com' },
|
||||
})
|
||||
inputElement.blur()
|
||||
fireEvent.blur(inputElement)
|
||||
|
||||
fetchMock.postOnce(
|
||||
'express:/project/:projectId/invite',
|
||||
|
||||
Reference in New Issue
Block a user