mirror of
https://github.com/overleaf/overleaf.git
synced 2024-11-21 20:47:08 -05:00
Merge pull request #13710 from overleaf/ii-review-panel-migration-bulk-actions-entry
[web] Create bulk actions entry and bulk actions modal GitOrigin-RevId: c88ce6213304a110ee7410529813310b863178c1
This commit is contained in:
parent
9e8e124113
commit
13a7e752d5
11 changed files with 223 additions and 14 deletions
|
@ -14,6 +14,7 @@
|
|||
"about_to_leave_projects": "",
|
||||
"about_to_trash_projects": "",
|
||||
"accept": "",
|
||||
"accept_all": "",
|
||||
"accept_invitation": "",
|
||||
"accepted_invite": "",
|
||||
"access_denied": "",
|
||||
|
@ -87,6 +88,8 @@
|
|||
"blank_project": "",
|
||||
"blocked_filename": "",
|
||||
"browser": "",
|
||||
"bulk_accept_confirm": "",
|
||||
"bulk_reject_confirm": "",
|
||||
"by_subscribing_you_agree_to_our_terms_of_service": "",
|
||||
"can_edit": "",
|
||||
"can_link_institution_email_acct_to_institution_acct": "",
|
||||
|
@ -809,6 +812,7 @@
|
|||
"refreshing": "",
|
||||
"regards": "",
|
||||
"reject": "",
|
||||
"reject_all": "",
|
||||
"relink_your_account": "",
|
||||
"remote_service_error": "",
|
||||
"remove": "",
|
||||
|
|
|
@ -7,7 +7,7 @@ import ChangeEntry from './entries/change-entry'
|
|||
import AggregateChangeEntry from './entries/aggregate-change-entry'
|
||||
import CommentEntry from './entries/comment-entry'
|
||||
import AddCommentEntry from './entries/add-comment-entry'
|
||||
import BulkActionsEntry from './entries/bulk-actions-entry'
|
||||
import BulkActionsEntry from './entries/bulk-actions-entry/bulk-actions-entry'
|
||||
import {
|
||||
useReviewPanelUpdaterFnsContext,
|
||||
useReviewPanelValueContext,
|
||||
|
@ -24,6 +24,7 @@ function CurrentFileContainer() {
|
|||
permissions,
|
||||
loadingThreads,
|
||||
users,
|
||||
nVisibleSelectedChanges: nChanges,
|
||||
toggleReviewPanel,
|
||||
} = useReviewPanelValueContext()
|
||||
const { setEntryHover } = useReviewPanelUpdaterFnsContext()
|
||||
|
@ -114,7 +115,13 @@ function CurrentFileContainer() {
|
|||
}
|
||||
|
||||
if (entry.type === 'bulk-actions') {
|
||||
return <BulkActionsEntry key={id} />
|
||||
return (
|
||||
<BulkActionsEntry
|
||||
key={id}
|
||||
entry={entry}
|
||||
nChanges={nChanges}
|
||||
/>
|
||||
)
|
||||
}
|
||||
|
||||
return null
|
||||
|
|
|
@ -1,7 +0,0 @@
|
|||
import EntryContainer from './entry-container'
|
||||
|
||||
function BulkActionsEntry() {
|
||||
return <EntryContainer>Bulk actions entry</EntryContainer>
|
||||
}
|
||||
|
||||
export default BulkActionsEntry
|
|
@ -0,0 +1,66 @@
|
|||
import { useTranslation } from 'react-i18next'
|
||||
import EntryContainer from '../entry-container'
|
||||
import EntryCallout from '../entry-callout'
|
||||
import Icon from '../../../../../../shared/components/icon'
|
||||
import BulkActions from './bulk-actions'
|
||||
import Modal, { useBulkActionsModal } from './modal'
|
||||
import { ReviewPanelBulkActionsEntry } from '../../../../../../../../types/review-panel/entry'
|
||||
|
||||
type BulkActionsEntryProps = {
|
||||
entry: ReviewPanelBulkActionsEntry
|
||||
nChanges: number
|
||||
}
|
||||
|
||||
function BulkActionsEntry({ entry, nChanges }: BulkActionsEntryProps) {
|
||||
const { t } = useTranslation()
|
||||
const {
|
||||
show,
|
||||
setShow,
|
||||
isAccept,
|
||||
handleShowBulkAcceptDialog,
|
||||
handleShowBulkRejectDialog,
|
||||
handleConfirmDialog,
|
||||
} = useBulkActionsModal()
|
||||
|
||||
return (
|
||||
<>
|
||||
<EntryContainer>
|
||||
{nChanges > 1 && (
|
||||
<>
|
||||
<EntryCallout
|
||||
className="rp-entry-callout-bulk-actions"
|
||||
style={{
|
||||
top: entry.screenPos
|
||||
? entry.screenPos.y + entry.screenPos.height - 1 + 'px'
|
||||
: undefined,
|
||||
}}
|
||||
/>
|
||||
<BulkActions
|
||||
className="rp-entry"
|
||||
style={{
|
||||
top: entry.screenPos.y + 'px',
|
||||
visibility: entry.visible ? 'visible' : 'hidden',
|
||||
}}
|
||||
>
|
||||
<BulkActions.Button onClick={handleShowBulkRejectDialog}>
|
||||
<Icon type="times" /> {t('reject_all')} ({nChanges})
|
||||
</BulkActions.Button>
|
||||
<BulkActions.Button onClick={handleShowBulkAcceptDialog}>
|
||||
<Icon type="check" /> {t('accept_all')} ({nChanges})
|
||||
</BulkActions.Button>
|
||||
</BulkActions>
|
||||
</>
|
||||
)}
|
||||
</EntryContainer>
|
||||
<Modal
|
||||
show={show}
|
||||
setShow={setShow}
|
||||
isAccept={isAccept}
|
||||
nChanges={nChanges}
|
||||
onConfirm={handleConfirmDialog}
|
||||
/>
|
||||
</>
|
||||
)
|
||||
}
|
||||
|
||||
export default BulkActionsEntry
|
|
@ -0,0 +1,24 @@
|
|||
import classnames from 'classnames'
|
||||
|
||||
function BulkActions({
|
||||
className,
|
||||
...rest
|
||||
}: React.ComponentPropsWithoutRef<'div'>) {
|
||||
return (
|
||||
<div className={classnames('rp-entry-bulk-actions', className)} {...rest} />
|
||||
)
|
||||
}
|
||||
|
||||
BulkActions.Button = function BulkActionsButton({
|
||||
className,
|
||||
...rest
|
||||
}: React.ComponentPropsWithoutRef<'button'>) {
|
||||
return (
|
||||
<button
|
||||
className={classnames('rp-bulk-actions-btn', className)}
|
||||
{...rest}
|
||||
/>
|
||||
)
|
||||
}
|
||||
|
||||
export default BulkActions
|
|
@ -0,0 +1,87 @@
|
|||
import { useState, useCallback } from 'react'
|
||||
import { useTranslation } from 'react-i18next'
|
||||
import { Button, Modal as BootstrapModal } from 'react-bootstrap'
|
||||
import AccessibleModal from '../../../../../../shared/components/accessible-modal'
|
||||
import { useReviewPanelValueContext } from '../../../../context/review-panel/review-panel-context'
|
||||
|
||||
type BulkActionsModalProps = {
|
||||
show: boolean
|
||||
setShow: React.Dispatch<React.SetStateAction<boolean>>
|
||||
isAccept: boolean
|
||||
nChanges: number
|
||||
onConfirm: () => void
|
||||
}
|
||||
|
||||
function Modal({
|
||||
show,
|
||||
setShow,
|
||||
isAccept,
|
||||
nChanges,
|
||||
onConfirm,
|
||||
}: BulkActionsModalProps) {
|
||||
const { t } = useTranslation()
|
||||
|
||||
return (
|
||||
<AccessibleModal show={show} onHide={() => setShow(false)}>
|
||||
<BootstrapModal.Header closeButton>
|
||||
<h3>{isAccept ? t('accept_all') : t('reject_all')}</h3>
|
||||
</BootstrapModal.Header>
|
||||
<BootstrapModal.Body>
|
||||
<p>
|
||||
{isAccept
|
||||
? t('bulk_accept_confirm', { nChanges })
|
||||
: t('bulk_reject_confirm', { nChanges })}
|
||||
</p>
|
||||
</BootstrapModal.Body>
|
||||
<BootstrapModal.Footer>
|
||||
<Button
|
||||
bsStyle={null}
|
||||
className="btn-secondary"
|
||||
onClick={() => setShow(false)}
|
||||
>
|
||||
{t('cancel')}
|
||||
</Button>
|
||||
<Button bsStyle={null} className="btn-primary" onClick={onConfirm}>
|
||||
{t('ok')}
|
||||
</Button>
|
||||
</BootstrapModal.Footer>
|
||||
</AccessibleModal>
|
||||
)
|
||||
}
|
||||
|
||||
export function useBulkActionsModal() {
|
||||
const [show, setShow] = useState(false)
|
||||
const [isAccept, setIsAccept] = useState(false)
|
||||
const { bulkAcceptActions, bulkRejectActions } = useReviewPanelValueContext()
|
||||
|
||||
const handleShowBulkAcceptDialog = useCallback(() => {
|
||||
setIsAccept(true)
|
||||
setShow(true)
|
||||
}, [])
|
||||
|
||||
const handleShowBulkRejectDialog = useCallback(() => {
|
||||
setIsAccept(false)
|
||||
setShow(true)
|
||||
}, [])
|
||||
|
||||
const handleConfirmDialog = useCallback(() => {
|
||||
if (isAccept) {
|
||||
bulkAcceptActions()
|
||||
} else {
|
||||
bulkRejectActions()
|
||||
}
|
||||
|
||||
setShow(false)
|
||||
}, [bulkAcceptActions, bulkRejectActions, isAccept])
|
||||
|
||||
return {
|
||||
show,
|
||||
setShow,
|
||||
isAccept,
|
||||
handleShowBulkAcceptDialog,
|
||||
handleShowBulkRejectDialog,
|
||||
handleConfirmDialog,
|
||||
}
|
||||
}
|
||||
|
||||
export default Modal
|
|
@ -16,6 +16,9 @@ function useAngularReviewPanelState(): ReviewPanelState {
|
|||
const [loading] = useScopeValue<ReviewPanel.Value<'loading'>>(
|
||||
'reviewPanel.overview.loading'
|
||||
)
|
||||
const [nVisibleSelectedChanges] = useScopeValue<
|
||||
ReviewPanel.Value<'nVisibleSelectedChanges'>
|
||||
>('reviewPanel.nVisibleSelectedChanges')
|
||||
const [collapsed, setCollapsed] = useScopeValue<
|
||||
ReviewPanel.Value<'collapsed'>
|
||||
>('reviewPanel.overview.docsCollapsedState')
|
||||
|
@ -97,6 +100,10 @@ function useAngularReviewPanelState(): ReviewPanelState {
|
|||
useScopeValue<ReviewPanel.Value<'acceptChanges'>>('acceptChanges')
|
||||
const [rejectChanges] =
|
||||
useScopeValue<ReviewPanel.Value<'rejectChanges'>>('rejectChanges')
|
||||
const [bulkAcceptActions] =
|
||||
useScopeValue<ReviewPanel.Value<'bulkAcceptActions'>>('bulkAcceptActions')
|
||||
const [bulkRejectActions] =
|
||||
useScopeValue<ReviewPanel.Value<'bulkRejectActions'>>('bulkRejectActions')
|
||||
|
||||
const handleSetSubview = useCallback(
|
||||
(subView: SubView) => {
|
||||
|
@ -132,6 +139,7 @@ function useAngularReviewPanelState(): ReviewPanelState {
|
|||
gotoEntry,
|
||||
handleLayoutChange,
|
||||
loadingThreads,
|
||||
nVisibleSelectedChanges,
|
||||
permissions,
|
||||
users,
|
||||
resolveComment,
|
||||
|
@ -152,6 +160,8 @@ function useAngularReviewPanelState(): ReviewPanelState {
|
|||
trackChangesForGuestsAvailable,
|
||||
formattedProjectMembers,
|
||||
toggleReviewPanel,
|
||||
bulkAcceptActions,
|
||||
bulkRejectActions,
|
||||
unresolveComment,
|
||||
deleteThread,
|
||||
refreshResolvedCommentsDropdown,
|
||||
|
@ -169,6 +179,7 @@ function useAngularReviewPanelState(): ReviewPanelState {
|
|||
gotoEntry,
|
||||
handleLayoutChange,
|
||||
loadingThreads,
|
||||
nVisibleSelectedChanges,
|
||||
permissions,
|
||||
users,
|
||||
resolveComment,
|
||||
|
@ -189,6 +200,8 @@ function useAngularReviewPanelState(): ReviewPanelState {
|
|||
trackChangesForGuestsAvailable,
|
||||
formattedProjectMembers,
|
||||
toggleReviewPanel,
|
||||
bulkAcceptActions,
|
||||
bulkRejectActions,
|
||||
unresolveComment,
|
||||
deleteThread,
|
||||
refreshResolvedCommentsDropdown,
|
||||
|
|
|
@ -24,6 +24,7 @@ export interface ReviewPanelState {
|
|||
gotoEntry: (docId: DocId, entryOffset: number) => void
|
||||
handleLayoutChange: () => void
|
||||
loadingThreads: boolean
|
||||
nVisibleSelectedChanges: number
|
||||
permissions: ReviewPanelPermissions
|
||||
users: ReviewPanelUsers
|
||||
resolveComment: (docId: DocId, entryId: ThreadId) => void
|
||||
|
@ -54,6 +55,8 @@ export interface ReviewPanelState {
|
|||
}
|
||||
>
|
||||
toggleReviewPanel: () => void
|
||||
bulkAcceptActions: () => void
|
||||
bulkRejectActions: () => void
|
||||
unresolveComment: (threadId: ThreadId) => void
|
||||
deleteThread: (_entryId: unknown, docId: DocId, threadId: ThreadId) => void
|
||||
refreshResolvedCommentsDropdown: () => Promise<void>
|
||||
|
|
|
@ -688,7 +688,7 @@ export default App.controller(
|
|||
dispatchReviewPanelEvent('changes:reject', change_ids)
|
||||
}
|
||||
|
||||
const bulkAccept = function () {
|
||||
ide.$scope.bulkAcceptActions = function () {
|
||||
_doAcceptChanges(ide.$scope.reviewPanel.selectedEntryIds.slice())
|
||||
eventTracking.sendMB('rp-bulk-accept', {
|
||||
view: $scope.ui.reviewPanelOpen
|
||||
|
@ -698,7 +698,7 @@ export default App.controller(
|
|||
})
|
||||
}
|
||||
|
||||
const bulkReject = function () {
|
||||
ide.$scope.bulkRejectActions = function () {
|
||||
_doRejectChanges(ide.$scope.reviewPanel.selectedEntryIds.slice())
|
||||
eventTracking.sendMB('rp-bulk-reject', {
|
||||
view: $scope.ui.reviewPanelOpen
|
||||
|
@ -729,9 +729,9 @@ export default App.controller(
|
|||
})
|
||||
.result.then(function (isAccept) {
|
||||
if (isAccept) {
|
||||
return bulkAccept()
|
||||
return ide.$scope.bulkAcceptActions()
|
||||
} else {
|
||||
return bulkReject()
|
||||
return ide.$scope.bulkRejectActions()
|
||||
}
|
||||
})
|
||||
|
||||
|
|
|
@ -232,6 +232,17 @@ describe('<ReviewPanel />', function () {
|
|||
it.skip('adds comment', function () {})
|
||||
})
|
||||
|
||||
describe('bulk actions entry', function () {
|
||||
// eslint-disable-next-line mocha/no-skipped-tests
|
||||
it.skip('renders the reject and accept all buttons`', function () {})
|
||||
|
||||
// eslint-disable-next-line mocha/no-skipped-tests
|
||||
it.skip('accepts all changes', function () {})
|
||||
|
||||
// eslint-disable-next-line mocha/no-skipped-tests
|
||||
it.skip('rejects all changes', function () {})
|
||||
})
|
||||
|
||||
describe('overview mode', function () {
|
||||
// eslint-disable-next-line mocha/no-skipped-tests
|
||||
it.skip('shows list of files changed', function () {})
|
||||
|
|
|
@ -57,8 +57,9 @@ export interface ReviewPanelAddCommentEntry extends ReviewPanelBaseEntry {
|
|||
type: 'add-comment'
|
||||
}
|
||||
|
||||
interface ReviewPanelBulkActionsEntry extends ReviewPanelBaseEntry {
|
||||
export interface ReviewPanelBulkActionsEntry extends ReviewPanelBaseEntry {
|
||||
type: 'bulk-actions'
|
||||
length: number
|
||||
}
|
||||
|
||||
export type ReviewPanelEntry =
|
||||
|
|
Loading…
Reference in a new issue