mirror of
https://github.com/yu-i-i/overleaf-cep.git
synced 2026-06-09 00:59:02 +02:00
History UI changes- Adding enums for history versions
GitOrigin-RevId: 63f3a324889ad10e4a96994fa61f04bea4333630
This commit is contained in:
+5
-1
@@ -99,7 +99,11 @@ function AllHistoryList() {
|
||||
const faded =
|
||||
updatesInfo.freeHistoryLimitHit &&
|
||||
index === visibleUpdates.length - 1
|
||||
const selectable = !faded && (selection.comparing || !selected)
|
||||
const selectable =
|
||||
!faded &&
|
||||
(selection.comparing ||
|
||||
selected === 'aboveSelected' ||
|
||||
selected === 'belowSelected')
|
||||
|
||||
return (
|
||||
<HistoryVersion
|
||||
|
||||
+7
-55
@@ -2,12 +2,12 @@ import { useTranslation } from 'react-i18next'
|
||||
import { useHistoryContext } from '../../../../context/history-context'
|
||||
import { UpdateRange } from '../../../../services/types/update'
|
||||
import Compare from './compare'
|
||||
import { updateRangeUnion } from '../../../../utils/range'
|
||||
import MaterialIcon from '../../../../../../shared/components/material-icon'
|
||||
import { ItemSelectionState } from '../../../../utils/history-details'
|
||||
|
||||
type CompareItemsProps = {
|
||||
updateRange: UpdateRange
|
||||
selected: boolean
|
||||
selected: ItemSelectionState
|
||||
closeDropdown: () => void
|
||||
}
|
||||
|
||||
@@ -18,62 +18,14 @@ function CompareItems({
|
||||
}: CompareItemsProps) {
|
||||
const { t } = useTranslation()
|
||||
const { selection } = useHistoryContext()
|
||||
const { updateRange: selRange, comparing } = selection
|
||||
|
||||
// Comparing mode variables
|
||||
const notASelectionBoundaryComparingMode =
|
||||
!!selRange &&
|
||||
comparing &&
|
||||
updateRange.toV !== selRange.toV &&
|
||||
updateRange.fromV !== selRange.fromV
|
||||
const showCompareWithSelected = !comparing && !!selRange && !selected
|
||||
const showCompareToThisComparingMode =
|
||||
notASelectionBoundaryComparingMode && updateRange.toV > selRange.toV
|
||||
const showCompareFromThisComparingMode =
|
||||
notASelectionBoundaryComparingMode && updateRange.fromV < selRange.fromV
|
||||
|
||||
// Normal mode variables
|
||||
const notASelectionBoundaryNormalMode =
|
||||
!!selRange &&
|
||||
updateRange.toV !== selRange.toV &&
|
||||
updateRange.fromV !== selRange.fromV
|
||||
const showCompareToThisNormalMode =
|
||||
notASelectionBoundaryNormalMode && updateRange.toV > selRange.toV
|
||||
const showCompareFromThisNormalMode =
|
||||
notASelectionBoundaryNormalMode && updateRange.fromV < selRange.fromV
|
||||
|
||||
let iconTypeNonSelectedVersion = ''
|
||||
let toolTipDescriptionNonSelectedVersion = ''
|
||||
|
||||
if (showCompareToThisNormalMode) {
|
||||
iconTypeNonSelectedVersion = 'align_start'
|
||||
toolTipDescriptionNonSelectedVersion = t(
|
||||
'history_compare_up_to_this_version'
|
||||
)
|
||||
}
|
||||
if (showCompareFromThisNormalMode) {
|
||||
iconTypeNonSelectedVersion = 'align_end'
|
||||
toolTipDescriptionNonSelectedVersion = t(
|
||||
'history_compare_from_this_version'
|
||||
)
|
||||
const { updateRange: selRange } = selection
|
||||
if (selRange === null) {
|
||||
return null
|
||||
}
|
||||
|
||||
return (
|
||||
<>
|
||||
{showCompareWithSelected ? (
|
||||
<Compare
|
||||
comparisonRange={updateRangeUnion(updateRange, selRange)}
|
||||
closeDropdown={closeDropdown}
|
||||
toolTipDescription={toolTipDescriptionNonSelectedVersion}
|
||||
icon={
|
||||
<MaterialIcon
|
||||
type={iconTypeNonSelectedVersion}
|
||||
className="material-symbols-rounded history-dropdown-icon p-1"
|
||||
/>
|
||||
}
|
||||
/>
|
||||
) : null}
|
||||
{showCompareFromThisComparingMode ? (
|
||||
{selected === 'belowSelected' ? (
|
||||
<Compare
|
||||
comparisonRange={{
|
||||
fromV: updateRange.fromV,
|
||||
@@ -91,7 +43,7 @@ function CompareItems({
|
||||
}
|
||||
/>
|
||||
) : null}
|
||||
{showCompareToThisComparingMode ? (
|
||||
{selected === 'aboveSelected' ? (
|
||||
<Compare
|
||||
comparisonRange={{
|
||||
fromV: selRange.fromV,
|
||||
|
||||
+5
-2
@@ -2,11 +2,12 @@ import classnames from 'classnames'
|
||||
import { HistoryContextValue } from '../../context/types/history-context-value'
|
||||
import { UpdateRange } from '../../services/types/update'
|
||||
import { ReactNode, MouseEvent } from 'react'
|
||||
import { ItemSelectionState } from '../../utils/history-details'
|
||||
|
||||
type HistoryVersionDetailsProps = {
|
||||
children: ReactNode
|
||||
updateRange: UpdateRange
|
||||
selected: boolean
|
||||
selected: ItemSelectionState
|
||||
selectable: boolean
|
||||
setSelection: HistoryContextValue['setSelection']
|
||||
}
|
||||
@@ -35,7 +36,9 @@ function HistoryVersionDetails({
|
||||
// eslint-disable-next-line jsx-a11y/click-events-have-key-events,jsx-a11y/no-static-element-interactions
|
||||
<div
|
||||
className={classnames('history-version-details clearfix', {
|
||||
'history-version-selected': selected,
|
||||
'history-version-selected':
|
||||
selected === 'selectedEdge' || selected === 'selected',
|
||||
'history-version-within-selected': selected === 'withinSelected',
|
||||
'history-version-selectable': selectable,
|
||||
})}
|
||||
data-testid="history-version-details"
|
||||
|
||||
+34
-12
@@ -8,7 +8,10 @@ import { formatTime, relativeDate } from '../../../utils/format-date'
|
||||
import { orderBy } from 'lodash'
|
||||
import { LoadedUpdate } from '../../services/types/update'
|
||||
import classNames from 'classnames'
|
||||
import { updateRangeForUpdate } from '../../utils/history-details'
|
||||
import {
|
||||
updateRangeForUpdate,
|
||||
ItemSelectionState,
|
||||
} from '../../utils/history-details'
|
||||
import { ActiveDropdown } from '../../hooks/use-dropdown-active-item'
|
||||
import { memo, useCallback } from 'react'
|
||||
import { HistoryContextValue } from '../../context/types/history-context-value'
|
||||
@@ -22,7 +25,7 @@ type HistoryVersionProps = {
|
||||
selectable: boolean
|
||||
faded: boolean
|
||||
showDivider: boolean
|
||||
selected: boolean
|
||||
selected: ItemSelectionState
|
||||
setSelection: HistoryContextValue['setSelection']
|
||||
dropdownOpen: boolean
|
||||
dropdownActive: boolean
|
||||
@@ -54,11 +57,28 @@ function HistoryVersion({
|
||||
|
||||
return (
|
||||
<>
|
||||
{showDivider ? <hr className="history-version-divider" /> : null}
|
||||
{showDivider ? (
|
||||
<div
|
||||
className={classNames({
|
||||
'history-version-divider-container': true,
|
||||
'version-element-within-selected ':
|
||||
selected === 'withinSelected' || selected === 'selectedEdge',
|
||||
})}
|
||||
>
|
||||
<hr className="history-version-divider" />
|
||||
</div>
|
||||
) : null}
|
||||
{update.meta.first_in_day ? (
|
||||
<time className="history-version-day">
|
||||
{relativeDate(update.meta.end_ts)}
|
||||
</time>
|
||||
<div
|
||||
className={classNames({
|
||||
'version-element-within-selected ':
|
||||
selected === 'withinSelected' || selected === 'selectedEdge',
|
||||
})}
|
||||
>
|
||||
<time className="history-version-day">
|
||||
{relativeDate(update.meta.end_ts)}
|
||||
</time>
|
||||
</div>
|
||||
) : null}
|
||||
<div
|
||||
data-testid="history-version"
|
||||
@@ -90,13 +110,15 @@ function HistoryVersion({
|
||||
</HistoryDropdown>
|
||||
)}
|
||||
|
||||
{!selected ? (
|
||||
{selected !== 'selected' ? (
|
||||
<span data-testid="compare-icon-version" className="pull-right">
|
||||
<CompareItems
|
||||
updateRange={updateRange}
|
||||
selected={selected}
|
||||
closeDropdown={closeDropdown}
|
||||
/>
|
||||
{selected !== 'withinSelected' ? (
|
||||
<CompareItems
|
||||
updateRange={updateRange}
|
||||
selected={selected}
|
||||
closeDropdown={closeDropdown}
|
||||
/>
|
||||
) : null}
|
||||
</span>
|
||||
) : null}
|
||||
|
||||
|
||||
+2
-1
@@ -12,13 +12,14 @@ import { ActiveDropdown } from '../../hooks/use-dropdown-active-item'
|
||||
import { HistoryContextValue } from '../../context/types/history-context-value'
|
||||
import LabelDropdownContent from './dropdown/label-dropdown-content'
|
||||
import CompareItems from './dropdown/menu-item/compare-items'
|
||||
import { ItemSelectionState } from '../../utils/history-details'
|
||||
|
||||
type LabelListItemProps = {
|
||||
version: Version
|
||||
labels: LoadedLabel[]
|
||||
currentUserId: string
|
||||
projectId: string
|
||||
selected: boolean
|
||||
selected: ItemSelectionState
|
||||
selectable: boolean
|
||||
setSelection: HistoryContextValue['setSelection']
|
||||
dropdownOpen: boolean
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
import { useUserContext } from '../../../../shared/context/user-context'
|
||||
import { isVersionSelected } from '../../utils/history-details'
|
||||
import { useMemo } from 'react'
|
||||
import { Version } from '../../services/types/update'
|
||||
import LabelListItem from './label-list-item'
|
||||
import useDropdownActiveItem from '../../hooks/use-dropdown-active-item'
|
||||
import { getVersionWithLabels } from '../../utils/label'
|
||||
@@ -18,18 +17,10 @@ function LabelsList() {
|
||||
[labels]
|
||||
)
|
||||
|
||||
const selectedVersions = new Set<Version>(
|
||||
Array.from(versionWithLabels.values(), value => value.version).filter(
|
||||
version => isVersionSelected(selection, version)
|
||||
)
|
||||
)
|
||||
|
||||
const singleVersionSelected = selectedVersions.size === 1
|
||||
|
||||
return (
|
||||
<>
|
||||
{versionWithLabels.map(({ version, labels }) => {
|
||||
const selected = selectedVersions.has(version)
|
||||
const selected = isVersionSelected(selection, version)
|
||||
const dropdownActive = version === activeDropdownItem.item
|
||||
|
||||
return (
|
||||
@@ -40,7 +31,7 @@ function LabelsList() {
|
||||
currentUserId={currentUserId}
|
||||
projectId={projectId}
|
||||
selected={selected}
|
||||
selectable={!(singleVersionSelected && selected)}
|
||||
selectable={selected !== 'selected'}
|
||||
setSelection={setSelection}
|
||||
dropdownOpen={activeDropdownItem.isOpened && dropdownActive}
|
||||
dropdownActive={dropdownActive}
|
||||
|
||||
@@ -37,36 +37,78 @@ export const getProjectOpDoc = (projectOp: ProjectOp) => {
|
||||
return ''
|
||||
}
|
||||
|
||||
export type ItemSelectionState =
|
||||
| 'selectedEdge'
|
||||
| 'withinSelected'
|
||||
| 'aboveSelected'
|
||||
| 'belowSelected'
|
||||
| 'selected'
|
||||
| null
|
||||
|
||||
export function isVersionSelected(
|
||||
selection: Selection,
|
||||
version: Version
|
||||
): boolean
|
||||
): ItemSelectionState
|
||||
// eslint-disable-next-line no-redeclare
|
||||
export function isVersionSelected(
|
||||
selection: Selection,
|
||||
fromV: Version,
|
||||
toV: Version
|
||||
): boolean
|
||||
): ItemSelectionState
|
||||
// eslint-disable-next-line no-redeclare
|
||||
export function isVersionSelected(
|
||||
selection: Selection,
|
||||
...args: [Version] | [Version, Version]
|
||||
): boolean {
|
||||
): ItemSelectionState {
|
||||
if (selection.updateRange) {
|
||||
let [fromV, toV] = args
|
||||
toV = toV ?? fromV
|
||||
|
||||
if (selection.comparing) {
|
||||
// compare mode
|
||||
return (
|
||||
fromV >= selection.updateRange.fromV && toV <= selection.updateRange.toV
|
||||
)
|
||||
} else {
|
||||
if (
|
||||
fromV > selection.updateRange.fromV &&
|
||||
toV < selection.updateRange.toV
|
||||
) {
|
||||
return 'withinSelected'
|
||||
}
|
||||
|
||||
// Condition for selectedEdge when the comparing versions are from labels list
|
||||
if (fromV === toV) {
|
||||
if (
|
||||
toV === selection.updateRange.fromV ||
|
||||
fromV === selection.updateRange.toV
|
||||
) {
|
||||
return 'selectedEdge'
|
||||
}
|
||||
}
|
||||
|
||||
// Comparing mode above selected condition
|
||||
if (fromV >= selection.updateRange.toV) {
|
||||
return 'aboveSelected'
|
||||
}
|
||||
// Comparing mode below selected condition
|
||||
if (toV <= selection.updateRange.fromV) {
|
||||
return 'belowSelected'
|
||||
}
|
||||
|
||||
if (
|
||||
fromV === selection.updateRange.fromV ||
|
||||
toV === selection.updateRange.toV
|
||||
) {
|
||||
return 'selectedEdge'
|
||||
}
|
||||
} else if (toV === selection.updateRange.toV) {
|
||||
// single version mode
|
||||
return toV === selection.updateRange.toV
|
||||
return 'selected'
|
||||
} else if (fromV >= selection.updateRange.toV) {
|
||||
// Non-Comparing mode above selected condition
|
||||
return 'aboveSelected'
|
||||
} else if (toV <= selection.updateRange.fromV) {
|
||||
// Non-Comparing mode below selected condition
|
||||
return 'belowSelected'
|
||||
}
|
||||
}
|
||||
return false
|
||||
|
||||
return null
|
||||
}
|
||||
|
||||
export const getUpdateForVersion = (version: number, updates: LoadedUpdate[]) =>
|
||||
|
||||
@@ -90,7 +90,6 @@ history-root {
|
||||
padding-top: 12px;
|
||||
padding-bottom: 4px;
|
||||
line-height: 20px;
|
||||
background-color: @white;
|
||||
}
|
||||
|
||||
.history-version-details {
|
||||
@@ -106,10 +105,34 @@ history-root {
|
||||
}
|
||||
}
|
||||
|
||||
&.history-version-selected,
|
||||
&.history-version-selected.history-version-selectable:hover {
|
||||
&.history-version-selected {
|
||||
background-color: @green-10;
|
||||
border-left: 3px solid @green-50;
|
||||
}
|
||||
|
||||
&.history-version-selected.history-version-selectable:hover {
|
||||
background-color: fade(@green-70, 16%);
|
||||
border-left: 3px solid @green-50;
|
||||
}
|
||||
|
||||
&.history-version-within-selected {
|
||||
background-color: @neutral-10;
|
||||
border-left: 3px solid @green-50;
|
||||
}
|
||||
|
||||
&.history-version-within-selected:hover {
|
||||
background-color: fade(@neutral-90, 8%);
|
||||
}
|
||||
}
|
||||
|
||||
.version-element-within-selected {
|
||||
background-color: @neutral-10;
|
||||
border-left: 3px solid @green-50;
|
||||
}
|
||||
|
||||
.version-element-selected {
|
||||
background-color: @green-10;
|
||||
border-left: 3px solid @green-50;
|
||||
}
|
||||
|
||||
.history-version-metadata-time {
|
||||
@@ -176,8 +199,12 @@ history-root {
|
||||
overflow-wrap: anywhere;
|
||||
}
|
||||
|
||||
.history-version-divider-container {
|
||||
padding: 6px 8px;
|
||||
}
|
||||
|
||||
.history-version-divider {
|
||||
margin: 6px 8px;
|
||||
margin: 0px;
|
||||
border-color: @neutral-20;
|
||||
}
|
||||
|
||||
@@ -384,6 +411,11 @@ history-root {
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
.history-compare-btn {
|
||||
line-height: 1;
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
.history-file-tree {
|
||||
display: flex !important; // To work around jQuery layout's inline styles
|
||||
flex-direction: column;
|
||||
|
||||
@@ -44,6 +44,8 @@
|
||||
@blueDark: #040d2d;
|
||||
@green: #46a546;
|
||||
@green-10: #ebf6ea;
|
||||
@green-50: #138a07;
|
||||
@green-70: #1f5919;
|
||||
@green-30: #8cca86;
|
||||
@red: #a93529;
|
||||
@yellow: #a1a729;
|
||||
|
||||
@@ -339,9 +339,14 @@ describe('change list', function () {
|
||||
cy.findByLabelText(/all history/i).click({ force: true })
|
||||
cy.findAllByTestId('history-version-details').should($versions => {
|
||||
const [first, ...rest] = Array.from($versions)
|
||||
expect(first.dataset.selected === 'true').to.be.true
|
||||
expect(rest.every(version => version.dataset.selected === 'false')).to
|
||||
.be.true
|
||||
expect(first.dataset.selected === 'selected').to.be.true
|
||||
expect(
|
||||
rest.every(
|
||||
version =>
|
||||
version.dataset.selected === 'belowSelected' ||
|
||||
version.dataset.selected === 'aboveSelected'
|
||||
)
|
||||
).to.be.true
|
||||
})
|
||||
})
|
||||
})
|
||||
@@ -361,9 +366,11 @@ describe('change list', function () {
|
||||
it('compares versions', function () {
|
||||
cy.findAllByTestId('history-version-details').should($versions => {
|
||||
const [first, ...rest] = Array.from($versions)
|
||||
expect(first).to.have.attr('data-selected', 'true')
|
||||
expect(first).to.have.attr('data-selected', 'selected')
|
||||
rest.forEach(version =>
|
||||
expect(version).to.have.attr('data-selected', 'false')
|
||||
// Based on the fact that we are selecting first version as we load the page
|
||||
// Every other version will be belowSelected
|
||||
expect(version).to.have.attr('data-selected', 'belowSelected')
|
||||
)
|
||||
})
|
||||
|
||||
|
||||
Reference in New Issue
Block a user