diff --git a/services/web/app/src/Features/Project/ProjectListController.mjs b/services/web/app/src/Features/Project/ProjectListController.mjs index 439100e9a3..4f7b0cb567 100644 --- a/services/web/app/src/Features/Project/ProjectListController.mjs +++ b/services/web/app/src/Features/Project/ProjectListController.mjs @@ -440,15 +440,6 @@ async function projectListPage(req, res, next) { ) } - // Get the user's assignment for this page's Bootstrap 5 split test, which - // populates splitTestVariants with a value for the split test name and allows - // Pug to read it - await SplitTestHandler.promises.getAssignment( - req, - res, - 'bootstrap-5-project-dashboard' - ) - res.render('project/list-react', { title: 'your_projects', usersBestSubscription, diff --git a/services/web/app/views/project/list-react.pug b/services/web/app/views/project/list-react.pug index 8c42569a05..d7c9295e14 100644 --- a/services/web/app/views/project/list-react.pug +++ b/services/web/app/views/project/list-react.pug @@ -6,7 +6,6 @@ block entrypointVar block vars - var suppressNavContentLinks = true - bootstrap5PageStatus = 'enabled' // One of 'disabled', 'enabled', and 'queryStringOnly' - - bootstrap5PageSplitTest = 'bootstrap-5-project-dashboard' block append meta meta(name="ol-usersBestSubscription" data-type="json" content=usersBestSubscription) diff --git a/services/web/frontend/extracted-translations.json b/services/web/frontend/extracted-translations.json index c7fe76f738..18900a4158 100644 --- a/services/web/frontend/extracted-translations.json +++ b/services/web/frontend/extracted-translations.json @@ -764,7 +764,6 @@ "join_project": "", "join_team_explanation": "", "joined_team": "", - "joining": "", "justify": "", "kb_suggestions_enquiry": "", "keep_current_plan": "", diff --git a/services/web/frontend/js/features/project-list/components/current-plan-widget/commons-plan.tsx b/services/web/frontend/js/features/project-list/components/current-plan-widget/commons-plan.tsx index 8eae9d1066..e687c8a362 100644 --- a/services/web/frontend/js/features/project-list/components/current-plan-widget/commons-plan.tsx +++ b/services/web/frontend/js/features/project-list/components/current-plan-widget/commons-plan.tsx @@ -2,9 +2,6 @@ import { useTranslation, Trans } from 'react-i18next' import { CommonsPlanSubscription } from '../../../../../../types/project/dashboard/subscription' import OLTooltip from '@/features/ui/components/ol/ol-tooltip' import MaterialIcon from '@/shared/components/material-icon' -import BootstrapVersionSwitcher from '@/features/ui/components/bootstrap-5/bootstrap-version-switcher' -import { bsVersion } from '@/features/utils/bootstrap-5' -import classnames from 'classnames' type CommonsPlanProps = Pick< CommonsPlanSubscription, @@ -23,14 +20,7 @@ function CommonsPlan({ return ( <> - - {currentPlanLabel} - + {currentPlanLabel} {currentPlanLabel}  - } - bs5={ - - } - /> + diff --git a/services/web/frontend/js/features/project-list/components/current-plan-widget/free-plan.tsx b/services/web/frontend/js/features/project-list/components/current-plan-widget/free-plan.tsx index 9b50254f2b..fe3fb18125 100644 --- a/services/web/frontend/js/features/project-list/components/current-plan-widget/free-plan.tsx +++ b/services/web/frontend/js/features/project-list/components/current-plan-widget/free-plan.tsx @@ -1,12 +1,9 @@ import { useTranslation, Trans } from 'react-i18next' import OLButton from '@/features/ui/components/ol/ol-button' import OLTooltip from '@/features/ui/components/ol/ol-tooltip' -import BootstrapVersionSwitcher from '@/features/ui/components/bootstrap-5/bootstrap-version-switcher' import MaterialIcon from '@/shared/components/material-icon' import { FreePlanSubscription } from '../../../../../../types/project/dashboard/subscription' import * as eventTracking from '../../../../infrastructure/event-tracking' -import { bsVersion } from '@/features/utils/bootstrap-5' -import classnames from 'classnames' type FreePlanProps = Pick @@ -27,14 +24,7 @@ function FreePlan({ featuresPageURL }: FreePlanProps) { return ( <> - - {currentPlanLabel} - + {currentPlanLabel} {currentPlanLabel}  - } - bs5={ - - } - /> + {' '} - + - - {currentPlanLabel} - + {currentPlanLabel} {currentPlanLabel}  - } - bs5={ - - } - /> + diff --git a/services/web/frontend/js/features/project-list/components/current-plan-widget/individual-plan.tsx b/services/web/frontend/js/features/project-list/components/current-plan-widget/individual-plan.tsx index cb542e645d..f0870ac12a 100644 --- a/services/web/frontend/js/features/project-list/components/current-plan-widget/individual-plan.tsx +++ b/services/web/frontend/js/features/project-list/components/current-plan-widget/individual-plan.tsx @@ -1,10 +1,7 @@ import { useTranslation, Trans } from 'react-i18next' import { IndividualPlanSubscription } from '../../../../../../types/project/dashboard/subscription' import OLTooltip from '@/features/ui/components/ol/ol-tooltip' -import BootstrapVersionSwitcher from '@/features/ui/components/bootstrap-5/bootstrap-version-switcher' import MaterialIcon from '@/shared/components/material-icon' -import { bsVersion } from '@/features/utils/bootstrap-5' -import classnames from 'classnames' type IndividualPlanProps = Pick< IndividualPlanSubscription, @@ -36,14 +33,7 @@ function IndividualPlan({ return ( <> - - {currentPlanLabel} - + {currentPlanLabel} {currentPlanLabel}  - } - bs5={ - - } - /> + diff --git a/services/web/frontend/js/features/project-list/components/dropdown/actions-dropdown.tsx b/services/web/frontend/js/features/project-list/components/dropdown/actions-dropdown.tsx index d9d7f6ba2e..058f0319ce 100644 --- a/services/web/frontend/js/features/project-list/components/dropdown/actions-dropdown.tsx +++ b/services/web/frontend/js/features/project-list/components/dropdown/actions-dropdown.tsx @@ -1,6 +1,4 @@ -import { useState, useCallback } from 'react' import { useTranslation } from 'react-i18next' -import { Dropdown as BS3Dropdown } from 'react-bootstrap' import { Spinner } from 'react-bootstrap-5' import { Dropdown, @@ -8,8 +6,6 @@ import { DropdownMenu, DropdownToggle, } from '@/features/ui/components/bootstrap-5/dropdown-menu' -import MenuItemButton from './menu-item-button' -import Icon from '../../../../shared/components/icon' import CopyProjectButton from '../table/cells/action-buttons/copy-project-button' import DownloadProjectButton from '../table/cells/action-buttons/download-project-button' import ArchiveProjectButton from '../table/cells/action-buttons/archive-project-button' @@ -22,256 +18,12 @@ import { Project } from '../../../../../../types/project/dashboard/api' import CompileAndDownloadProjectPDFButton from '../table/cells/action-buttons/compile-and-download-project-pdf-button' import RenameProjectButton from '../table/cells/action-buttons/rename-project-button' import MaterialIcon from '@/shared/components/material-icon' -import BootstrapVersionSwitcher from '@/features/ui/components/bootstrap-5/bootstrap-version-switcher' - -type ActionButtonProps = { - project: Project - onClick: (e?: T, fn?: (e?: T) => void) => void -} - -function CopyProjectButtonMenuItem({ project, onClick }: ActionButtonProps) { - return ( - - {(text, handleOpenModal) => ( - handleOpenModal(e, onClick)} - className="projects-action-menu-item" - > - {' '} - {text} - - )} - - ) -} - -function CompileAndDownloadProjectPDFButtonMenuItem({ - project, - onClick, -}: ActionButtonProps) { - return ( - - {(text, pendingCompile, downloadProject) => ( - downloadProject(e, onClick)} - className="projects-action-menu-item" - > - {pendingCompile ? ( - - ) : ( - - )}{' '} - {text} - - )} - - ) -} - -function DownloadProjectButtonMenuItem({ - project, - onClick, -}: ActionButtonProps) { - const handleClick = (downloadProject: () => void) => { - downloadProject() - onClick() - } - - return ( - - {(text, downloadProject) => ( - handleClick(downloadProject)} - className="projects-action-menu-item" - > - {' '} - {text} - - )} - - ) -} - -function ArchiveProjectButtonMenuItem({ project, onClick }: ActionButtonProps) { - const handleClick = (handleOpenModal: () => void) => { - handleOpenModal() - onClick() - } - - return ( - - {(text, handleOpenModal) => ( - handleClick(handleOpenModal)} - className="projects-action-menu-item" - > - {' '} - {text} - - )} - - ) -} - -function TrashProjectButtonMenuItem({ project, onClick }: ActionButtonProps) { - const handleClick = (handleOpenModal: () => void) => { - handleOpenModal() - onClick() - } - - return ( - - {(text, handleOpenModal) => ( - handleClick(handleOpenModal)} - className="projects-action-menu-item" - > - {' '} - {text} - - )} - - ) -} - -function UnarchiveProjectButtonMenuItem({ - project, - onClick, -}: ActionButtonProps) { - const handleClick = (unarchiveProject: () => Promise) => { - unarchiveProject() - onClick() - } - - return ( - - {(text, unarchiveProject) => ( - handleClick(unarchiveProject)} - className="projects-action-menu-item" - > - {' '} - {text} - - )} - - ) -} - -function UntrashProjectButtonMenuItem({ project, onClick }: ActionButtonProps) { - const handleClick = (untrashProject: () => Promise) => { - untrashProject() - onClick() - } - - return ( - - {(text, untrashProject) => ( - handleClick(untrashProject)} - className="projects-action-menu-item" - > - {' '} - {text} - - )} - - ) -} - -function LeaveProjectButtonMenuItem({ project, onClick }: ActionButtonProps) { - return ( - - {text => ( - - {' '} - {text} - - )} - - ) -} - -function DeleteProjectButtonMenuItem({ project, onClick }: ActionButtonProps) { - return ( - - {text => ( - - {' '} - {text} - - )} - - ) -} - -function RenameProjectButtonMenuItem({ project, onClick }: ActionButtonProps) { - const handleClick = (handleOpenModal: () => void) => { - handleOpenModal() - onClick() - } - return ( - - {(text, handleOpenModal) => ( - handleClick(handleOpenModal)} - className="projects-action-menu-item" - > - {' '} - {text} - - )} - - ) -} type ActionDropdownProps = { project: Project } -export function BS3ActionsDropdown({ project }: ActionDropdownProps) { - const [isOpened, setIsOpened] = useState(false) - - const handleClose = useCallback(() => { - setIsOpened(false) - }, [setIsOpened]) - - return ( - setIsOpened(open)} - > - - - - - - - - - - - - - - - - - ) -} - -function BS5ActionsDropdown({ project }: ActionDropdownProps) { +function ActionsDropdown({ project }: ActionDropdownProps) { const { t } = useTranslation() return ( @@ -434,13 +186,4 @@ function BS5ActionsDropdown({ project }: ActionDropdownProps) { ) } -function ActionsDropdown({ project }: ActionDropdownProps) { - return ( - } - bs5={} - /> - ) -} - export default ActionsDropdown diff --git a/services/web/frontend/js/features/project-list/components/dropdown/projects-dropdown.tsx b/services/web/frontend/js/features/project-list/components/dropdown/projects-dropdown.tsx index d2ce1181ac..7811e457c2 100644 --- a/services/web/frontend/js/features/project-list/components/dropdown/projects-dropdown.tsx +++ b/services/web/frontend/js/features/project-list/components/dropdown/projects-dropdown.tsx @@ -1,10 +1,5 @@ import { useState, useEffect, useRef } from 'react' import { useTranslation } from 'react-i18next' -import { - Dropdown as BS3Dropdown, - MenuItem as BS3MenuItem, -} from 'react-bootstrap' -import Icon from '../../../../shared/components/icon' import { Filter, UNCATEGORIZED_KEY, @@ -19,8 +14,6 @@ import { } from '@/features/ui/components/bootstrap-5/dropdown-menu' import ProjectsFilterMenu from '../projects-filter-menu' import TagsList from '../tags-list' -import MenuItemButton from './menu-item-button' -import BootstrapVersionSwitcher from '@/features/ui/components/bootstrap-5/bootstrap-version-switcher' type ItemProps = { filter: Filter @@ -38,30 +31,15 @@ export function Item({ filter, text, onClick }: ItemProps) { return ( {isActive => ( - - {isActive ? ( - - ) : null} - {text} - - } - bs5={ - - {text} - - } - /> + + {text} + )} ) @@ -70,7 +48,6 @@ export function Item({ filter, text, onClick }: ItemProps) { function ProjectsDropdown() { const { t } = useTranslation() const [title, setTitle] = useState(() => t('all_projects')) - const [isOpened, setIsOpened] = useState(false) const { filter, selectedTagId, tags } = useProjectListContext() const filterTranslations = useRef>({ all: t('all_projects'), @@ -79,7 +56,6 @@ function ProjectsDropdown() { archived: t('archived_projects'), trashed: t('trashed_projects'), }) - const handleClose = () => setIsOpened(false) useEffect(() => { if (selectedTagId === undefined) { @@ -98,84 +74,37 @@ function ProjectsDropdown() { }, [filter, tags, selectedTagId, t]) return ( - setIsOpened(open)} - > - - {title} - - - - - - - - - {t('tags')}: - - - - } - bs5={ - - - - {title} - - - -
  • - -
  • -
  • - -
  • -
  • - -
  • -
  • - -
  • -
  • - -
  • - - {t('tags')}: - - -
    -
    - } - /> + + + + {title} + + + +
  • + +
  • +
  • + +
  • +
  • + +
  • +
  • + +
  • +
  • + +
  • + {t('tags')}: + +
    +
    ) } diff --git a/services/web/frontend/js/features/project-list/components/dropdown/sort-by-dropdown.tsx b/services/web/frontend/js/features/project-list/components/dropdown/sort-by-dropdown.tsx index d7fd703edc..0d23aebf57 100644 --- a/services/web/frontend/js/features/project-list/components/dropdown/sort-by-dropdown.tsx +++ b/services/web/frontend/js/features/project-list/components/dropdown/sort-by-dropdown.tsx @@ -1,15 +1,9 @@ import { useState, useEffect, useRef } from 'react' import { useTranslation } from 'react-i18next' -import { - Dropdown as BS3Dropdown, - MenuItem as BS3MenuItem, -} from 'react-bootstrap' -import Icon from '../../../../shared/components/icon' import useSort from '../../hooks/use-sort' import withContent, { SortBtnProps } from '../sort/with-content' import { useProjectListContext } from '../../context/project-list-context' import { Sort } from '../../../../../../types/project/dashboard/api' -import MenuItemButton from './menu-item-button' import { Dropdown, DropdownHeader, @@ -17,31 +11,17 @@ import { DropdownMenu, DropdownToggle, } from '@/features/ui/components/bootstrap-5/dropdown-menu' -import BootstrapVersionSwitcher from '@/features/ui/components/bootstrap-5/bootstrap-version-switcher' function Item({ onClick, text, iconType, screenReaderText }: SortBtnProps) { return ( - - {iconType ? ( - - ) : null} - {text} - {screenReaderText} - - } - bs5={ - - {text} - - } - /> + + {text} + ) } @@ -50,7 +30,6 @@ const ItemWithContent = withContent(Item) function SortByDropdown() { const { t } = useTranslation() const [title, setTitle] = useState(() => t('last_modified')) - const [isOpened, setIsOpened] = useState(false) const { sort } = useProjectListContext() const { handleSort } = useSort() const sortByTranslations = useRef>({ @@ -60,11 +39,6 @@ function SortByDropdown() { }) const handleClick = (by: Sort['by']) => { - setTitle(sortByTranslations.current[by]) - setIsOpened(false) - handleSort(by) - } - const handleClickBS5 = (by: Sort['by']) => { setTitle(sortByTranslations.current[by]) handleSort(by) } @@ -74,84 +48,41 @@ function SortByDropdown() { }, [sort.by]) return ( - setIsOpened(open)} - > - - {title} - - - - {t('sort_by')}: - handleClick('title')} - /> - handleClick('owner')} - /> - handleClick('lastUpdated')} - /> - - - } - bs5={ - - - - {title} - - - - - {t('sort_by')}: - - handleClickBS5('title')} - /> - handleClickBS5('owner')} - /> - handleClickBS5('lastUpdated')} - /> - - - } - /> + + + + {title} + + + + + {t('sort_by')}: + + handleClick('title')} + /> + handleClick('owner')} + /> + handleClick('lastUpdated')} + /> + + ) } diff --git a/services/web/frontend/js/features/project-list/components/modals/create-tag-modal.tsx b/services/web/frontend/js/features/project-list/components/modals/create-tag-modal.tsx index 99d3018560..eac01b918a 100644 --- a/services/web/frontend/js/features/project-list/components/modals/create-tag-modal.tsx +++ b/services/web/frontend/js/features/project-list/components/modals/create-tag-modal.tsx @@ -129,9 +129,6 @@ export default function CreateTagModal({ status === 'pending' || !tagName?.length || !!validationError } isLoading={isLoading} - bs3Props={{ - loading: isLoading ? `${t('creating')}…` : t('create'), - }} > {t('create')}
    diff --git a/services/web/frontend/js/features/project-list/components/modals/delete-tag-modal.tsx b/services/web/frontend/js/features/project-list/components/modals/delete-tag-modal.tsx index 89f1d2db5e..1f54c86c78 100644 --- a/services/web/frontend/js/features/project-list/components/modals/delete-tag-modal.tsx +++ b/services/web/frontend/js/features/project-list/components/modals/delete-tag-modal.tsx @@ -72,9 +72,6 @@ export default function DeleteTagModal({ variant="danger" disabled={isLoading} isLoading={isLoading} - bs3Props={{ - loading: isLoading ? `${t('deleting')}…` : t('delete'), - }} > {t('delete')} diff --git a/services/web/frontend/js/features/project-list/components/modals/edit-tag-modal.tsx b/services/web/frontend/js/features/project-list/components/modals/edit-tag-modal.tsx index cc59d31173..e7aa93e649 100644 --- a/services/web/frontend/js/features/project-list/components/modals/edit-tag-modal.tsx +++ b/services/web/frontend/js/features/project-list/components/modals/edit-tag-modal.tsx @@ -138,9 +138,6 @@ export function EditTagModal({ id, tag, onEdit, onClose }: EditTagModalProps) { !!validationError } isLoading={isLoading} - bs3Props={{ - loading: isLoading ? `${t('saving')}…` : t('save'), - }} > {t('save')} diff --git a/services/web/frontend/js/features/project-list/components/modals/manage-tag-modal.tsx b/services/web/frontend/js/features/project-list/components/modals/manage-tag-modal.tsx index 777ed46c3e..ceec6de972 100644 --- a/services/web/frontend/js/features/project-list/components/modals/manage-tag-modal.tsx +++ b/services/web/frontend/js/features/project-list/components/modals/manage-tag-modal.tsx @@ -18,9 +18,7 @@ import OLForm from '@/features/ui/components/ol/ol-form' import OLFormGroup from '@/features/ui/components/ol/ol-form-group' import OLFormLabel from '@/features/ui/components/ol/ol-form-label' import OLButton from '@/features/ui/components/ol/ol-button' -import BootstrapVersionSwitcher from '@/features/ui/components/bootstrap-5/bootstrap-version-switcher' import Notification from '@/shared/components/notification' -import { bsVersion } from '@/features/utils/bootstrap-5' type ManageTagModalProps = { id: string @@ -125,12 +123,9 @@ export function ManageTagModal({ runDeleteTag(tag._id)} - className={bsVersion({ bs3: 'pull-left', bs5: 'me-auto' })} + className="me-auto" disabled={isDeleteLoading || isUpdateLoading} isLoading={isDeleteLoading} - bs3Props={{ - loading: isDeleteLoading ? `${t('deleting')}…` : t('delete_tag'), - }} > {t('delete_tag')} @@ -151,15 +146,9 @@ export function ManageTagModal({ (newTagName === tag?.name && selectedColor === getTagColor(tag)) )} isLoading={isUpdateLoading} - bs3Props={{ - loading: isUpdateLoading - ? `${t('saving')}…` - : t('save_or_cancel-save'), - }} > {t('save_or_cancel-save')} - } /> ) diff --git a/services/web/frontend/js/features/project-list/components/new-project-button.tsx b/services/web/frontend/js/features/project-list/components/new-project-button.tsx index ef519a563c..2288ab7b08 100644 --- a/services/web/frontend/js/features/project-list/components/new-project-button.tsx +++ b/services/web/frontend/js/features/project-list/components/new-project-button.tsx @@ -1,10 +1,5 @@ import { type JSXElementConstructor, useCallback, useState } from 'react' -import { - Dropdown as BS3Dropdown, - MenuItem as BS3MenuItem, -} from 'react-bootstrap' import { useTranslation } from 'react-i18next' -import ControlledDropdown from '../../../shared/components/controlled-dropdown' import getMeta from '../../../utils/meta' import NewProjectButtonModal, { NewProjectButtonModalVariant, @@ -13,7 +8,6 @@ import AddAffiliation, { useAddAffiliation } from './add-affiliation' import { Nullable } from '../../../../../types/utils' import { sendMB } from '../../../infrastructure/event-tracking' import importOverleafModules from '../../../../macros/import-overleaf-module.macro' -import BootstrapVersionSwitcher from '@/features/ui/components/bootstrap-5/bootstrap-version-switcher' import { Dropdown, DropdownDivider, @@ -151,225 +145,119 @@ function NewProjectButton({ return ( <> - - + + {buttonText || t('new_project')} + + +
  • + + handleModalMenuClick(e, { + modalVariant: 'blank_project', + dropdownMenuEvent: 'blank-project', + }) + } > - {buttonText || t('new_project')} - - - - handleModalMenuClick(e, { - modalVariant: 'blank_project', - dropdownMenuEvent: 'blank-project', - }) - } - > - {t('blank_project')} - - - handleModalMenuClick(e, { - modalVariant: 'example_project', - dropdownMenuEvent: 'example-project', - }) - } - > - {t('example_project')} - - - handleModalMenuClick(e, { - modalVariant: 'upload_project', - dropdownMenuEvent: 'upload-project', - }) - } - > - {t('upload_project')} - - {ImportProjectFromGithubMenu && ( - - handleModalMenuClick(e, { - modalVariant: 'import_from_github', - dropdownMenuEvent: 'import-from-github', - }) - } - /> - )} - {portalTemplates.length > 0 ? ( - <> - - - {portalTemplates.map((portalTemplate, index) => ( - - handlePortalTemplateClick(e, portalTemplate.name) - } - > - {portalTemplate.name} - - ))} - - ) : null} - - {templateLinks && templateLinks.length > 0 && ( - <> - - - - )} - {templateLinks?.map((templateLink, index) => ( - - handleStaticTemplateClick(e, templateLink.trackingKey) - } - > - {templateLink.name === 'view_all' - ? t('view_all') - : templateLink.name} - - ))} - {showAddAffiliationWidget && enableAddAffiliationWidget ? ( - <> - -
  • - -
  • - - ) : null} - - - } - bs5={ - - + +
  • + + handleModalMenuClick(e, { + modalVariant: 'example_project', + dropdownMenuEvent: 'example-project', + }) + } > - {buttonText || t('new_project')} - - -
  • - - handleModalMenuClick(e, { - modalVariant: 'blank_project', - dropdownMenuEvent: 'blank-project', - }) - } - > - {t('blank_project')} - -
  • -
  • - - handleModalMenuClick(e, { - modalVariant: 'example_project', - dropdownMenuEvent: 'example-project', - }) - } - > - {t('example_project')} - -
  • -
  • - - handleModalMenuClick(e, { - modalVariant: 'upload_project', - dropdownMenuEvent: 'upload-project', - }) - } - > - {t('upload_project')} - -
  • -
  • - {ImportProjectFromGithubMenu && ( - - handleModalMenuClick(e, { - modalVariant: 'import_from_github', - dropdownMenuEvent: 'import-from-github', - }) - } - /> - )} -
  • - {portalTemplates.length > 0 ? ( - <> - - - {portalTemplates.map((portalTemplate, index) => ( -
  • - - handlePortalTemplateClick(e, portalTemplate.name) - } - aria-label={`${portalTemplate.name} ${t('template')}`} - > - {portalTemplate.name} - -
  • - ))} - - ) : null} - - {templateLinks && templateLinks.length > 0 && ( - <> - - - - )} - {templateLinks?.map((templateLink, index) => ( -
  • + {t('example_project')} + +
  • +
  • + + handleModalMenuClick(e, { + modalVariant: 'upload_project', + dropdownMenuEvent: 'upload-project', + }) + } + > + {t('upload_project')} + +
  • +
  • + {ImportProjectFromGithubMenu && ( + + handleModalMenuClick(e, { + modalVariant: 'import_from_github', + dropdownMenuEvent: 'import-from-github', + }) + } + /> + )} +
  • + {portalTemplates.length > 0 ? ( + <> + + + {portalTemplates.map((portalTemplate, index) => ( +
  • - handleStaticTemplateClick(e, templateLink.trackingKey) + handlePortalTemplateClick(e, portalTemplate.name) } - aria-label={`${templateLink.name} ${t('template')}`} + aria-label={`${portalTemplate.name} ${t('template')}`} > - {templateLink.name === 'view_all' - ? t('view_all') - : templateLink.name} + {portalTemplate.name}
  • ))} - {showAddAffiliationWidget && enableAddAffiliationWidget ? ( - <> - -
  • - -
  • - - ) : null} -
    - - } - /> + + ) : null} + + {templateLinks && templateLinks.length > 0 && ( + <> + + + + )} + {templateLinks?.map((templateLink, index) => ( +
  • + + handleStaticTemplateClick(e, templateLink.trackingKey) + } + aria-label={`${templateLink.name} ${t('template')}`} + > + {templateLink.name === 'view_all' + ? t('view_all') + : templateLink.name} + +
  • + ))} + {showAddAffiliationWidget && enableAddAffiliationWidget ? ( + <> + +
  • + +
  • + + ) : null} + + setModal(null)} /> ) diff --git a/services/web/frontend/js/features/project-list/components/new-project-button/modal-content-new-project-form.tsx b/services/web/frontend/js/features/project-list/components/new-project-button/modal-content-new-project-form.tsx index 169258de40..13e0458127 100644 --- a/services/web/frontend/js/features/project-list/components/new-project-button/modal-content-new-project-form.tsx +++ b/services/web/frontend/js/features/project-list/components/new-project-button/modal-content-new-project-form.tsx @@ -102,9 +102,6 @@ function ModalContentNewProjectForm({ onCancel, template = 'none' }: Props) { onClick={createNewProject} disabled={projectName === '' || isLoading} isLoading={isLoading} - bs3Props={{ - loading: isLoading ? `${t('creating')}…` : t('create'), - }} > {t('create')} diff --git a/services/web/frontend/js/features/project-list/components/notifications/groups/affiliation/reconfirm-affiliation.tsx b/services/web/frontend/js/features/project-list/components/notifications/groups/affiliation/reconfirm-affiliation.tsx index ba4cacac2c..3416aefb04 100644 --- a/services/web/frontend/js/features/project-list/components/notifications/groups/affiliation/reconfirm-affiliation.tsx +++ b/services/web/frontend/js/features/project-list/components/notifications/groups/affiliation/reconfirm-affiliation.tsx @@ -1,6 +1,5 @@ import { useState, useEffect } from 'react' import { useTranslation, Trans } from 'react-i18next' -import Icon from '../../../../../../shared/components/icon' import getMeta from '../../../../../../utils/meta' import useAsync from '../../../../../../shared/hooks/use-async' import { @@ -88,13 +87,6 @@ function ReconfirmAffiliation({ className="btn-inline-link" disabled={isLoading} isLoading={isLoading} - bs3Props={{ - loading: isLoading ? ( - <> - {t('sending')}… - - ) : null, - }} > {t('resend_confirmation_email')} @@ -143,14 +135,6 @@ function ReconfirmAffiliation({ action={ - {t('sending')}… - - ) : null, - }} isLoading={isLoading || isPending} disabled={isLoading || isPending} onClick={handleRequestReconfirmation} diff --git a/services/web/frontend/js/features/project-list/components/notifications/groups/common.tsx b/services/web/frontend/js/features/project-list/components/notifications/groups/common.tsx index 9509b17c14..96bbd1a840 100644 --- a/services/web/frontend/js/features/project-list/components/notifications/groups/common.tsx +++ b/services/web/frontend/js/features/project-list/components/notifications/groups/common.tsx @@ -1,6 +1,5 @@ import { useTranslation, Trans } from 'react-i18next' import Notification from '../notification' -import Icon from '../../../../../shared/components/icon' import getMeta from '../../../../../utils/meta' import useAsyncDismiss from '../hooks/useAsyncDismiss' import useAsync from '../../../../../shared/hooks/use-async' @@ -96,13 +95,6 @@ function CommonNotification({ notification }: CommonNotificationProps) { ) : ( - {t('joining')}… - - ) : null, - }} isLoading={isLoading} disabled={isLoading} onClick={() => handleAcceptInvite(notification)} diff --git a/services/web/frontend/js/features/project-list/components/project-list-root.tsx b/services/web/frontend/js/features/project-list/components/project-list-root.tsx index d56646b717..7eb287c362 100644 --- a/services/web/frontend/js/features/project-list/components/project-list-root.tsx +++ b/services/web/frontend/js/features/project-list/components/project-list-root.tsx @@ -26,11 +26,8 @@ import withErrorBoundary from '../../../infrastructure/error-boundary' import { GenericErrorBoundaryFallback } from '../../../shared/components/generic-error-boundary-fallback' import { SplitTestProvider } from '@/shared/context/split-test-context' import OLCol from '@/features/ui/components/ol/ol-col' -import { bsVersion } from '@/features/utils/bootstrap-5' -import classnames from 'classnames' import Notification from '@/shared/components/notification' import OLRow from '@/features/ui/components/ol/ol-row' -import BootstrapVersionSwitcher from '@/features/ui/components/bootstrap-5/bootstrap-version-switcher' import { TableContainer } from '@/features/ui/components/bootstrap-5/table' function ProjectListRoot() { @@ -78,13 +75,7 @@ function ProjectListPageContent() { const { t } = useTranslation() const tableTopArea = ( -
    +
    -
    +
    {totalProjectsCount > 0 ? ( <> @@ -131,43 +117,22 @@ function ProjectListPageContent() { filter={filter} selectedTag={selectedTag} selectedTagId={selectedTagId} - className={classnames( - 'text-truncate', - bsVersion({ - bs5: 'd-none d-md-block', - bs3: 'hidden-xs', - }) - )} + className="text-truncate d-none d-md-block" />
    -
    +
    {selectedProjects.length === 0 ? ( ) : ( )}
    -
    +
    - + -
    +
    -
    +
    - - {tableTopArea} - -
    - } - bs5={ - - {tableTopArea} - - - } - /> + + {tableTopArea} + + @@ -253,11 +198,7 @@ function DashApiError() { const { t } = useTranslation() return ( - +
    e.preventDefault()} - bs3Props={{ horizontal: true }} {...props} > - + , - bs5: , - })} + prepend={} append={ inputValue.length > 0 && ( ) } diff --git a/services/web/frontend/js/features/project-list/components/sidebar/sidebar.tsx b/services/web/frontend/js/features/project-list/components/sidebar/sidebar.tsx index 6ce451607d..55bab83261 100644 --- a/services/web/frontend/js/features/project-list/components/sidebar/sidebar.tsx +++ b/services/web/frontend/js/features/project-list/components/sidebar/sidebar.tsx @@ -3,8 +3,6 @@ import SidebarFilters from './sidebar-filters' import AddAffiliation, { useAddAffiliation } from '../add-affiliation' import SurveyWidget from '../survey-widget' import { usePersistedResize } from '../../../../shared/hooks/use-resize' -import { bsVersion } from '@/features/utils/bootstrap-5' -import classnames from 'classnames' function Sidebar() { const { show: showAddAffiliationWidget } = useAddAffiliation() @@ -14,10 +12,7 @@ function Sidebar() { return (
    - - - - - - handleEditTag(e, tag._id)} - > - {t('edit')} - - handleDeleteTag(e, tag._id)} - > - {t('delete')} - - - - } - bs3={ - - -
      -
    • - -
    • -
    • - -
    • -
    -
    - } - /> + + + + + + + handleEditTag(e, tag._id)} + > + {t('edit')} + + handleDeleteTag(e, tag._id)} + > + {t('delete')} + + + ) })} diff --git a/services/web/frontend/js/features/project-list/components/sort/with-content.tsx b/services/web/frontend/js/features/project-list/components/sort/with-content.tsx index 88226f649c..a76d746a32 100644 --- a/services/web/frontend/js/features/project-list/components/sort/with-content.tsx +++ b/services/web/frontend/js/features/project-list/components/sort/with-content.tsx @@ -1,6 +1,5 @@ import { useTranslation } from 'react-i18next' import { Sort } from '../../../../../../types/project/dashboard/api' -import { bsVersion } from '@/features/utils/bootstrap-5' type SortBtnOwnProps = { column: string @@ -28,9 +27,7 @@ function withContent( if (column === sort.by) { iconType = - sort.order === 'asc' - ? bsVersion({ bs5: 'arrow_upward_alt', bs3: 'caret-up' }) - : bsVersion({ bs5: 'arrow_downward_alt', bs3: 'caret-down' }) + sort.order === 'asc' ? 'arrow_upward_alt' : 'arrow_downward_alt' screenReaderText = t('reverse_x_sort_order', { x: text }) } diff --git a/services/web/frontend/js/features/project-list/components/survey-widget.tsx b/services/web/frontend/js/features/project-list/components/survey-widget.tsx index 1695ec8d80..ae0847ef92 100644 --- a/services/web/frontend/js/features/project-list/components/survey-widget.tsx +++ b/services/web/frontend/js/features/project-list/components/survey-widget.tsx @@ -2,7 +2,6 @@ import usePersistedState from '../../../shared/hooks/use-persisted-state' import getMeta from '../../../utils/meta' import { useCallback } from 'react' import Close from '@/shared/components/close' -import { bsVersion } from '@/features/utils/bootstrap-5' export default function SurveyWidget() { const survey = getMeta('ol-survey') @@ -22,13 +21,7 @@ export default function SurveyWidget() { return (
    -
    +
    {survey.preText}  diff --git a/services/web/frontend/js/features/project-list/components/table/cells/action-buttons/compile-and-download-project-pdf-button.tsx b/services/web/frontend/js/features/project-list/components/table/cells/action-buttons/compile-and-download-project-pdf-button.tsx index feb5ecccbe..2225945465 100644 --- a/services/web/frontend/js/features/project-list/components/table/cells/action-buttons/compile-and-download-project-pdf-button.tsx +++ b/services/web/frontend/js/features/project-list/components/table/cells/action-buttons/compile-and-download-project-pdf-button.tsx @@ -1,7 +1,6 @@ import { useTranslation } from 'react-i18next' import { memo, useCallback, useState } from 'react' import { Project } from '../../../../../../../../types/project/dashboard/api' -import Icon from '../../../../../../shared/components/icon' import * as eventTracking from '../../../../../../infrastructure/event-tracking' import { useLocation } from '../../../../../../shared/hooks/use-location' import useAbortController from '../../../../../../shared/hooks/use-abort-controller' @@ -15,7 +14,6 @@ import OLModal, { OLModalHeader, OLModalTitle, } from '@/features/ui/components/ol/ol-modal' -import { bsVersion } from '@/features/utils/bootstrap-5' import OLIconButton from '@/features/ui/components/ol/ol-icon-button' type CompileAndDownloadProjectPDFButtonProps = { @@ -159,13 +157,7 @@ const CompileAndDownloadProjectPDFButtonTooltip = memo( loadingLabel={text} isLoading={pendingCompile} className="action-btn" - icon={bsVersion({ bs5: 'picture_as_pdf', bs3: 'file-pdf-o' })} - bs3Props={{ - fw: true, - loading: pendingCompile ? ( - - ) : null, - }} + icon="picture_as_pdf" /> diff --git a/services/web/frontend/js/features/project-list/components/table/cells/action-buttons/copy-project-button.tsx b/services/web/frontend/js/features/project-list/components/table/cells/action-buttons/copy-project-button.tsx index e5d34abb24..6141fd93d4 100644 --- a/services/web/frontend/js/features/project-list/components/table/cells/action-buttons/copy-project-button.tsx +++ b/services/web/frontend/js/features/project-list/components/table/cells/action-buttons/copy-project-button.tsx @@ -12,7 +12,6 @@ import { useProjectTags } from '@/features/project-list/hooks/use-project-tags' import { isSmallDevice } from '../../../../../../infrastructure/event-tracking' import OLTooltip from '@/features/ui/components/ol/ol-tooltip' import OLIconButton from '@/features/ui/components/ol/ol-icon-button' -import { bsVersion } from '@/features/utils/bootstrap-5' type CopyButtonProps = { project: Project @@ -111,8 +110,7 @@ const CopyProjectButtonTooltip = memo(function CopyProjectButtonTooltip({ variant="link" accessibilityLabel={text} className="action-btn" - icon={bsVersion({ bs5: 'file_copy', bs3: 'files-o' })} - bs3Props={{ fw: true }} + icon="file_copy" /> diff --git a/services/web/frontend/js/features/project-list/components/table/cells/action-buttons/delete-project-button.tsx b/services/web/frontend/js/features/project-list/components/table/cells/action-buttons/delete-project-button.tsx index cfb62d6b59..b38ed4a545 100644 --- a/services/web/frontend/js/features/project-list/components/table/cells/action-buttons/delete-project-button.tsx +++ b/services/web/frontend/js/features/project-list/components/table/cells/action-buttons/delete-project-button.tsx @@ -8,7 +8,6 @@ import { useProjectListContext } from '../../../../context/project-list-context' import getMeta from '@/utils/meta' import OLTooltip from '@/features/ui/components/ol/ol-tooltip' import OLIconButton from '@/features/ui/components/ol/ol-icon-button' -import { bsVersion } from '@/features/utils/bootstrap-5' type DeleteProjectButtonProps = { project: Project @@ -76,8 +75,7 @@ const DeleteProjectButtonTooltip = memo(function DeleteProjectButtonTooltip({ variant="link" accessibilityLabel={text} className="action-btn" - icon={bsVersion({ bs5: 'block', bs3: 'ban' })} - bs3Props={{ fw: true }} + icon="block" /> diff --git a/services/web/frontend/js/features/project-list/components/table/cells/action-buttons/download-project-button.tsx b/services/web/frontend/js/features/project-list/components/table/cells/action-buttons/download-project-button.tsx index 568c6f5887..4a5e599790 100644 --- a/services/web/frontend/js/features/project-list/components/table/cells/action-buttons/download-project-button.tsx +++ b/services/web/frontend/js/features/project-list/components/table/cells/action-buttons/download-project-button.tsx @@ -6,7 +6,6 @@ import { useLocation } from '../../../../../../shared/hooks/use-location' import { isSmallDevice } from '../../../../../../infrastructure/event-tracking' import OLTooltip from '@/features/ui/components/ol/ol-tooltip' import OLIconButton from '@/features/ui/components/ol/ol-icon-button' -import { bsVersion } from '@/features/utils/bootstrap-5' type DownloadProjectButtonProps = { project: Project @@ -52,8 +51,7 @@ const DownloadProjectButtonTooltip = memo( variant="link" accessibilityLabel={text} className="action-btn" - icon={bsVersion({ bs5: 'download', bs3: 'cloud-download' })} - bs3Props={{ fw: true }} + icon="download" /> diff --git a/services/web/frontend/js/features/project-list/components/table/cells/action-buttons/leave-project-button.tsx b/services/web/frontend/js/features/project-list/components/table/cells/action-buttons/leave-project-button.tsx index f50797007f..b21d7161f0 100644 --- a/services/web/frontend/js/features/project-list/components/table/cells/action-buttons/leave-project-button.tsx +++ b/services/web/frontend/js/features/project-list/components/table/cells/action-buttons/leave-project-button.tsx @@ -8,7 +8,6 @@ import { Project } from '../../../../../../../../types/project/dashboard/api' import getMeta from '@/utils/meta' import OLTooltip from '@/features/ui/components/ol/ol-tooltip' import OLIconButton from '@/features/ui/components/ol/ol-icon-button' -import { bsVersion } from '@/features/utils/bootstrap-5' type LeaveProjectButtonProps = { project: Project @@ -75,8 +74,7 @@ const LeaveProjectButtonTooltip = memo(function LeaveProjectButtonTooltip({ variant="link" accessibilityLabel={text} className="action-btn" - icon={bsVersion({ bs5: 'logout', bs3: 'sign-out' })} - bs3Props={{ fw: true }} + icon="logout" /> diff --git a/services/web/frontend/js/features/project-list/components/table/cells/action-buttons/trash-project-button.tsx b/services/web/frontend/js/features/project-list/components/table/cells/action-buttons/trash-project-button.tsx index f81712a8d2..24c5404331 100644 --- a/services/web/frontend/js/features/project-list/components/table/cells/action-buttons/trash-project-button.tsx +++ b/services/web/frontend/js/features/project-list/components/table/cells/action-buttons/trash-project-button.tsx @@ -7,7 +7,6 @@ import { useProjectListContext } from '../../../../context/project-list-context' import { trashProject } from '../../../../util/api' import OLTooltip from '@/features/ui/components/ol/ol-tooltip' import OLIconButton from '@/features/ui/components/ol/ol-icon-button' -import { bsVersion } from '@/features/utils/bootstrap-5' type TrashProjectButtonProps = { project: Project @@ -75,8 +74,7 @@ const TrashProjectButtonTooltip = memo(function TrashProjectButtonTooltip({ variant="link" accessibilityLabel={text} className="action-btn" - icon={bsVersion({ bs5: 'delete', bs3: 'trash' })} - bs3Props={{ fw: true }} + icon="delete" /> diff --git a/services/web/frontend/js/features/project-list/components/table/cells/action-buttons/unarchive-project-button.tsx b/services/web/frontend/js/features/project-list/components/table/cells/action-buttons/unarchive-project-button.tsx index 17aec589cf..93e19f6b63 100644 --- a/services/web/frontend/js/features/project-list/components/table/cells/action-buttons/unarchive-project-button.tsx +++ b/services/web/frontend/js/features/project-list/components/table/cells/action-buttons/unarchive-project-button.tsx @@ -5,7 +5,6 @@ import { useProjectListContext } from '../../../../context/project-list-context' import { unarchiveProject } from '../../../../util/api' import OLTooltip from '@/features/ui/components/ol/ol-tooltip' import OLIconButton from '@/features/ui/components/ol/ol-icon-button' -import { bsVersion } from '@/features/utils/bootstrap-5' type UnarchiveProjectButtonProps = { project: Project @@ -54,8 +53,7 @@ const UnarchiveProjectButtonTooltip = memo( variant="link" accessibilityLabel={text} className="action-btn" - icon={bsVersion({ bs5: 'restore_page', bs3: 'reply' })} - bs3Props={{ fw: true }} + icon="restore_page" /> diff --git a/services/web/frontend/js/features/project-list/components/table/cells/action-buttons/untrash-project-button.tsx b/services/web/frontend/js/features/project-list/components/table/cells/action-buttons/untrash-project-button.tsx index 0e56ba6f98..2d9956d707 100644 --- a/services/web/frontend/js/features/project-list/components/table/cells/action-buttons/untrash-project-button.tsx +++ b/services/web/frontend/js/features/project-list/components/table/cells/action-buttons/untrash-project-button.tsx @@ -5,7 +5,6 @@ import { useProjectListContext } from '../../../../context/project-list-context' import { untrashProject } from '../../../../util/api' import OLTooltip from '@/features/ui/components/ol/ol-tooltip' import OLIconButton from '@/features/ui/components/ol/ol-icon-button' -import { bsVersion } from '@/features/utils/bootstrap-5' type UntrashProjectButtonProps = { project: Project @@ -53,8 +52,7 @@ const UntrashProjectButtonTooltip = memo(function UntrashProjectButtonTooltip({ variant="link" accessibilityLabel={text} className="action-btn" - icon={bsVersion({ bs5: 'restore_page', bs3: 'reply' })} - bs3Props={{ fw: true }} + icon="restore_page" /> diff --git a/services/web/frontend/js/features/project-list/components/table/cells/inline-tags.tsx b/services/web/frontend/js/features/project-list/components/table/cells/inline-tags.tsx index 91541ec832..b3b6fec9cb 100644 --- a/services/web/frontend/js/features/project-list/components/table/cells/inline-tags.tsx +++ b/services/web/frontend/js/features/project-list/components/table/cells/inline-tags.tsx @@ -1,13 +1,10 @@ -import { useCallback, useState, useRef } from 'react' +import { useCallback } from 'react' import { useTranslation } from 'react-i18next' import { Tag as TagType } from '../../../../../../../app/src/Features/Tags/types' -import Icon from '../../../../../shared/components/icon' import { useProjectListContext } from '../../../context/project-list-context' import { removeProjectFromTag } from '../../../util/api' -import classnames from 'classnames' import { getTagColor } from '../../../util/tag' import Tag from '@/features/ui/components/bootstrap-5/tag' -import BootstrapVersionSwitcher from '@/features/ui/components/bootstrap-5/bootstrap-version-switcher' type InlineTagsProps = { projectId: string @@ -36,17 +33,6 @@ type InlineTagProps = { function InlineTag({ tag, projectId }: InlineTagProps) { const { t } = useTranslation() const { selectTag, removeProjectFromTagInView } = useProjectListContext() - const [classNames, setClassNames] = useState('') - const tagLabelRef = useRef(null) - const tagBtnRef = useRef(null) - - const handleLabelClick = (e: React.MouseEvent) => { - // trigger the click on the button only when the event - // is triggered from the wrapper element - if (e.target === tagLabelRef.current) { - tagBtnRef.current?.click() - } - } const handleRemoveTag = useCallback( async (tagId: string, projectId: string) => { @@ -55,66 +41,25 @@ function InlineTag({ tag, projectId }: InlineTagProps) { }, [removeProjectFromTagInView] ) - const handleCloseMouseOver = () => setClassNames('tag-label-close-hover') - const handleCloseMouseOut = () => setClassNames('') - return ( - - - {/* eslint-disable-next-line jsx-a11y/mouse-events-have-key-events */} - -
    + } - bs5={ - - } - contentProps={{ - 'aria-label': t('select_tag', { tagName: tag.name }), - onClick: () => selectTag(tag._id), - }} - closeBtnProps={{ - onClick: () => handleRemoveTag(tag._id, projectId), - }} - className="ms-2" - > - {tag.name} - - } - /> + contentProps={{ + 'aria-label': t('select_tag', { tagName: tag.name }), + onClick: () => selectTag(tag._id), + }} + closeBtnProps={{ + onClick: () => handleRemoveTag(tag._id, projectId), + }} + className="ms-2" + > + {tag.name} + ) } diff --git a/services/web/frontend/js/features/project-list/components/table/cells/owner-cell.tsx b/services/web/frontend/js/features/project-list/components/table/cells/owner-cell.tsx index 8f8a3ebdd6..b72350cf14 100644 --- a/services/web/frontend/js/features/project-list/components/table/cells/owner-cell.tsx +++ b/services/web/frontend/js/features/project-list/components/table/cells/owner-cell.tsx @@ -1,9 +1,7 @@ import { useTranslation } from 'react-i18next' -import Icon from '../../../../../shared/components/icon' import { getOwnerName } from '../../../util/project' import { Project } from '../../../../../../../types/project/dashboard/api' import OLTooltip from '@/features/ui/components/ol/ol-tooltip' -import BootstrapVersionSwitcher from '@/features/ui/components/bootstrap-5/bootstrap-version-switcher' import MaterialIcon from '@/shared/components/material-icon' type LinkSharingIconProps = { @@ -28,21 +26,10 @@ function LinkSharingIcon({ {/* OverlayTrigger won't fire unless icon is wrapped in a span */} {prependSpace ? ' ' : ''} - - } - bs5={ - - } + diff --git a/services/web/frontend/js/features/project-list/components/table/project-checkbox.tsx b/services/web/frontend/js/features/project-list/components/table/project-checkbox.tsx index 9d06c6fcea..80a6574e6b 100644 --- a/services/web/frontend/js/features/project-list/components/table/project-checkbox.tsx +++ b/services/web/frontend/js/features/project-list/components/table/project-checkbox.tsx @@ -23,7 +23,6 @@ export const ProjectCheckbox = memo<{ projectId: string; projectName: string }>( checked={selectedProjectIds.has(projectId)} aria-label={t('select_project', { project: projectName })} data-project-id={projectId} - bs3Props={{ bsClass: 'dash-cell-checkbox-wrapper' }} /> ) } diff --git a/services/web/frontend/js/features/project-list/components/table/project-list-table-row.tsx b/services/web/frontend/js/features/project-list/components/table/project-list-table-row.tsx index 59d1a1cf6d..70a13b4a8c 100644 --- a/services/web/frontend/js/features/project-list/components/table/project-list-table-row.tsx +++ b/services/web/frontend/js/features/project-list/components/table/project-list-table-row.tsx @@ -8,8 +8,6 @@ import { getOwnerName } from '../../util/project' import { Project } from '../../../../../../types/project/dashboard/api' import { ProjectCheckbox } from './project-checkbox' import { ProjectListOwnerName } from '@/features/project-list/components/table/project-list-owner-name' -import { bsVersion } from '@/features/utils/bootstrap-5' -import classnames from 'classnames' type ProjectListTableRowProps = { project: Project @@ -19,79 +17,32 @@ function ProjectListTableRow({ project, selected }: ProjectListTableRowProps) { const ownerName = getOwnerName(project) return ( - - + + {project.name}{' '} - + - + {ownerName ? : null} - + - + - + -
    +
    -
    +
    diff --git a/services/web/frontend/js/features/project-list/components/table/project-list-table.tsx b/services/web/frontend/js/features/project-list/components/table/project-list-table.tsx index 0a3302b343..ae1341270e 100644 --- a/services/web/frontend/js/features/project-list/components/table/project-list-table.tsx +++ b/services/web/frontend/js/features/project-list/components/table/project-list-table.tsx @@ -1,6 +1,5 @@ import { useCallback, useRef, useEffect } from 'react' import { useTranslation } from 'react-i18next' -import Icon from '../../../../shared/components/icon' import ProjectListTableRow from './project-list-table-row' import { useProjectListContext } from '../../context/project-list-context' import useSort from '../../hooks/use-sort' @@ -8,30 +7,16 @@ import withContent, { SortBtnProps } from '../sort/with-content' import OLTable from '@/features/ui/components/ol/ol-table' import OLFormCheckbox from '@/features/ui/components/ol/ol-form-checkbox' import MaterialIcon from '@/shared/components/material-icon' -import BootstrapVersionSwitcher from '@/features/ui/components/bootstrap-5/bootstrap-version-switcher' -import { bsVersion } from '@/features/utils/bootstrap-5' -import classnames from 'classnames' function SortBtn({ onClick, text, iconType, screenReaderText }: SortBtnProps) { return ( ) } @@ -66,26 +51,11 @@ function ProjectListTable() { return ( - - {t('projects_list')} - - + {t('projects_list')} + @@ -123,22 +89,13 @@ function ProjectListTable() { /> {t('date_and_owner')} handleSort('lastUpdated')} /> - + {t('tags')} diff --git a/services/web/frontend/js/features/project-list/components/table/project-tools/buttons/archive-projects-button.tsx b/services/web/frontend/js/features/project-list/components/table/project-tools/buttons/archive-projects-button.tsx index b60dd3402e..1085cbd198 100644 --- a/services/web/frontend/js/features/project-list/components/table/project-tools/buttons/archive-projects-button.tsx +++ b/services/web/frontend/js/features/project-list/components/table/project-tools/buttons/archive-projects-button.tsx @@ -7,7 +7,6 @@ import useIsMounted from '../../../../../../shared/hooks/use-is-mounted' import { useProjectListContext } from '../../../../context/project-list-context' import { archiveProject } from '../../../../util/api' import { Project } from '../../../../../../../../types/project/dashboard/api' -import { bsVersion } from '@/features/utils/bootstrap-5' function ArchiveProjectsButton() { const { selectedProjects, toggleSelectedProject, updateProjectViewData } = @@ -50,7 +49,7 @@ function ArchiveProjectsButton() { onClick={handleOpenModal} variant="secondary" accessibilityLabel={text} - icon={bsVersion({ bs5: 'inbox', bs3: 'inbox' })} + icon="inbox" /> ) diff --git a/services/web/frontend/js/features/project-list/components/table/project-tools/buttons/project-tools-more-dropdown-button.tsx b/services/web/frontend/js/features/project-list/components/table/project-tools/buttons/project-tools-more-dropdown-button.tsx index 9e10a3474b..0e05aa984c 100644 --- a/services/web/frontend/js/features/project-list/components/table/project-tools/buttons/project-tools-more-dropdown-button.tsx +++ b/services/web/frontend/js/features/project-list/components/table/project-tools/buttons/project-tools-more-dropdown-button.tsx @@ -1,7 +1,5 @@ import { memo } from 'react' -import { Dropdown as BS3Dropdown } from 'react-bootstrap' import { useTranslation } from 'react-i18next' -import ControlledDropdown from '../../../../../../shared/components/controlled-dropdown' import CopyProjectMenuItem from '../menu-items/copy-project-menu-item' import RenameProjectMenuItem from '../menu-items/rename-project-menu-item' import { @@ -9,46 +7,24 @@ import { DropdownMenu, DropdownToggle, } from '@/features/ui/components/bootstrap-5/dropdown-menu' -import BootstrapVersionSwitcher from '@/features/ui/components/bootstrap-5/bootstrap-version-switcher' function ProjectToolsMoreDropdownButton() { const { t } = useTranslation() return ( - - - {t('more')} - - - - - - - } - bs5={ - - - {t('more')} - - -
  • - -
  • -
  • - -
  • -
    -
    - } - /> + + + {t('more')} + + +
  • + +
  • +
  • + +
  • +
    +
    ) } diff --git a/services/web/frontend/js/features/project-list/components/table/project-tools/buttons/tags-dropdown.tsx b/services/web/frontend/js/features/project-list/components/table/project-tools/buttons/tags-dropdown.tsx index 3bb86dce78..a67663f1f0 100644 --- a/services/web/frontend/js/features/project-list/components/table/project-tools/buttons/tags-dropdown.tsx +++ b/services/web/frontend/js/features/project-list/components/table/project-tools/buttons/tags-dropdown.tsx @@ -1,15 +1,11 @@ import { sortBy } from 'lodash' import { memo, useCallback } from 'react' -import { Button, Dropdown as BS3Dropdown } from 'react-bootstrap' import { useTranslation } from 'react-i18next' -import ControlledDropdown from '../../../../../../shared/components/controlled-dropdown' -import Icon from '../../../../../../shared/components/icon' import MaterialIcon from '../../../../../../shared/components/material-icon' import { useProjectListContext } from '../../../../context/project-list-context' import useTag from '../../../../hooks/use-tag' import { addProjectsToTag, removeProjectsFromTag } from '../../../../util/api' import { getTagColor } from '../../../../util/tag' -import BootstrapVersionSwitcher from '@/features/ui/components/bootstrap-5/bootstrap-version-switcher' import { Dropdown, DropdownDivider, @@ -79,140 +75,62 @@ function TagsDropdown() { [selectedProjects] ) - const containsSomeSelectedProjects = useCallback( - tag => { - for (const project of selectedProjects) { - if (tag.project_ids?.includes(project.id)) { - return true - } - } - return false - }, - [selectedProjects] - ) - return ( <> - - + + + + + {t('add_to_tag')} + {sortBy(tags, tag => tag.name?.toLowerCase()).map((tag, index) => ( +
  • + + containsAllSelectedProjects(tag) + ? handleRemoveTagFromSelectedProjects(e, tag._id) + : handleAddTagToSelectedProjects(e, tag._id) + } + aria-label={t('add_or_remove_project_from_tag', { + tagName: tag.name, + })} + as="button" + tabIndex={-1} + leadingIcon={ + containsAllSelectedProjects(tag) ? ( + 'check' + ) : ( + + ) + } + > + + {tag.name} + +
  • + ))} + +
  • + - - - -
  • - {t('add_to_tag')} -
  • - {sortBy(tags, tag => tag.name?.toLowerCase()).map(tag => { - return ( -
  • - -
  • - ) - })} -
  • -
  • - -
  • - - - } - bs5={ - - - - - - {t('add_to_tag')} - {sortBy(tags, tag => tag.name?.toLowerCase()).map( - (tag, index) => ( -
  • - - containsAllSelectedProjects(tag) - ? handleRemoveTagFromSelectedProjects(e, tag._id) - : handleAddTagToSelectedProjects(e, tag._id) - } - aria-label={t('add_or_remove_project_from_tag', { - tagName: tag.name, - })} - as="button" - tabIndex={-1} - leadingIcon={ - containsAllSelectedProjects(tag) ? ( - 'check' - ) : ( - - ) - } - > - - {tag.name} - -
  • - ) - )} - -
  • - - {t('create_new_tag')} - -
  • -
    -
    - } - /> + {t('create_new_tag')} + + +
    + ) diff --git a/services/web/frontend/js/features/project-list/components/table/project-tools/buttons/trash-projects-button.tsx b/services/web/frontend/js/features/project-list/components/table/project-tools/buttons/trash-projects-button.tsx index dc56b6b0ab..07dbd68527 100644 --- a/services/web/frontend/js/features/project-list/components/table/project-tools/buttons/trash-projects-button.tsx +++ b/services/web/frontend/js/features/project-list/components/table/project-tools/buttons/trash-projects-button.tsx @@ -7,7 +7,6 @@ import useIsMounted from '../../../../../../shared/hooks/use-is-mounted' import { useProjectListContext } from '../../../../context/project-list-context' import { trashProject } from '../../../../util/api' import { Project } from '../../../../../../../../types/project/dashboard/api' -import { bsVersion } from '@/features/utils/bootstrap-5' function TrashProjectsButton() { const { selectedProjects, toggleSelectedProject, updateProjectViewData } = @@ -50,7 +49,7 @@ function TrashProjectsButton() { onClick={handleOpenModal} variant="secondary" accessibilityLabel={text} - icon={bsVersion({ bs5: 'delete', bs3: 'trash' })} + icon="delete" /> void @@ -39,143 +35,63 @@ function TagsList({ onTagClick, onEditClick }: TagsListProps) { return ( <> - - {sortBy(tags, ['name']).map((tag, index) => ( - - handleClick(e as unknown as React.MouseEvent, tag) - } - className="projects-types-menu-item projects-types-menu-tag-item" - afterNode={ - - } - > - - {selectedTagId === tag._id ? ( - - ) : null} - - - - - {tag.name} - ({tag.project_ids?.length}) - + <> + {sortBy(tags, ['name']).map((tag, index) => ( +
  • + handleClick(e as unknown as React.MouseEvent, tag)} + leadingIcon={ + + - - ))} - { - selectTag(UNCATEGORIZED_KEY) - onTagClick?.() - }} + } + trailingIcon={selectedTagId === tag._id ? 'check' : undefined} + active={selectedTagId === tag._id} > - {selectedTagId === UNCATEGORIZED_KEY ? ( - - ) : null} - - {t('uncategorized')}  - ({untaggedProjectsCount}) + + {tag.name} ({tag.project_ids?.length}) - - { - openCreateTagModal() - onTagClick?.() + + { + e.stopPropagation() + handleManageTag(e, tag._id) }} - className="projects-types-menu-item" + aria-label={t('edit_tag')} > - - - {t('new_tag')} - - - - } - bs5={ - <> - {sortBy(tags, ['name']).map((tag, index) => ( -
  • - - handleClick(e as unknown as React.MouseEvent, tag) - } - leadingIcon={ - - - - } - trailingIcon={selectedTagId === tag._id ? 'check' : undefined} - active={selectedTagId === tag._id} - > - - {tag.name} ({tag.project_ids?.length}) - - - { - e.stopPropagation() - handleManageTag(e, tag._id) - }} - aria-label={t('edit_tag')} - > - - -
  • - ))} -
  • - selectTag(UNCATEGORIZED_KEY)} - trailingIcon={ - selectedTagId === UNCATEGORIZED_KEY ? 'check' : undefined - } - active={selectedTagId === UNCATEGORIZED_KEY} - > - {t('uncategorized')} ({untaggedProjectsCount}) - -
  • -
  • - - {t('new_tag')} - -
  • - - } - /> + + + + ))} +
  • + selectTag(UNCATEGORIZED_KEY)} + trailingIcon={ + selectedTagId === UNCATEGORIZED_KEY ? 'check' : undefined + } + active={selectedTagId === UNCATEGORIZED_KEY} + > + {t('uncategorized')} ({untaggedProjectsCount}) + +
  • +
  • + + {t('new_tag')} + +
  • + diff --git a/services/web/frontend/js/features/project-list/components/welcome-message-new/welcome-message-create-new-project-dropdown.tsx b/services/web/frontend/js/features/project-list/components/welcome-message-new/welcome-message-create-new-project-dropdown.tsx index be5455b7c1..0d6f7781b0 100644 --- a/services/web/frontend/js/features/project-list/components/welcome-message-new/welcome-message-create-new-project-dropdown.tsx +++ b/services/web/frontend/js/features/project-list/components/welcome-message-new/welcome-message-create-new-project-dropdown.tsx @@ -1,4 +1,4 @@ -import { useCallback, useState, forwardRef } from 'react' +import { useCallback, forwardRef } from 'react' import { useTranslation } from 'react-i18next' import { sendMB } from '../../../../infrastructure/event-tracking' import getMeta from '../../../../utils/meta' @@ -11,7 +11,6 @@ import { DropdownMenu, DropdownToggle, } from '@/features/ui/components/bootstrap-5/dropdown-menu' -import BootstrapVersionSwitcher from '@/features/ui/components/bootstrap-5/bootstrap-version-switcher' const CustomDropdownToggle = forwardRef< HTMLButtonElement, @@ -57,36 +56,11 @@ type WelcomeMessageCreateNewProjectDropdownProps = { function WelcomeMessageCreateNewProjectDropdown({ setActiveModal, }: WelcomeMessageCreateNewProjectDropdownProps) { - const [showDropdown, setShowDropdown] = useState(false) const { t } = useTranslation() const portalTemplates = getMeta('ol-portalTemplates') || [] const { isOverleaf } = getMeta('ol-ExposedSettings') - const handleClick = useCallback(() => { - sendMB('welcome-page-create-first-project-click', { - dropdownMenu: 'main-button', - dropdownOpen: showDropdown, - }) - - // toggle the dropdown - setShowDropdown(!showDropdown) - }, [setShowDropdown, showDropdown]) - - const handleKeyDown = useCallback( - (e: React.KeyboardEvent) => { - if (e.code === 'Enter') { - handleClick() - } else if (e.code === 'Space') { - handleClick() - - // prevent page down when pressing space - e.preventDefault() - } - }, - [handleClick] - ) - const handleDropdownItemClick = useCallback( ( e: React.MouseEvent, @@ -96,15 +70,13 @@ function WelcomeMessageCreateNewProjectDropdown({ // prevent firing the main dropdown onClick event e.stopPropagation() - setShowDropdown(false) - sendMB('welcome-page-create-first-project-click', { dropdownOpen: true, dropdownMenu: dropdownMenuEvent, }) setActiveModal(modalVariant) }, - [setActiveModal, setShowDropdown] + [setActiveModal] ) const handlePortalTemplateClick = useCallback( @@ -112,187 +84,91 @@ function WelcomeMessageCreateNewProjectDropdown({ // prevent firing the main dropdown onClick event e.stopPropagation() - setShowDropdown(false) - sendMB('welcome-page-create-first-project-click', { dropdownMenu: 'institution-template', dropdownOpen: true, institutionTemplateName, }) }, - [setShowDropdown] + [] ) return ( - -
    + + +
  • + + handleDropdownItemClick(e, 'blank_project', 'blank-project') + } + tabIndex={-1} > -

    {t('create_a_new_project')}

    - - {showDropdown && ( -
    - - - - {isOverleaf && ( - - )} - {portalTemplates.length > 0 ? ( - <> -
    -
    - {t('institution_templates')} -
    - {portalTemplates?.map((portalTemplate, index) => ( - - handlePortalTemplateClick(e, portalTemplate.name) - } - > - {portalTemplate.name} - - ))} - - ) : null} -
    - )} -
  • -
    - } - bs5={ - - - -
  • + {t('blank_project')} + +
  • +
  • + + handleDropdownItemClick(e, 'example_project', 'example-project') + } + tabIndex={-1} + > + {t('example_project')} + +
  • +
  • + + handleDropdownItemClick(e, 'upload_project', 'upload-project') + } + tabIndex={-1} + > + {t('upload_project')} + +
  • + {isOverleaf && ( +
  • + + handleDropdownItemClick( + e, + 'import_from_github', + 'import-from-github' + ) + } + tabIndex={-1} + > + {t('import_from_github')} + +
  • + )} + {(portalTemplates?.length ?? 0) > 0 ? ( + <> + + + {portalTemplates?.map((portalTemplate, index) => ( - handleDropdownItemClick(e, 'blank_project', 'blank-project') - } - tabIndex={-1} + key={`portal-template-${index}`} + onClick={e => handlePortalTemplateClick(e, portalTemplate.name)} + href={`${portalTemplate.url}#templates`} > - {t('blank_project')} + {portalTemplate.name} - -
  • - - handleDropdownItemClick( - e, - 'example_project', - 'example-project' - ) - } - tabIndex={-1} - > - {t('example_project')} - -
  • -
  • - - handleDropdownItemClick(e, 'upload_project', 'upload-project') - } - tabIndex={-1} - > - {t('upload_project')} - -
  • - {isOverleaf && ( -
  • - - handleDropdownItemClick( - e, - 'import_from_github', - 'import-from-github' - ) - } - tabIndex={-1} - > - {t('import_from_github')} - -
  • - )} - {(portalTemplates?.length ?? 0) > 0 ? ( - <> - - - {portalTemplates?.map((portalTemplate, index) => ( - - handlePortalTemplateClick(e, portalTemplate.name) - } - href={`${portalTemplate.url}#templates`} - > - {portalTemplate.name} - - ))} - - ) : null} -
    -
    - } - /> + ))} + + ) : null} + + ) } diff --git a/services/web/test/frontend/features/project-list/components/new-project-button.test.tsx b/services/web/test/frontend/features/project-list/components/new-project-button.test.tsx index 1629655973..93c7a68e3a 100644 --- a/services/web/test/frontend/features/project-list/components/new-project-button.test.tsx +++ b/services/web/test/frontend/features/project-list/components/new-project-button.test.tsx @@ -117,7 +117,7 @@ describe('', function () { // dynamic menu based on portalTemplates const affiliationTemplate = screen.getByRole('menuitem', { - name: 'Affiliation 1', + name: 'Affiliation 1 Template', }) expect(affiliationTemplate.getAttribute('href')).to.equal( '/edu/test-new-template#templates' diff --git a/services/web/test/frontend/features/project-list/components/notifications.test.tsx b/services/web/test/frontend/features/project-list/components/notifications.test.tsx index 9965a440ea..1f0bdadf93 100644 --- a/services/web/test/frontend/features/project-list/components/notifications.test.tsx +++ b/services/web/test/frontend/features/project-list/components/notifications.test.tsx @@ -1,5 +1,5 @@ import { expect } from 'chai' -import sinon from 'sinon' +import sinon, { SinonStub } from 'sinon' import { fireEvent, render, @@ -46,6 +46,7 @@ import { individualSubscription, } from '../fixtures/user-subscriptions' import getMeta from '@/utils/meta' +import * as bootstrapUtils from '@/features/utils/bootstrap-5' const renderWithinProjectListProvider = (Component: React.ComponentType) => { render(, { @@ -65,6 +66,16 @@ describe('', function () { appName: 'Overleaf', } + let isBootstrap5Stub: SinonStub + + before(function () { + isBootstrap5Stub = sinon.stub(bootstrapUtils, 'isBootstrap5').returns(true) + }) + + after(function () { + isBootstrap5Stub.restore() + }) + beforeEach(function () { fetchMock.reset() @@ -127,15 +138,15 @@ describe('', function () { expect(joinBtn.disabled).to.be.true - await waitForElementToBeRemoved(() => - screen.getByRole('button', { name: /joining/i }) - ) + screen.debug() + + await waitForElementToBeRemoved(() => screen.getByText('Loading')) expect(acceptMock.called()).to.be.true screen.getByText(/joined/i) expect(screen.queryByRole('button', { name: /join project/i })).to.be.null - const openProject = screen.getByRole('link', { name: /open project/i }) + const openProject = screen.getByRole('button', { name: /open project/i }) expect(openProject.getAttribute('href')).to.equal( `/project/${notificationProjectInvite.messageOpts.projectId}` ) @@ -173,12 +184,12 @@ describe('', function () { fireEvent.click(joinBtn) await waitForElementToBeRemoved(() => - screen.getByRole('button', { name: /joining/i }) + screen.getByRole('button', { name: /loading/i }) ) expect(fetchMock.called()).to.be.true screen.getByRole('button', { name: /join project/i }) - expect(screen.queryByRole('link', { name: /open project/i })).to.be.null + expect(screen.queryByRole('button', { name: /open project/i })).to.be.null }) it('shows WFH2020', async function () { @@ -197,7 +208,7 @@ describe('', function () { screen.getByRole('alert') screen.getByText(/your free WFH2020 upgrade came to an end on/i) - const viewLink = screen.getByRole('link', { name: /view/i }) + const viewLink = screen.getByRole('button', { name: /view/i }) expect(viewLink.getAttribute('href')).to.equal( 'https://www.overleaf.com/events/wfh2020' ) @@ -236,7 +247,7 @@ describe('', function () { expect(findOutMore.getAttribute('href')).to.equal( 'https://www.overleaf.com/learn/how-to/Institutional_Login' ) - const linkAccount = screen.getByRole('link', { name: /link account/i }) + const linkAccount = screen.getByRole('button', { name: /link account/i }) expect(linkAccount.getAttribute('href')).to.equal( `${exposedSettings.samlInitPath}?university_id=${notificationIPMatchedAffiliation.messageOpts.institutionId}&auto=/project` ) @@ -271,7 +282,7 @@ describe('', function () { /add an institutional email address to claim your features/i ) - const addAffiliation = screen.getByRole('link', { + const addAffiliation = screen.getByRole('button', { name: /add affiliation/i, }) expect(addAffiliation.getAttribute('href')).to.equal(`/user/settings`) @@ -297,7 +308,7 @@ describe('', function () { screen.getByText(/file limit/i) screen.getByText(/You can't add more files to the project or sync it/i) - const accountSettings = screen.getByRole('link', { + const accountSettings = screen.getByRole('button', { name: /Open project/i, }) expect(accountSettings.getAttribute('href')).to.equal('/project/123') @@ -487,7 +498,7 @@ describe('', function () { '/learn/how-to/Institutional_Login' ) - const action = screen.getByRole('link', { name: /link account/i }) + const action = screen.getByRole('button', { name: /link account/i }) expect(action.getAttribute('href')).to.equal( `${exposedSettings.samlInitPath}?university_id=${notificationsInstitution.institutionId}&auto=/project&email=${notificationsInstitution.email}` ) @@ -552,7 +563,7 @@ describe('', function () { screen.getByRole('alert') screen.getByText(/which is already registered with/i) - const action = screen.getByRole('link', { name: /find out more/i }) + const action = screen.getByRole('button', { name: /find out more/i }) expect(action.getAttribute('href')).to.equal( '/learn/how-to/Institutional_Login' ) @@ -735,7 +746,7 @@ describe('', function () { screen.getByRole('button', { name: /confirm affiliation/i }) ) - await waitForElementToBeRemoved(() => screen.getByText(/sending/i)) + await waitForElementToBeRemoved(() => screen.getByText(/loading/i)) screen.getByText(/check your email inbox to confirm/i) expect(screen.queryByRole('button', { name: /confirm affiliation/i })).to .be.null @@ -745,7 +756,7 @@ describe('', function () { fireEvent.click( screen.getByRole('button', { name: /resend confirmation email/i }) ) - await waitForElementToBeRemoved(() => screen.getByText(/sending/i)) + await waitForElementToBeRemoved(() => screen.getByText('Loading')) expect(sendReconfirmationMock.calls()).to.have.lengthOf(2) }) @@ -814,7 +825,7 @@ describe('', function () { renderWithinProjectListProvider(GroupsAndEnterpriseBanner) await fetchMock.flush(true) - expect(screen.queryByRole('link', { name: 'Contact Sales' })).to.be.null + expect(screen.queryByRole('button', { name: 'Contact Sales' })).to.be.null }) it('shows the banner for users that have dismissed the previous banners', async function () { @@ -824,7 +835,7 @@ describe('', function () { renderWithinProjectListProvider(GroupsAndEnterpriseBanner) await fetchMock.flush(true) - expect(screen.queryByRole('link', { name: 'Contact Sales' })).to.not.be + expect(screen.queryByRole('button', { name: 'Contact Sales' })).to.not.be .null }) @@ -840,7 +851,7 @@ describe('', function () { renderWithinProjectListProvider(GroupsAndEnterpriseBanner) await fetchMock.flush(true) - expect(screen.queryByRole('link', { name: 'Contact Sales' })).to.not.be + expect(screen.queryByRole('button', { name: 'Contact Sales' })).to.not.be .null }) @@ -856,7 +867,7 @@ describe('', function () { renderWithinProjectListProvider(GroupsAndEnterpriseBanner) await fetchMock.flush(true) - expect(screen.queryByRole('link', { name: 'Contact Sales' })).to.be.null + expect(screen.queryByRole('button', { name: 'Contact Sales' })).to.be.null }) describe('users that are not in group and are not affiliated', function () { @@ -897,7 +908,7 @@ describe('', function () { screen.getByText( 'Overleaf On-Premises: Does your company want to keep its data within its firewall? Overleaf offers Server Pro, an on-premises solution for companies. Get in touch to learn more.' ) - const link = screen.getByRole('link', { name: 'Contact Sales' }) + const link = screen.getByRole('button', { name: 'Contact Sales' }) expect(link.getAttribute('href')).to.equal(`/for/contact-sales-2`) }) @@ -914,7 +925,7 @@ describe('', function () { screen.getByText( 'Why do Fortune 500 companies and top research institutions trust Overleaf to streamline their collaboration? Get in touch to learn more.' ) - const link = screen.getByRole('link', { name: 'Contact Sales' }) + const link = screen.getByRole('button', { name: 'Contact Sales' }) expect(link.getAttribute('href')).to.equal(`/for/contact-sales-4`) }) @@ -941,7 +952,7 @@ describe('', function () { }) it('shows the banner', function () { renderWithinProjectListProvider(UserNotifications) - const ctaLink = screen.getByRole('link', { + const ctaLink = screen.getByRole('button', { name: 'Get Writefull Premium', }) expect(ctaLink.getAttribute('href')).to.equal( @@ -951,7 +962,7 @@ describe('', function () { it('dismisses the banner when the close button is clicked', function () { renderWithinProjectListProvider(UserNotifications) - screen.getByRole('link', { name: /Writefull/ }) + screen.getByRole('button', { name: /Writefull/ }) const WritefullPromoBanner = screen.getByTestId( 'writefull-premium-promo-banner' ) diff --git a/services/web/test/frontend/features/project-list/components/sidebar/tags-list.test.tsx b/services/web/test/frontend/features/project-list/components/sidebar/tags-list.test.tsx index 9795d2ead5..a15d44538a 100644 --- a/services/web/test/frontend/features/project-list/components/sidebar/tags-list.test.tsx +++ b/services/web/test/frontend/features/project-list/components/sidebar/tags-list.test.tsx @@ -158,14 +158,12 @@ describe('', function () { describe('Edit modal', function () { beforeEach(async function () { const tag1Button = screen.getByText('Tag 1') - - const editButton = within( + const dropdownToggle = within( tag1Button.closest('li') as HTMLElement - ).getByRole('button', { - name: 'Edit', - }) - - await fireEvent.click(editButton) + ).getByTestId('tag-dropdown-toggle') + await fireEvent.click(dropdownToggle) + const editMenuItem = await screen.findByRole('menuitem', { name: 'Edit' }) + await fireEvent.click(editMenuItem) }) it('modal is open', async function () { @@ -250,15 +248,15 @@ describe('', function () { describe('Delete modal', function () { beforeEach(async function () { - const tag1Button = screen.getByText('Another tag') - - const deleteButton = within( + const tag1Button = screen.getByText('Tag 1') + const dropdownToggle = within( tag1Button.closest('li') as HTMLElement - ).getByRole('button', { + ).getByTestId('tag-dropdown-toggle') + await fireEvent.click(dropdownToggle) + const deleteMenuItem = await screen.findByRole('menuitem', { name: 'Delete', }) - - await fireEvent.click(deleteButton) + await fireEvent.click(deleteMenuItem) }) it('modal is open', async function () { diff --git a/services/web/test/frontend/features/project-list/components/table/project-tools/project-tools.test.tsx b/services/web/test/frontend/features/project-list/components/table/project-tools/project-tools.test.tsx index 0922dfd699..cc2cee1d0a 100644 --- a/services/web/test/frontend/features/project-list/components/table/project-tools/project-tools.test.tsx +++ b/services/web/test/frontend/features/project-list/components/table/project-tools/project-tools.test.tsx @@ -11,13 +11,22 @@ describe('', function () { resetProjectListContextFetch() }) - it('renders the project tools for the all projects filter', function () { + it('renders the project tools for the all projects filter', async function () { renderWithProjectListContext() - expect(screen.getAllByRole('button').length).to.equal(5) - screen.getByLabelText('Download') - screen.getByLabelText('Archive') - screen.getByLabelText('Trash') - screen.getByLabelText('Tags') - screen.getByRole('button', { name: 'Create new tag' }) + + const initialButtons = screen.getAllByRole('button') + expect(initialButtons).to.have.length(4) + + expect(screen.getByLabelText('Download')).to.exist + expect(screen.getByLabelText('Archive')).to.exist + expect(screen.getByLabelText('Trash')).to.exist + expect(screen.getByLabelText('Tags')).to.exist + + expect(screen.queryByText('Create new tag')).to.not.exist + + screen.getByLabelText('Tags').click() + + const createTagButton = await screen.findByText('Create new tag') + expect(createTagButton).to.exist }) }) diff --git a/services/web/test/frontend/features/project-list/components/welcome-message.test.tsx b/services/web/test/frontend/features/project-list/components/welcome-message.test.tsx index c6eb315196..064a488e3c 100644 --- a/services/web/test/frontend/features/project-list/components/welcome-message.test.tsx +++ b/services/web/test/frontend/features/project-list/components/welcome-message.test.tsx @@ -61,7 +61,7 @@ describe('', function () { screen.getByText('Institution Templates') // dynamic menu based on portalTemplates - const affiliationTemplate = screen.getByRole('link', { + const affiliationTemplate = screen.getByRole('menuitem', { name: 'Affiliation 1', })