From c06ba692239335299727fdf8d039cb79701daea8 Mon Sep 17 00:00:00 2001 From: Davinder Singh Date: Wed, 23 Aug 2023 10:14:08 +0100 Subject: [PATCH] History ui - Adding the compare dropdown on the withinSelected versions GitOrigin-RevId: 728979b11918c9bc1e535024040d90053536251d --- .../web/frontend/extracted-translations.json | 1 + .../change-list/all-history-list.tsx | 11 +- .../change-list/dropdown/actions-dropdown.tsx | 13 +- .../compare-version-dropdown-content.tsx | 112 ++++++++++++++++++ .../dropdown/compare-version-dropdown.tsx | 39 ++++++ .../change-list/dropdown/history-dropdown.tsx | 7 ++ .../dropdown/label-dropdown-content.tsx | 2 +- .../dropdown/menu-item/compare-items.tsx | 4 + .../dropdown/menu-item/compare.tsx | 3 + .../dropdown/version-dropdown-content.tsx | 2 +- .../change-list/history-version.tsx | 39 +++++- .../change-list/label-list-item.tsx | 58 ++++++--- .../components/change-list/labels-list.tsx | 11 +- .../hooks/use-dropdown-active-item.tsx | 11 +- .../stylesheets/app/editor/history-react.less | 11 +- services/web/locales/en.json | 1 + .../history/components/change-list.spec.tsx | 20 ++++ 17 files changed, 310 insertions(+), 35 deletions(-) create mode 100644 services/web/frontend/js/features/history/components/change-list/dropdown/compare-version-dropdown-content.tsx create mode 100644 services/web/frontend/js/features/history/components/change-list/dropdown/compare-version-dropdown.tsx diff --git a/services/web/frontend/extracted-translations.json b/services/web/frontend/extracted-translations.json index 6a91815ce1..dacbdd8ef1 100644 --- a/services/web/frontend/extracted-translations.json +++ b/services/web/frontend/extracted-translations.json @@ -159,6 +159,7 @@ "commons_plan_tooltip": "", "compact": "", "company_name": "", + "compare": "", "comparing_from_x_to_y": "", "compile_error_entry_description": "", "compile_error_handling": "", diff --git a/services/web/frontend/js/features/history/components/change-list/all-history-list.tsx b/services/web/frontend/js/features/history/components/change-list/all-history-list.tsx index 547f529448..e1c7d576b0 100644 --- a/services/web/frontend/js/features/history/components/change-list/all-history-list.tsx +++ b/services/web/frontend/js/features/history/components/change-list/all-history-list.tsx @@ -94,7 +94,12 @@ function AllHistoryList() { update.fromV, update.toV ) - const dropdownActive = update === activeDropdownItem.item + const dropdownActive = + update === activeDropdownItem.item && + activeDropdownItem.whichDropDown === 'moreOptions' + const compareDropdownActive = + update === activeDropdownItem.item && + activeDropdownItem.whichDropDown === 'compare' const showDivider = Boolean(update.meta.first_in_day && index > 0) const faded = updatesInfo.freeHistoryLimitHit && @@ -119,6 +124,10 @@ function AllHistoryList() { setActiveDropdownItem={setActiveDropdownItem} closeDropdownForItem={closeDropdownForItem} dropdownOpen={activeDropdownItem.isOpened && dropdownActive} + compareDropdownActive={compareDropdownActive} + compareDropdownOpen={ + activeDropdownItem.isOpened && compareDropdownActive + } dropdownActive={dropdownActive} /> ) diff --git a/services/web/frontend/js/features/history/components/change-list/dropdown/actions-dropdown.tsx b/services/web/frontend/js/features/history/components/change-list/dropdown/actions-dropdown.tsx index 95f07d6677..b7480eae77 100644 --- a/services/web/frontend/js/features/history/components/change-list/dropdown/actions-dropdown.tsx +++ b/services/web/frontend/js/features/history/components/change-list/dropdown/actions-dropdown.tsx @@ -1,8 +1,6 @@ -import { useRef, useEffect } from 'react' -import { useTranslation } from 'react-i18next' +import { useRef, useEffect, ReactNode } from 'react' import { Dropdown } from 'react-bootstrap' import DropdownToggleWithTooltip from '../../../../../shared/components/dropdown/dropdown-toggle-with-tooltip' -import Icon from '../../../../../shared/components/icon' import DropdownMenuWithRef from '../../../../../shared/components/dropdown/dropdown-menu-with-ref' type DropdownMenuProps = { @@ -10,6 +8,8 @@ type DropdownMenuProps = { children: React.ReactNode parentSelector?: string isOpened: boolean + iconTag: ReactNode + toolTipDescription: string setIsOpened: (isOpened: boolean) => void } @@ -18,9 +18,10 @@ function ActionsDropdown({ children, parentSelector, isOpened, + iconTag, setIsOpened, + toolTipDescription, }: DropdownMenuProps) { - const { t } = useTranslation() const menuRef = useRef() // handle the placement of the dropdown above or below the toggle button @@ -58,11 +59,11 @@ function ActionsDropdown({ className="history-version-dropdown-menu-btn" tooltipProps={{ id, - description: t('more_actions'), + description: toolTipDescription, overlayProps: { placement: 'bottom', trigger: ['hover'] }, }} > - + {iconTag} { + closeDropdownForItem(update, 'compare') + }, [closeDropdownForItem, update]) + + const { t } = useTranslation() + + return ( + <> + + + + + + + + ) +} + +type VersionDropdownContentLabelsListProps = { + version: Version + versionTimestamp: number + closeDropdownForItem: ActiveDropdown['closeDropdownForItem'] +} + +function CompareVersionDropdownContentLabelsList({ + version, + versionTimestamp, + closeDropdownForItem, +}: VersionDropdownContentLabelsListProps) { + const closeDropdownLabels = useCallback(() => { + closeDropdownForItem(version, 'compare') + }, [closeDropdownForItem, version]) + + const { t } = useTranslation() + + return ( + <> + + + + + + + + ) +} + +type DropdownOptionProps = { + children: ReactNode +} + +function DropdownOption({ children, ...props }: DropdownOptionProps) { + return ( + <> + {children} + + ) +} + +export { + CompareVersionDropdownContentAllHistory, + CompareVersionDropdownContentLabelsList, +} diff --git a/services/web/frontend/js/features/history/components/change-list/dropdown/compare-version-dropdown.tsx b/services/web/frontend/js/features/history/components/change-list/dropdown/compare-version-dropdown.tsx new file mode 100644 index 0000000000..612976fbd5 --- /dev/null +++ b/services/web/frontend/js/features/history/components/change-list/dropdown/compare-version-dropdown.tsx @@ -0,0 +1,39 @@ +import ActionsDropdown from './actions-dropdown' +import MaterialIcon from '../../../../../shared/components/material-icon' +import { useTranslation } from 'react-i18next' + +type CompareVersionDropdownProps = { + children: React.ReactNode + id: string + isOpened: boolean + setIsOpened: (isOpened: boolean) => void +} + +function CompareVersionDropdown({ + children, + id, + isOpened, + setIsOpened, +}: CompareVersionDropdownProps) { + const { t } = useTranslation() + return ( + + } + > + {children} + + ) +} + +export default CompareVersionDropdown diff --git a/services/web/frontend/js/features/history/components/change-list/dropdown/history-dropdown.tsx b/services/web/frontend/js/features/history/components/change-list/dropdown/history-dropdown.tsx index 4636bdef56..419af3355b 100644 --- a/services/web/frontend/js/features/history/components/change-list/dropdown/history-dropdown.tsx +++ b/services/web/frontend/js/features/history/components/change-list/dropdown/history-dropdown.tsx @@ -1,4 +1,6 @@ import ActionsDropdown from './actions-dropdown' +import Icon from '../../../../../shared/components/icon' +import { useTranslation } from 'react-i18next' type HistoryDropdownProps = { children: React.ReactNode @@ -13,11 +15,16 @@ function HistoryDropdown({ isOpened, setIsOpened, }: HistoryDropdownProps) { + const { t } = useTranslation() return ( + } parentSelector="[data-history-version-list-container]" > {children} diff --git a/services/web/frontend/js/features/history/components/change-list/dropdown/label-dropdown-content.tsx b/services/web/frontend/js/features/history/components/change-list/dropdown/label-dropdown-content.tsx index f78aa0888a..8f91999594 100644 --- a/services/web/frontend/js/features/history/components/change-list/dropdown/label-dropdown-content.tsx +++ b/services/web/frontend/js/features/history/components/change-list/dropdown/label-dropdown-content.tsx @@ -15,7 +15,7 @@ function LabelDropdownContent({ closeDropdownForItem, }: LabelDropdownContentProps) { const closeDropdown = useCallback(() => { - closeDropdownForItem(version) + closeDropdownForItem(version, 'moreOptions') }, [closeDropdownForItem, version]) return ( diff --git a/services/web/frontend/js/features/history/components/change-list/dropdown/menu-item/compare-items.tsx b/services/web/frontend/js/features/history/components/change-list/dropdown/menu-item/compare-items.tsx index 1a9cfcf2be..3b49b99d45 100644 --- a/services/web/frontend/js/features/history/components/change-list/dropdown/menu-item/compare-items.tsx +++ b/services/web/frontend/js/features/history/components/change-list/dropdown/menu-item/compare-items.tsx @@ -8,12 +8,14 @@ import { ItemSelectionState } from '../../../../utils/history-details' type CompareItemsProps = { updateRange: UpdateRange selected: ItemSelectionState + text?: string closeDropdown: () => void } function CompareItems({ updateRange, selected, + text, closeDropdown, }: CompareItemsProps) { const { t } = useTranslation() @@ -35,6 +37,7 @@ function CompareItems({ }} closeDropdown={closeDropdown} toolTipDescription={t('history_compare_from_this_version')} + text={text} icon={ void } function Compare({ comparisonRange, + text, closeDropdown, toolTipDescription, icon = , @@ -46,6 +48,7 @@ function Compare({ > {toolTipDescription} {icon} + {text ?? {text}} ) diff --git a/services/web/frontend/js/features/history/components/change-list/dropdown/version-dropdown-content.tsx b/services/web/frontend/js/features/history/components/change-list/dropdown/version-dropdown-content.tsx index 3dd89c8646..33605d2199 100644 --- a/services/web/frontend/js/features/history/components/change-list/dropdown/version-dropdown-content.tsx +++ b/services/web/frontend/js/features/history/components/change-list/dropdown/version-dropdown-content.tsx @@ -16,7 +16,7 @@ function VersionDropdownContent({ closeDropdownForItem, }: VersionDropdownContentProps) { const closeDropdown = useCallback(() => { - closeDropdownForItem(update) + closeDropdownForItem(update, 'moreOptions') }, [closeDropdownForItem, update]) return ( diff --git a/services/web/frontend/js/features/history/components/change-list/history-version.tsx b/services/web/frontend/js/features/history/components/change-list/history-version.tsx index 5a8c2fb2e3..3e99b0cea6 100644 --- a/services/web/frontend/js/features/history/components/change-list/history-version.tsx +++ b/services/web/frontend/js/features/history/components/change-list/history-version.tsx @@ -17,6 +17,8 @@ import { memo, useCallback } from 'react' import { HistoryContextValue } from '../../context/types/history-context-value' import VersionDropdownContent from './dropdown/version-dropdown-content' import CompareItems from './dropdown/menu-item/compare-items' +import CompareVersionDropdown from './dropdown/compare-version-dropdown' +import { CompareVersionDropdownContentAllHistory } from './dropdown/compare-version-dropdown-content' type HistoryVersionProps = { update: LoadedUpdate @@ -29,6 +31,8 @@ type HistoryVersionProps = { setSelection: HistoryContextValue['setSelection'] dropdownOpen: boolean dropdownActive: boolean + compareDropdownOpen: boolean + compareDropdownActive: boolean setActiveDropdownItem: ActiveDropdown['setActiveDropdownItem'] closeDropdownForItem: ActiveDropdown['closeDropdownForItem'] } @@ -44,13 +48,15 @@ function HistoryVersion({ setSelection, dropdownOpen, dropdownActive, + compareDropdownOpen, + compareDropdownActive, setActiveDropdownItem, closeDropdownForItem, }: HistoryVersionProps) { const orderedLabels = orderBy(update.labels, ['created_at'], ['desc']) const closeDropdown = useCallback(() => { - closeDropdownForItem(update) + closeDropdownForItem(update, 'moreOptions') }, [closeDropdownForItem, update]) const updateRange = updateRangeForUpdate(update) @@ -97,7 +103,11 @@ function HistoryVersion({ id={`${update.fromV}_${update.toV}`} isOpened={dropdownOpen} setIsOpened={(isOpened: boolean) => - setActiveDropdownItem({ item: update, isOpened }) + setActiveDropdownItem({ + item: update, + isOpened, + whichDropDown: 'moreOptions', + }) } > {dropdownActive ? ( @@ -111,15 +121,34 @@ function HistoryVersion({ )} {selected !== 'selected' ? ( - +
{selected !== 'withinSelected' ? ( - ) : null} - + ) : ( + + setActiveDropdownItem({ + item: update, + isOpened, + whichDropDown: 'compare', + }) + } + > + {compareDropdownActive ? ( + + ) : null} + + )} +
) : null}
diff --git a/services/web/frontend/js/features/history/components/change-list/label-list-item.tsx b/services/web/frontend/js/features/history/components/change-list/label-list-item.tsx index 54de760715..27457833c4 100644 --- a/services/web/frontend/js/features/history/components/change-list/label-list-item.tsx +++ b/services/web/frontend/js/features/history/components/change-list/label-list-item.tsx @@ -13,6 +13,8 @@ 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' +import CompareVersionDropdown from './dropdown/compare-version-dropdown' +import { CompareVersionDropdownContentLabelsList } from './dropdown/compare-version-dropdown-content' type LabelListItemProps = { version: Version @@ -24,6 +26,8 @@ type LabelListItemProps = { setSelection: HistoryContextValue['setSelection'] dropdownOpen: boolean dropdownActive: boolean + compareDropdownOpen: boolean + compareDropdownActive: boolean setActiveDropdownItem: ActiveDropdown['setActiveDropdownItem'] closeDropdownForItem: ActiveDropdown['closeDropdownForItem'] } @@ -38,6 +42,8 @@ function LabelListItem({ setSelection, dropdownOpen, dropdownActive, + compareDropdownOpen, + compareDropdownActive, setActiveDropdownItem, closeDropdownForItem, }: LabelListItemProps) { @@ -57,12 +63,16 @@ function LabelListItem({ const setIsOpened = useCallback( (isOpened: boolean) => { - setActiveDropdownItem({ item: version, isOpened }) + setActiveDropdownItem({ + item: version, + isOpened, + whichDropDown: 'moreOptions', + }) }, [setActiveDropdownItem, version] ) const closeDropdown = useCallback(() => { - closeDropdownForItem(version) + closeDropdownForItem(version, 'moreOptions') }, [closeDropdownForItem, version]) return ( @@ -86,18 +96,38 @@ function LabelListItem({ /> ) : null} - - - + {selected !== 'selected' ? ( +
+ {selected !== 'withinSelected' ? ( + + ) : ( + + setActiveDropdownItem({ + item: version, + isOpened, + whichDropDown: 'compare', + }) + } + > + {compareDropdownActive ? ( + + ) : null} + + )} +
+ ) : null} +
{labels.map(label => (
diff --git a/services/web/frontend/js/features/history/components/change-list/labels-list.tsx b/services/web/frontend/js/features/history/components/change-list/labels-list.tsx index b2e5cc943b..c361628b70 100644 --- a/services/web/frontend/js/features/history/components/change-list/labels-list.tsx +++ b/services/web/frontend/js/features/history/components/change-list/labels-list.tsx @@ -21,7 +21,12 @@ function LabelsList() { <> {versionWithLabels.map(({ version, labels }) => { const selected = isVersionSelected(selection, version) - const dropdownActive = version === activeDropdownItem.item + const dropdownActive = + version === activeDropdownItem.item && + activeDropdownItem.whichDropDown === 'moreOptions' + const compareDropdownActive = + version === activeDropdownItem.item && + activeDropdownItem.whichDropDown === 'compare' return ( diff --git a/services/web/frontend/js/features/history/hooks/use-dropdown-active-item.tsx b/services/web/frontend/js/features/history/hooks/use-dropdown-active-item.tsx index 16d7c2ec2f..28a001f511 100644 --- a/services/web/frontend/js/features/history/hooks/use-dropdown-active-item.tsx +++ b/services/web/frontend/js/features/history/hooks/use-dropdown-active-item.tsx @@ -2,16 +2,21 @@ import { Dispatch, SetStateAction, useCallback, useState } from 'react' import { LoadedUpdate, Version } from '../services/types/update' type DropdownItem = LoadedUpdate | Version +type WhichDropDownType = 'moreOptions' | 'compare' | null export type ActiveDropdownValue = { item: DropdownItem | null isOpened: boolean + whichDropDown: WhichDropDownType } export type ActiveDropdown = { activeDropdownItem: ActiveDropdownValue setActiveDropdownItem: Dispatch> - closeDropdownForItem: (item: DropdownItem) => void + closeDropdownForItem: ( + item: DropdownItem, + whichDropDown: WhichDropDownType + ) => void } function useDropdownActiveItem(): ActiveDropdown { @@ -19,10 +24,12 @@ function useDropdownActiveItem(): ActiveDropdown { useState({ item: null, isOpened: false, + whichDropDown: null, }) const closeDropdownForItem = useCallback( - (item: DropdownItem) => setActiveDropdownItem({ item, isOpened: false }), + (item: DropdownItem, whichDropDown: WhichDropDownType) => + setActiveDropdownItem({ item, isOpened: false, whichDropDown }), [setActiveDropdownItem] ) diff --git a/services/web/frontend/stylesheets/app/editor/history-react.less b/services/web/frontend/stylesheets/app/editor/history-react.less index 82a8e79970..0fdf79ab37 100644 --- a/services/web/frontend/stylesheets/app/editor/history-react.less +++ b/services/web/frontend/stylesheets/app/editor/history-react.less @@ -409,11 +409,14 @@ history-root { .history-compare-btn { line-height: 1; padding: 0; + color: black; + display: flex; + justify-content: center; + align-items: center; } - -.history-compare-btn { - line-height: 1; - padding: 0; +.history-compare-btn:hover { + text-decoration: none; + color: black; } .history-file-tree { diff --git a/services/web/locales/en.json b/services/web/locales/en.json index d7fcbc5b1a..60898d8aec 100644 --- a/services/web/locales/en.json +++ b/services/web/locales/en.json @@ -258,6 +258,7 @@ "commons_plan_tooltip": "You’re on the __plan__ plan because of your affiliation with __institution__. Click to find out how to make the most of your Overleaf premium features.", "compact": "Compact", "company_name": "Company Name", + "compare": "Compare", "compare_to_another_version": "Compare to another version", "comparing_from_x_to_y": "Comparing from <0>__startTime__ to <0>__endTime__", "compile_error_entry_description": "An error which prevented this project from compiling", diff --git a/services/web/test/frontend/features/history/components/change-list.spec.tsx b/services/web/test/frontend/features/history/components/change-list.spec.tsx index aa4d829aa9..21cde521ec 100644 --- a/services/web/test/frontend/features/history/components/change-list.spec.tsx +++ b/services/web/test/frontend/features/history/components/change-list.spec.tsx @@ -349,6 +349,26 @@ describe('change list', function () { ).to.be.true }) }) + it('opens the compare drop down and compares with selected version', function () { + cy.findByLabelText(/all history/i).click({ force: true }) + cy.findAllByTestId('history-version-details') + .eq(2) + .within(() => { + cy.findByRole('button', { + name: /compare from this version/i, + }).click() + }) + cy.findByRole('button', { name: /compare drop down/i }).click() + cy.findByRole('button', { name: /compare up to this version/i }).click() + + cy.findAllByTestId('history-version-details').should($versions => { + const [first, ...rest] = Array.from($versions) + expect(first).to.have.attr('data-selected', 'aboveSelected') + rest.forEach(version => + expect(version).to.have.attr('data-selected', 'selectedEdge') + ) + }) + }) }) describe('compare mode', function () {