{hiddenProjectsCount > 0 ? (
-
+ <>
+
loadMoreProjects()}
+ >
+ {t('show_x_more_projects', { x: loadMoreCount })}
+
+ >
) : null}
{hiddenProjectsCount > 0 ? (
@@ -33,15 +34,13 @@ export default function LoadMore() {
n: visibleProjects.length + hiddenProjectsCount,
})}
{' '}
-
+ {t('show_all_projects')}
+
>
) : (
diff --git a/services/web/frontend/js/features/project-list/components/modals/projects-action-modal.tsx b/services/web/frontend/js/features/project-list/components/modals/projects-action-modal.tsx
index e658584cb4..95b1314ceb 100644
--- a/services/web/frontend/js/features/project-list/components/modals/projects-action-modal.tsx
+++ b/services/web/frontend/js/features/project-list/components/modals/projects-action-modal.tsx
@@ -1,5 +1,4 @@
import { memo, useEffect, useState } from 'react'
-import { Modal } from 'react-bootstrap'
import { useTranslation } from 'react-i18next'
import { Project } from '../../../../../../types/project/dashboard/api'
import { getUserFacingMessage } from '../../../../infrastructure/fetch-json'
@@ -12,6 +11,7 @@ import OLModal, {
OLModalBody,
OLModalFooter,
OLModalHeader,
+ OLModalTitle,
} from '@/features/ui/components/ol/ol-modal'
type ProjectsActionModalProps = {
@@ -80,7 +80,7 @@ function ProjectsActionModal({
backdrop="static"
>
- {title}
+ {title}
{children}
diff --git a/services/web/frontend/js/features/project-list/components/modals/projects-list.tsx b/services/web/frontend/js/features/project-list/components/modals/projects-list.tsx
index 77592d7057..6a94a312d7 100644
--- a/services/web/frontend/js/features/project-list/components/modals/projects-list.tsx
+++ b/services/web/frontend/js/features/project-list/components/modals/projects-list.tsx
@@ -8,7 +8,7 @@ type ProjectsToDisplayProps = {
function ProjectsList({ projects, projectsToDisplay }: ProjectsToDisplayProps) {
return (
-
+
{projectsToDisplay.map(project => (
- void
@@ -82,24 +85,22 @@ function RenameProjectModal({
]
)
- const handleOnChange = (
- event: React.ChangeEvent
- ) => {
+ const handleOnChange = (event: React.ChangeEvent) => {
setNewProjectName(event.target.value)
}
return (
-
-
- {t('rename_project')}
-
-
+
+ {t('rename_project')}
+
+
{isError && (
)}
-
-
-
-
+
+
{t('cancel')}
-
-
-
-
+
+
+
)
}
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 9e5355a70f..9f280eee99 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
@@ -4,7 +4,6 @@ import {
} from '../context/project-list-context'
import { ColorPickerProvider } from '../context/color-picker-context'
import * as eventTracking from '../../../infrastructure/event-tracking'
-import { Col, Row } from 'react-bootstrap'
import { useTranslation } from 'react-i18next'
import useWaitForI18n from '../../../shared/hooks/use-wait-for-i18n'
import CurrentPlanWidget from './current-plan-widget/current-plan-widget'
@@ -30,6 +29,9 @@ 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() {
const { isReady } = useWaitForI18n()
@@ -75,6 +77,32 @@ function ProjectListPageContent() {
const { t } = useTranslation()
+ const tableTopArea = (
+
+ )
+
return isLoading ? (
@@ -94,41 +122,62 @@ function ProjectListPageContent() {
{error ?
: ''}
-
-
+
+
-
-
+
+
-
+
{selectedProjects.length === 0 ? (
) : (
)}
-
-
-
+
+
-
-
+
+
-
+
-
-
-
-
-
-
-
+
+
+
+ {tableTopArea}
+
-
-
-
-
-
-
-
+ }
+ bs5={
+
+ {tableTopArea}
+
+
+ }
+ />
+
+
+
+
-
-
+
+
>
) : (
{error ?
: ''}
-
+
-
+
-
+
-
+
)}
@@ -201,7 +249,7 @@ function ProjectListPageContent() {
function DashApiError() {
const { t } = useTranslation()
return (
-
+
-
+
)
}
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 15e33a8b08..88226f649c 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,5 +1,6 @@
import { useTranslation } from 'react-i18next'
import { Sort } from '../../../../../../types/project/dashboard/api'
+import { bsVersion } from '@/features/utils/bootstrap-5'
type SortBtnOwnProps = {
column: string
@@ -26,7 +27,10 @@ function withContent
(
let screenReaderText = t('sort_by_x', { x: text })
if (column === sort.by) {
- iconType = sort.order === 'asc' ? 'caret-up' : 'caret-down'
+ iconType =
+ sort.order === 'asc'
+ ? bsVersion({ bs5: 'arrow_upward_alt', bs3: 'caret-up' })
+ : bsVersion({ bs5: 'arrow_downward_alt', bs3: 'caret-down' })
screenReaderText = t('reverse_x_sort_order', { x: text })
}
diff --git a/services/web/frontend/js/features/project-list/components/table/cells/action-buttons/archive-project-button.tsx b/services/web/frontend/js/features/project-list/components/table/cells/action-buttons/archive-project-button.tsx
index 7639473b4d..14b8bd4c8c 100644
--- a/services/web/frontend/js/features/project-list/components/table/cells/action-buttons/archive-project-button.tsx
+++ b/services/web/frontend/js/features/project-list/components/table/cells/action-buttons/archive-project-button.tsx
@@ -1,12 +1,13 @@
import { useTranslation } from 'react-i18next'
import { Project } from '../../../../../../../../types/project/dashboard/api'
import { memo, useCallback, useState } from 'react'
-import Icon from '../../../../../../shared/components/icon'
-import Tooltip from '../../../../../../shared/components/tooltip'
import ArchiveProjectModal from '../../../modals/archive-project-modal'
import useIsMounted from '../../../../../../shared/hooks/use-is-mounted'
import { useProjectListContext } from '../../../../context/project-list-context'
import { archiveProject } 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 ArchiveProjectButtonProps = {
project: Project
@@ -65,20 +66,28 @@ const ArchiveProjectButtonTooltip = memo(function ArchiveProjectButtonTooltip({
return (
{(text, handleOpenModal) => (
-
-
-
+
+
+
+
)}
)
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 b45105dce4..2d17174cd3 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
@@ -2,21 +2,31 @@ 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 Tooltip from '../../../../../../shared/components/tooltip'
import * as eventTracking from '../../../../../../infrastructure/event-tracking'
import { useLocation } from '../../../../../../shared/hooks/use-location'
import useAbortController from '../../../../../../shared/hooks/use-abort-controller'
import { postJSON } from '../../../../../../infrastructure/fetch-json'
-import AccessibleModal from '../../../../../../shared/components/accessible-modal'
-import { Button, Modal } from 'react-bootstrap'
import { isSmallDevice } from '../../../../../../infrastructure/event-tracking'
+import OLTooltip from '@/features/ui/components/ol/ol-tooltip'
+import OLButton from '@/features/ui/components/ol/ol-button'
+import OLModal, {
+ OLModalBody,
+ OLModalFooter,
+ 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 = {
project: Project
children: (
text: string,
pendingDownload: boolean,
- downloadProject: (fn: () => void) => void
+ downloadProject: (
+ e?: T,
+ fn?: (e?: T) => void
+ ) => void
) => React.ReactElement
}
@@ -31,7 +41,7 @@ function CompileAndDownloadProjectPDFButton({
const [pendingCompile, setPendingCompile] = useState(false)
const downloadProject = useCallback(
- onDone => {
+ (e?: T, onDone?: (e?: T) => void) => {
setPendingCompile(pendingCompile => {
if (pendingCompile) return true
eventTracking.sendMB('project-list-page-interaction', {
@@ -72,7 +82,7 @@ function CompileAndDownloadProjectPDFButton({
location.assign(
`/download/project/${project.id}/build/${outputFile.build}/output/output.pdf?${params}`
)
- onDone()
+ onDone?.(e)
} else {
setShowErrorModal(true)
}
@@ -111,19 +121,19 @@ function CompileErrorModal({
const { t } = useTranslation()
return (
<>
-
-
-
+
+
+
{project.name}: {t('pdf_unavailable_for_download')}
-
-
- {t('generic_linked_file_compile_error')}
-
-
-
-
-
-
+
+
+ {t('generic_linked_file_compile_error')}
+
+
+ {t('open_project')}
+
+
+
>
)
}
@@ -135,24 +145,35 @@ const CompileAndDownloadProjectPDFButtonTooltip = memo(
return (
{(text, pendingCompile, compileAndDownloadProject) => (
-
-
-
+
+
+ ) : null,
+ }}
+ />
+
+
)}
)
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 e753848adb..e5184eac12 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
@@ -1,7 +1,5 @@
import { memo, useCallback, useState } from 'react'
import { useTranslation } from 'react-i18next'
-import Icon from '../../../../../../shared/components/icon'
-import Tooltip from '../../../../../../shared/components/tooltip'
import CloneProjectModal from '../../../../../clone-project-modal/components/clone-project-modal'
import useIsMounted from '../../../../../../shared/hooks/use-is-mounted'
import { useProjectListContext } from '../../../../context/project-list-context'
@@ -12,14 +10,18 @@ import {
} from '../../../../../../../../types/project/dashboard/api'
import { useProjectTags } from '@/features/project-list/hooks/use-project-tags'
import { isSmallDevice } from '../../../../../../infrastructure/event-tracking'
-
-type HandleOpenModal = (fn?: () => void) => void
+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
children: (
text: string,
- handleOpenModal: HandleOpenModal
+ handleOpenModal: (
+ e?: T,
+ fn?: (e?: T) => void
+ ) => void
) => React.ReactElement
}
@@ -37,9 +39,9 @@ function CopyProjectButton({ project, children }: CopyButtonProps) {
const projectTags = useProjectTags(project.id)
const handleOpenModal = useCallback(
- (onOpen?: Parameters[0]) => {
+ (e?: T, onOpen?: (e?: T) => void) => {
setShowModal(true)
- onOpen?.()
+ onOpen?.(e)
},
[]
)
@@ -97,20 +99,28 @@ const CopyProjectButtonTooltip = memo(function CopyProjectButtonTooltip({
return (
{(text, handleOpenModal) => (
-
-
-
+
+
+
+
)}
)
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 fd7fdbdf3d..e5d0f9a621 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
@@ -1,13 +1,14 @@
import { memo, useCallback, useMemo, useState } from 'react'
import { useTranslation } from 'react-i18next'
import { Project } from '../../../../../../../../types/project/dashboard/api'
-import Icon from '../../../../../../shared/components/icon'
-import Tooltip from '../../../../../../shared/components/tooltip'
import DeleteProjectModal from '../../../modals/delete-project-modal'
import useIsMounted from '../../../../../../shared/hooks/use-is-mounted'
import { deleteProject } from '../../../../util/api'
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
@@ -63,20 +64,28 @@ const DeleteProjectButtonTooltip = memo(function DeleteProjectButtonTooltip({
return (
{(text, handleOpenModal) => (
-
-
-
+
+
+
+
)}
)
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 9478d7eb97..45d7d599c9 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
@@ -1,11 +1,12 @@
import { useTranslation } from 'react-i18next'
import { memo, useCallback } from 'react'
import { Project } from '../../../../../../../../types/project/dashboard/api'
-import Icon from '../../../../../../shared/components/icon'
-import Tooltip from '../../../../../../shared/components/tooltip'
import * as eventTracking from '../../../../../../infrastructure/event-tracking'
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
@@ -39,20 +40,28 @@ const DownloadProjectButtonTooltip = memo(
return (
{(text, downloadProject) => (
-
-
-
+
+
+
+
)}
)
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 7649c3f3a3..c279fbb292 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
@@ -1,13 +1,14 @@
import { memo, useCallback, useMemo, useState } from 'react'
import { useTranslation } from 'react-i18next'
-import Icon from '../../../../../../shared/components/icon'
-import Tooltip from '../../../../../../shared/components/tooltip'
import LeaveProjectModal from '../../../modals/leave-project-modal'
import { useProjectListContext } from '../../../../context/project-list-context'
import useIsMounted from '../../../../../../shared/hooks/use-is-mounted'
import { leaveProject } from '../../../../util/api'
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
@@ -62,20 +63,28 @@ const LeaveProjectButtonTooltip = memo(function LeaveProjectButtonTooltip({
return (
{(text, handleOpenModal) => (
-
-
-
+
+
+
+
)}
)
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 616810dbf2..e7384f44a3 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
@@ -1,12 +1,13 @@
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 Tooltip from '../../../../../../shared/components/tooltip'
import TrashProjectModal from '../../../modals/trash-project-modal'
import useIsMounted from '../../../../../../shared/hooks/use-is-mounted'
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
@@ -62,20 +63,28 @@ const TrashProjectButtonTooltip = memo(function TrashProjectButtonTooltip({
return (
{(text, handleOpenModal) => (
-
-
-
+
+
+
+
)}
)
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 2d1fba5af5..9cb99b2075 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
@@ -1,10 +1,11 @@
import { useTranslation } from 'react-i18next'
import { memo, useCallback } from 'react'
import { Project } from '../../../../../../../../types/project/dashboard/api'
-import Icon from '../../../../../../shared/components/icon'
-import Tooltip from '../../../../../../shared/components/tooltip'
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
@@ -41,20 +42,28 @@ const UnarchiveProjectButtonTooltip = memo(
return (
{(text, handleUnarchiveProject) => (
-
-
-
+
+
+
+
)}
)
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 5513bc5cc3..ce3db4b665 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
@@ -1,10 +1,11 @@
import { useTranslation } from 'react-i18next'
import { memo, useCallback } from 'react'
import { Project } from '../../../../../../../../types/project/dashboard/api'
-import Icon from '../../../../../../shared/components/icon'
-import Tooltip from '../../../../../../shared/components/tooltip'
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
@@ -40,20 +41,28 @@ const UntrashProjectButtonTooltip = memo(function UntrashProjectButtonTooltip({
return (
{(text, handleUntrashProject) => (
-
-
-
+
+
+
+
)}
)
diff --git a/services/web/frontend/js/features/project-list/components/table/cells/last-updated-cell.tsx b/services/web/frontend/js/features/project-list/components/table/cells/last-updated-cell.tsx
index 953a3c572c..b6417e92d4 100644
--- a/services/web/frontend/js/features/project-list/components/table/cells/last-updated-cell.tsx
+++ b/services/web/frontend/js/features/project-list/components/table/cells/last-updated-cell.tsx
@@ -1,7 +1,7 @@
import { formatDate, fromNowDate } from '../../../../../utils/dates'
import { Project } from '../../../../../../../types/project/dashboard/api'
-import Tooltip from '../../../../../shared/components/tooltip'
import { LastUpdatedBy } from '@/features/project-list/components/table/cells/last-updated-by'
+import OLTooltip from '@/features/ui/components/ol/ol-tooltip'
type LastUpdatedCellProps = {
project: Project
@@ -12,7 +12,7 @@ export default function LastUpdatedCell({ project }: LastUpdatedCellProps) {
const tooltipText = formatDate(project.lastUpdated)
return (
- {lastUpdatedDate}
)}
-
+
)
}
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 82fa8a61b8..e3cc0d8c41 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,8 +1,8 @@
import { useTranslation } from 'react-i18next'
import Icon from '../../../../../shared/components/icon'
-import Tooltip from '../../../../../shared/components/tooltip'
import { getOwnerName } from '../../../util/project'
import { Project } from '../../../../../../../types/project/dashboard/api'
+import OLTooltip from '@/features/ui/components/ol/ol-tooltip'
type LinkSharingIconProps = {
prependSpace: boolean
@@ -17,7 +17,7 @@ function LinkSharingIcon({
}: LinkSharingIconProps) {
const { t } = useTranslation()
return (
-
-
+
)
}
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 5a4112a918..9d06c6fcea 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
@@ -1,26 +1,32 @@
import { memo, useCallback } from 'react'
+import { useTranslation } from 'react-i18next'
import { useProjectListContext } from '@/features/project-list/context/project-list-context'
+import OLFormCheckbox from '@/features/ui/components/ol/ol-form-checkbox'
-export const ProjectCheckbox = memo<{ projectId: string }>(({ projectId }) => {
- const { selectedProjectIds, toggleSelectedProject } = useProjectListContext()
+export const ProjectCheckbox = memo<{ projectId: string; projectName: string }>(
+ ({ projectId, projectName }) => {
+ const { t } = useTranslation()
+ const { selectedProjectIds, toggleSelectedProject } =
+ useProjectListContext()
- const handleCheckboxChange = useCallback(
- event => {
- toggleSelectedProject(projectId, event.target.checked)
- },
- [projectId, toggleSelectedProject]
- )
+ const handleCheckboxChange = useCallback(
+ event => {
+ toggleSelectedProject(projectId, event.target.checked)
+ },
+ [projectId, toggleSelectedProject]
+ )
- return (
-
- )
-})
+ return (
+
+ )
+ }
+)
ProjectCheckbox.displayName = 'ProjectCheckbox'
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 8fcb047efb..59d1a1cf6d 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
@@ -1,5 +1,4 @@
import { memo } from 'react'
-import { useTranslation } from 'react-i18next'
import InlineTags from './cells/inline-tags'
import OwnerCell from './cells/owner-cell'
import LastUpdatedCell from './cells/last-updated-cell'
@@ -9,45 +8,90 @@ 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
+ selected: boolean
}
-function ProjectListTableRow({ project }: ProjectListTableRowProps) {
- const { t } = useTranslation()
-
+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 75fe3de7c4..c0a75344e3 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,21 +1,37 @@
-import { useCallback } from 'react'
+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'
import withContent, { SortBtnProps } from '../sort/with-content'
-import { Project } from '../../../../../../types/project/dashboard/api'
+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 (
)
}
@@ -31,6 +47,7 @@ function ProjectListTable() {
selectOrUnselectAllProjects,
} = useProjectListContext()
const { handleSort } = useSort()
+ const checkAllRef = useRef()
const handleAllProjectsCheckboxChange = useCallback(
(event: React.ChangeEvent) => {
@@ -39,18 +56,39 @@ function ProjectListTable() {
[selectOrUnselectAllProjects]
)
+ useEffect(() => {
+ if (checkAllRef.current) {
+ checkAllRef.current.indeterminate =
+ selectedProjects.length > 0 &&
+ selectedProjects.length !== visibleProjects.length
+ }
+ }, [selectedProjects, visibleProjects])
+
return (
-
+
)
}
diff --git a/services/web/frontend/js/features/ui/components/bootstrap-5/button.tsx b/services/web/frontend/js/features/ui/components/bootstrap-5/button.tsx
index 6b9c87322d..1d51545aaa 100644
--- a/services/web/frontend/js/features/ui/components/bootstrap-5/button.tsx
+++ b/services/web/frontend/js/features/ui/components/bootstrap-5/button.tsx
@@ -15,6 +15,7 @@ export default function Button({
className,
leadingIcon,
isLoading = false,
+ loadingLabel,
size = 'default',
trailingIcon,
variant = 'primary',
@@ -41,7 +42,9 @@ export default function Button({
className={loadingSpinnerClassName}
role="status"
/>
- {t('loading')}
+
+ {loadingLabel ?? t('loading')}
+
)}
diff --git a/services/web/frontend/js/features/ui/components/bootstrap-5/dropdown-menu.tsx b/services/web/frontend/js/features/ui/components/bootstrap-5/dropdown-menu.tsx
index 7ea7b8ed39..d9df962cae 100644
--- a/services/web/frontend/js/features/ui/components/bootstrap-5/dropdown-menu.tsx
+++ b/services/web/frontend/js/features/ui/components/bootstrap-5/dropdown-menu.tsx
@@ -29,7 +29,44 @@ export const DropdownItem = forwardRef<
{ active, children, description, leadingIcon, trailingIcon, ...props },
ref
) => {
- const trailingIconType = active ? 'check' : trailingIcon
+ let leadingIconComponent = null
+ if (leadingIcon) {
+ if (typeof leadingIcon === 'string') {
+ leadingIconComponent = (
+
+ )
+ } else {
+ leadingIconComponent = (
+
+ {leadingIcon}
+
+ )
+ }
+ }
+
+ let trailingIconComponent = null
+ if (trailingIcon) {
+ if (typeof trailingIcon === 'string') {
+ const trailingIconType = active ? 'check' : trailingIcon
+
+ trailingIconComponent = (
+
+ )
+ } else {
+ trailingIconComponent = (
+
+ {trailingIcon}
+
+ )
+ }
+ }
+
return (
- {leadingIcon && (
-
- )}
+ {leadingIconComponent}
{children}
- {trailingIconType && (
-
- )}
+ {trailingIconComponent}
{description && (
{description}
)}
diff --git a/services/web/frontend/js/features/ui/components/bootstrap-5/table.tsx b/services/web/frontend/js/features/ui/components/bootstrap-5/table.tsx
index 1e7752ddab..7599d0a74d 100644
--- a/services/web/frontend/js/features/ui/components/bootstrap-5/table.tsx
+++ b/services/web/frontend/js/features/ui/components/bootstrap-5/table.tsx
@@ -1,22 +1,40 @@
import { Table as BS5Table } from 'react-bootstrap-5'
import classnames from 'classnames'
-function Table({ responsive, ...rest }: React.ComponentProps) {
- const content = (
+export function TableContainer({
+ responsive,
+ bordered,
+ children,
+}: React.ComponentProps) {
+ return (
-
+ {children}
)
+}
- if (responsive) {
- return {content}
- }
+type TableProps = React.ComponentProps & {
+ container?: boolean
+}
- return content
+function Table({
+ container = true,
+ responsive,
+ bordered,
+ ...rest
+}: TableProps) {
+ return container ? (
+
+
+
+ ) : (
+
+ )
}
export default Table
diff --git a/services/web/frontend/js/features/ui/components/ol/ol-form-checkbox.tsx b/services/web/frontend/js/features/ui/components/ol/ol-form-checkbox.tsx
index b10a368788..90ef65eff8 100644
--- a/services/web/frontend/js/features/ui/components/ol/ol-form-checkbox.tsx
+++ b/services/web/frontend/js/features/ui/components/ol/ol-form-checkbox.tsx
@@ -1,13 +1,15 @@
import { Form } from 'react-bootstrap-5'
-import { Checkbox as BS3Checkbox } from 'react-bootstrap'
+import { Checkbox as BS3Checkbox, Radio as BS3Radio } from 'react-bootstrap'
import BootstrapVersionSwitcher from '@/features/ui/components/bootstrap-5/bootstrap-version-switcher'
+import { getAriaAndDataProps } from '@/features/utils/bootstrap-5'
type OLFormCheckboxProps = React.ComponentProps<(typeof Form)['Check']> & {
+ inputRef?: React.MutableRefObject
bs3Props?: Record
}
function OLFormCheckbox(props: OLFormCheckboxProps) {
- const { bs3Props, ...rest } = props
+ const { bs3Props, inputRef, ...rest } = props
const bs3FormLabelProps: React.ComponentProps = {
children: rest.label,
@@ -17,14 +19,27 @@ function OLFormCheckbox(props: OLFormCheckboxProps) {
disabled: rest.disabled,
inline: rest.inline,
title: rest.title,
+ autoComplete: rest.autoComplete,
onChange: rest.onChange as (e: React.ChangeEvent) => void,
+ inputRef: node => {
+ if (inputRef) {
+ inputRef.current = node
+ }
+ },
+ ...getAriaAndDataProps(rest),
...bs3Props,
}
return (
}
- bs5={}
+ bs3={
+ rest.type === 'radio' ? (
+
+ ) : (
+
+ )
+ }
+ bs5={}
/>
)
}
diff --git a/services/web/frontend/js/features/ui/components/ol/ol-form-control.tsx b/services/web/frontend/js/features/ui/components/ol/ol-form-control.tsx
index 4d1dc7616a..ed1109d9ad 100644
--- a/services/web/frontend/js/features/ui/components/ol/ol-form-control.tsx
+++ b/services/web/frontend/js/features/ui/components/ol/ol-form-control.tsx
@@ -2,6 +2,7 @@ import { forwardRef } from 'react'
import { Form } from 'react-bootstrap-5'
import { FormControl as BS3FormControl } from 'react-bootstrap'
import BootstrapVersionSwitcher from '@/features/ui/components/bootstrap-5/bootstrap-version-switcher'
+import { getAriaAndDataProps } from '@/features/utils/bootstrap-5'
type OLFormControlProps = React.ComponentProps<(typeof Form)['Control']> & {
bs3Props?: Record
@@ -22,6 +23,7 @@ const OLFormControl = forwardRef(
placeholder: rest.placeholder,
readOnly: rest.readOnly,
autoComplete: rest.autoComplete,
+ autoFocus: rest.autoFocus,
minLength: rest.minLength,
maxLength: rest.maxLength,
onChange: rest.onChange as (e: React.ChangeEvent) => void,
@@ -38,20 +40,9 @@ const OLFormControl = forwardRef(
...bs3Props,
}
- // get all `aria-*` and `data-*` attributes
- const extraProps = Object.entries(rest).reduce(
- (acc, [key, value]) => {
- if (key.startsWith('aria-') || key.startsWith('data-')) {
- acc[key] = value
- }
- return acc
- },
- {} as Record
- )
-
bs3FormControlProps = {
...bs3FormControlProps,
- ...extraProps,
+ ...getAriaAndDataProps(rest),
'data-ol-dirty': rest['data-ol-dirty'],
} as typeof bs3FormControlProps & Record
diff --git a/services/web/frontend/js/features/ui/components/ol/ol-form.tsx b/services/web/frontend/js/features/ui/components/ol/ol-form.tsx
index a7f2be1295..dbdc0a6c24 100644
--- a/services/web/frontend/js/features/ui/components/ol/ol-form.tsx
+++ b/services/web/frontend/js/features/ui/components/ol/ol-form.tsx
@@ -13,6 +13,7 @@ function OLForm(props: OLFormProps) {
componentClass: rest.as,
bsClass: rest.className,
children: rest.children,
+ id: rest.id,
onSubmit: rest.onSubmit as React.FormEventHandler | undefined,
...bs3Props,
}
diff --git a/services/web/frontend/js/features/ui/components/ol/ol-icon-button.tsx b/services/web/frontend/js/features/ui/components/ol/ol-icon-button.tsx
index a0e19e90af..3d59f0aeeb 100644
--- a/services/web/frontend/js/features/ui/components/ol/ol-icon-button.tsx
+++ b/services/web/frontend/js/features/ui/components/ol/ol-icon-button.tsx
@@ -17,18 +17,19 @@ export type OLIconButtonProps = IconButtonProps & {
export default function OLIconButton(props: OLIconButtonProps) {
const { bs3Props, ...rest } = props
- const { fw, ...bs3Rest } = bs3Props || {}
+ const { fw, loading, ...bs3Rest } = bs3Props || {}
return (
- {bs3Props?.loading}
-
+ {loading || (
+
+ )}
}
bs5={}
diff --git a/services/web/frontend/js/features/ui/components/ol/ol-table.tsx b/services/web/frontend/js/features/ui/components/ol/ol-table.tsx
index 056a965a04..f2a8cb582c 100644
--- a/services/web/frontend/js/features/ui/components/ol/ol-table.tsx
+++ b/services/web/frontend/js/features/ui/components/ol/ol-table.tsx
@@ -7,7 +7,7 @@ type OLFormProps = React.ComponentProps & {
}
function OLTable(props: OLFormProps) {
- const { bs3Props, ...rest } = props
+ const { bs3Props, container, ...rest } = props
const bs3FormProps: React.ComponentProps = {
bsClass: rest.className,
@@ -21,7 +21,7 @@ function OLTable(props: OLFormProps) {
return (
}
- bs5={}
+ bs5={}
/>
)
}
diff --git a/services/web/frontend/js/features/ui/components/ol/ol-tooltip.tsx b/services/web/frontend/js/features/ui/components/ol/ol-tooltip.tsx
index 3104683049..99df8d0828 100644
--- a/services/web/frontend/js/features/ui/components/ol/ol-tooltip.tsx
+++ b/services/web/frontend/js/features/ui/components/ol/ol-tooltip.tsx
@@ -13,7 +13,9 @@ function OLTooltip(props: OLTooltipProps) {
children: bs5Props.children,
id: bs5Props.id,
description: bs5Props.description,
- overlayProps: {},
+ overlayProps: {
+ placement: bs5Props.overlayProps?.placement,
+ },
...bs3Props,
}
diff --git a/services/web/frontend/js/features/ui/components/types/button-props.ts b/services/web/frontend/js/features/ui/components/types/button-props.ts
index cf5488ba23..794d7422d8 100644
--- a/services/web/frontend/js/features/ui/components/types/button-props.ts
+++ b/services/web/frontend/js/features/ui/components/types/button-props.ts
@@ -10,6 +10,7 @@ export type ButtonProps = {
target?: string
rel?: string
isLoading?: boolean
+ loadingLabel?: string
onClick?: MouseEventHandler
size?: 'small' | 'default' | 'large'
trailingIcon?: string
diff --git a/services/web/frontend/js/features/ui/components/types/dropdown-menu-props.ts b/services/web/frontend/js/features/ui/components/types/dropdown-menu-props.ts
index 72bc803935..6c9a483c06 100644
--- a/services/web/frontend/js/features/ui/components/types/dropdown-menu-props.ts
+++ b/services/web/frontend/js/features/ui/components/types/dropdown-menu-props.ts
@@ -24,9 +24,9 @@ export type DropdownItemProps = PropsWithChildren<{
disabled?: boolean
eventKey?: string | number
href?: string
- leadingIcon?: string
+ leadingIcon?: string | React.ReactNode
onClick?: React.MouseEventHandler
- trailingIcon?: string
+ trailingIcon?: string | React.ReactNode
variant?: 'default' | 'danger'
className?: string
role?: string
diff --git a/services/web/frontend/js/features/utils/bootstrap-5.ts b/services/web/frontend/js/features/utils/bootstrap-5.ts
index fdf9d71550..2723ea458e 100644
--- a/services/web/frontend/js/features/utils/bootstrap-5.ts
+++ b/services/web/frontend/js/features/utils/bootstrap-5.ts
@@ -5,3 +5,16 @@ export const isBootstrap5 = getMeta('ol-bootstrapVersion') === 5
export const bsVersion = ({ bs5, bs3 }: { bs5?: string; bs3?: string }) => {
return isBootstrap5 ? bs5 : bs3
}
+
+// get all `aria-*` and `data-*` attributes
+export const getAriaAndDataProps = (obj: Record) => {
+ return Object.entries(obj).reduce(
+ (acc, [key, value]) => {
+ if (key.startsWith('aria-') || key.startsWith('data-')) {
+ acc[key] = value
+ }
+ return acc
+ },
+ {} as Record
+ )
+}
diff --git a/services/web/frontend/js/shared/components/material-icon.tsx b/services/web/frontend/js/shared/components/material-icon.tsx
index 5c78302a4b..c7acac7a14 100644
--- a/services/web/frontend/js/shared/components/material-icon.tsx
+++ b/services/web/frontend/js/shared/components/material-icon.tsx
@@ -1,5 +1,6 @@
import classNames from 'classnames'
import React from 'react'
+import { bsVersion } from '@/features/utils/bootstrap-5'
type IconProps = React.ComponentProps<'i'> & {
type: string
@@ -20,7 +21,9 @@ function MaterialIcon({
{type}
{accessibilityLabel && (
- {accessibilityLabel}
+
+ {accessibilityLabel}
+
)}
>
)
diff --git a/services/web/frontend/stylesheets/app/project-list-react.less b/services/web/frontend/stylesheets/app/project-list-react.less
index e98c990171..db2ae13872 100644
--- a/services/web/frontend/stylesheets/app/project-list-react.less
+++ b/services/web/frontend/stylesheets/app/project-list-react.less
@@ -417,11 +417,13 @@
border: 0;
text-align: left;
color: @ol-type-color;
+ background-color: transparent;
padding: 0;
font-weight: bold;
max-width: 100%;
overflow: hidden;
text-overflow: ellipsis;
+ text-decoration: none;
&:hover,
&:focus {
@@ -437,6 +439,14 @@
input[type='checkbox'] {
margin-top: 5px;
}
+
+ .dash-cell-checkbox-wrapper {
+ label {
+ display: block;
+ margin: 0;
+ line-height: 1;
+ }
+ }
}
.dash-cell-name {
@@ -827,13 +837,6 @@
}
}
-.project-list-load-more {
- display: flex;
- flex-direction: column;
- justify-content: center;
- align-items: center;
-}
-
.project-list-load-more-button {
margin-bottom: @margin-sm;
}
diff --git a/services/web/frontend/stylesheets/app/project-list.less b/services/web/frontend/stylesheets/app/project-list.less
index 2b97ebd9b1..df5e821041 100644
--- a/services/web/frontend/stylesheets/app/project-list.less
+++ b/services/web/frontend/stylesheets/app/project-list.less
@@ -121,16 +121,11 @@
.project-list-table-name-cell,
.project-list-table-owner-cell,
.project-list-table-lastupdated-cell,
-.project-list-table-actions-cell,
-.project-list-table-no-projects-cell {
+.project-list-table-actions-cell {
padding: (@line-height-computed / 4) 0;
vertical-align: top;
}
-.project-list-table-no-projects-cell {
- text-align: center;
-}
-
.project-list-table-name-cell {
width: 50%;
padding-right: @line-height-computed / 2;
diff --git a/services/web/frontend/stylesheets/bootstrap-5/base/bootstrap.scss b/services/web/frontend/stylesheets/bootstrap-5/base/bootstrap.scss
index 6b857a9894..b32748c842 100644
--- a/services/web/frontend/stylesheets/bootstrap-5/base/bootstrap.scss
+++ b/services/web/frontend/stylesheets/bootstrap-5/base/bootstrap.scss
@@ -52,5 +52,8 @@
// Components custom style
@import '../components/all';
+// Custom helpers
+@import '../helpers/all';
+
// Pages custom style
@import '../pages/all';
diff --git a/services/web/frontend/stylesheets/bootstrap-5/base/typography.scss b/services/web/frontend/stylesheets/bootstrap-5/base/typography.scss
index 49b51d0b2a..c2f2c2aeca 100644
--- a/services/web/frontend/stylesheets/bootstrap-5/base/typography.scss
+++ b/services/web/frontend/stylesheets/bootstrap-5/base/typography.scss
@@ -63,3 +63,7 @@ pre,
samp {
@include body-base;
}
+
+.list-style-check-green {
+ list-style-image: url('../../../../public/img/fa-check-green.svg');
+}
diff --git a/services/web/frontend/stylesheets/bootstrap-5/components/button.scss b/services/web/frontend/stylesheets/bootstrap-5/components/button.scss
index 593b376e65..73af656924 100644
--- a/services/web/frontend/stylesheets/bootstrap-5/components/button.scss
+++ b/services/web/frontend/stylesheets/bootstrap-5/components/button.scss
@@ -186,6 +186,10 @@
.icon-large {
font-size: 24px;
}
+
+ .spinner {
+ margin: var(--spacing-01);
+ }
}
.icon-button-small {
diff --git a/services/web/frontend/stylesheets/bootstrap-5/components/dropdown-menu.scss b/services/web/frontend/stylesheets/bootstrap-5/components/dropdown-menu.scss
index bb5290e232..b07e61e410 100644
--- a/services/web/frontend/stylesheets/bootstrap-5/components/dropdown-menu.scss
+++ b/services/web/frontend/stylesheets/bootstrap-5/components/dropdown-menu.scss
@@ -92,6 +92,11 @@
.dropdown-item-leading-icon {
padding-right: var(--spacing-04);
+
+ &.spinner {
+ margin-left: var(--spacing-01);
+ margin-right: var(--spacing-01);
+ }
}
// description text should look disabled when the dropdown item is disabled
diff --git a/services/web/frontend/stylesheets/bootstrap-5/components/table.scss b/services/web/frontend/stylesheets/bootstrap-5/components/table.scss
index 3f8392374a..6aa83affd8 100644
--- a/services/web/frontend/stylesheets/bootstrap-5/components/table.scss
+++ b/services/web/frontend/stylesheets/bootstrap-5/components/table.scss
@@ -1,74 +1,50 @@
.table-container {
flex: 1;
margin-bottom: var(--spacing-06);
+ background-color: var(--white);
.table {
margin-bottom: initial;
}
}
-.table-container-bordered {
- --table-container-border-width: var(--bs-border-width);
-
- border-color: $table-border-color;
- border-radius: var(--border-radius-base);
- border-width: var(--table-container-border-width);
- border-style: solid;
-
- .table {
- th,
- td {
- &:first-child {
- border-left-width: 0;
- }
-
- &:last-child {
- border-right-width: 0;
- }
- }
-
- tr:first-child {
- border-top-width: 0;
-
- th,
+.table {
+ tr {
+ &:last-child {
td {
- &:first-child {
- border-top-left-radius: calc(
- var(--border-radius-base) - var(--table-container-border-width)
- );
- }
- }
-
- th,
- td {
- &:last-child {
- border-top-right-radius: calc(
- var(--border-radius-base) - var(--table-container-border-width)
- );
- }
- }
- }
-
- tr:last-child {
- border-bottom-width: 0;
-
- th,
- td {
- &:first-child {
- border-bottom-left-radius: calc(
- var(--border-radius-base) - var(--table-container-border-width)
- );
- }
- }
-
- th,
- td {
- &:last-child {
- border-bottom-right-radius: calc(
- var(--border-radius-base) - var(--table-container-border-width)
- );
- }
+ border-bottom-width: 0;
}
}
}
+
+ th,
+ td {
+ a {
+ text-decoration: none;
+ }
+ }
+}
+
+.table-container-bordered {
+ padding: var(--spacing-04);
+ border-color: $table-border-color;
+ border-radius: var(--border-radius-base);
+ border-width: var(--bs-border-width);
+ border-style: solid;
+}
+
+.table-hover {
+ th {
+ &:hover {
+ background-color: $table-hover-bg;
+ }
+ }
+}
+
+.table-striped {
+ tr,
+ td {
+ border-top-width: 0;
+ border-bottom-width: 0;
+ }
}
diff --git a/services/web/frontend/stylesheets/bootstrap-5/helpers/all.scss b/services/web/frontend/stylesheets/bootstrap-5/helpers/all.scss
new file mode 100644
index 0000000000..86f7f04e7c
--- /dev/null
+++ b/services/web/frontend/stylesheets/bootstrap-5/helpers/all.scss
@@ -0,0 +1 @@
+@import 'visually-hidden';
diff --git a/services/web/frontend/stylesheets/bootstrap-5/helpers/visually-hidden.scss b/services/web/frontend/stylesheets/bootstrap-5/helpers/visually-hidden.scss
new file mode 100644
index 0000000000..b95f745c6d
--- /dev/null
+++ b/services/web/frontend/stylesheets/bootstrap-5/helpers/visually-hidden.scss
@@ -0,0 +1,5 @@
+.visually-hidden-max-md {
+ @include media-breakpoint-down(md) {
+ @include visually-hidden();
+ }
+}
diff --git a/services/web/frontend/stylesheets/bootstrap-5/pages/project-list.scss b/services/web/frontend/stylesheets/bootstrap-5/pages/project-list.scss
index 3eded11545..082c2bc574 100644
--- a/services/web/frontend/stylesheets/bootstrap-5/pages/project-list.scss
+++ b/services/web/frontend/stylesheets/bootstrap-5/pages/project-list.scss
@@ -12,6 +12,10 @@
}
}
+.action-btn {
+ padding: 0 var(--spacing-02);
+}
+
.project-list-react {
body > &.content {
padding-top: $header-height;
@@ -155,6 +159,240 @@
overflow-x: hidden;
padding: var(--spacing-08) var(--spacing-06);
}
+
+ .project-dash-table {
+ width: 100%;
+ table-layout: fixed;
+
+ @include media-breakpoint-down(md) {
+ tr:not(:last-child) {
+ border-bottom: 1px solid $table-border-color;
+ }
+
+ td {
+ border-bottom-width: 0;
+ }
+ }
+
+ tbody {
+ tr.no-projects:hover {
+ td {
+ box-shadow: none;
+ }
+ }
+ }
+
+ .table-header-sort-btn {
+ border: 0;
+ text-align: left;
+ color: var(--content-secondary);
+ background-color: transparent;
+ padding: 0;
+ font-weight: bold;
+ max-width: 100%;
+ overflow: hidden;
+ text-overflow: ellipsis;
+ text-decoration: none;
+
+ &:hover,
+ &:focus {
+ color: var(--content-secondary);
+ text-decoration: none;
+ }
+
+ .material-symbols {
+ vertical-align: bottom;
+ font-size: var(--font-size-06);
+ }
+ }
+
+ .dash-cell-name {
+ hyphens: auto;
+ width: 50%;
+ word-break: break-word;
+ }
+
+ .dash-cell-owner {
+ width: 20%;
+ }
+
+ .dash-cell-date {
+ width: 25%;
+ }
+
+ .dash-cell-actions {
+ display: none;
+ text-align: right;
+
+ .btn {
+ text-decoration: none;
+ }
+ }
+
+ .dash-cell-date-owner {
+ font-size: $font-size-sm;
+ @include text-truncate();
+ }
+
+ @include media-breakpoint-up(sm) {
+ .dash-cell-checkbox {
+ width: 4%;
+ }
+
+ .dash-cell-name {
+ width: 50%;
+ }
+
+ .dash-cell-owner {
+ width: 21%;
+ }
+
+ .dash-cell-date {
+ width: 25%;
+ }
+
+ .dash-cell-actions {
+ width: 0%;
+ }
+ }
+
+ @include media-breakpoint-up(md) {
+ .dash-cell-checkbox {
+ width: 4%;
+ }
+
+ .dash-cell-name {
+ width: 44%;
+ }
+
+ .dash-cell-owner {
+ width: 16%;
+ }
+
+ .dash-cell-date {
+ width: 21%;
+ }
+
+ .dash-cell-actions {
+ display: table-cell;
+ width: 15%;
+ }
+
+ .project-tools {
+ float: none;
+ }
+ }
+
+ @include media-breakpoint-up(lg) {
+ .dash-cell-checkbox {
+ width: 3%;
+ }
+
+ .dash-cell-name {
+ width: 46%;
+ }
+
+ .dash-cell-owner {
+ width: 13%;
+ }
+
+ .dash-cell-date {
+ width: 16%;
+ }
+
+ .dash-cell-actions {
+ width: 22%;
+ }
+
+ tbody {
+ .dash-cell-actions {
+ white-space: nowrap;
+ }
+ }
+ }
+
+ @include media-breakpoint-up(xl) {
+ .dash-cell-checkbox {
+ width: 3%;
+ }
+
+ .dash-cell-name {
+ width: 46%;
+ }
+
+ .dash-cell-owner {
+ width: 15%;
+ }
+
+ .dash-cell-date {
+ width: 19%;
+ }
+
+ .dash-cell-actions {
+ width: 17%;
+ }
+ }
+
+ @include media-breakpoint-up(xxl) {
+ .dash-cell-checkbox {
+ width: 2%;
+ }
+
+ .dash-cell-name {
+ width: 49%;
+ }
+
+ .dash-cell-owner {
+ width: 16%;
+ }
+
+ .dash-cell-date {
+ width: 19%;
+ }
+
+ .dash-cell-actions {
+ width: 14%;
+ }
+ }
+
+ @include media-breakpoint-down(md) {
+ tr {
+ position: relative;
+ display: flex;
+ flex-direction: column;
+ }
+
+ .dash-cell-name,
+ .dash-cell-owner,
+ .dash-cell-date,
+ .dash-cell-tag,
+ .dash-cell-actions {
+ display: block;
+ width: auto;
+ }
+
+ .dash-cell-actions {
+ position: absolute;
+ top: var(--spacing-04);
+ right: var(--spacing-04);
+ padding: 0 !important;
+ }
+
+ .dropdown-table-button-toggle {
+ padding: var(--spacing-04);
+ font-size: 0;
+ line-height: 1;
+ border-radius: 50%;
+ color: var(--content-primary);
+ background-color: transparent;
+
+ &:hover,
+ &:active {
+ background-color: rgba($neutral-90, 0.08);
+ }
+ }
+ }
+ }
}
.project-list-upload-project-modal-uppy-dashboard .uppy-Root {
@@ -222,3 +460,7 @@
}
}
}
+
+.project-list-load-more-button {
+ margin-bottom: var(--spacing-05);
+}
diff --git a/services/web/locales/en.json b/services/web/locales/en.json
index 34f07313f4..47d63cf13d 100644
--- a/services/web/locales/en.json
+++ b/services/web/locales/en.json
@@ -1763,7 +1763,6 @@
"shortcut_to_open_advanced_reference_search": "(__ctrlSpace__ or __altSpace__)",
"show_all": "show all",
"show_all_projects": "Show all projects",
- "show_all_uppercase": "Show all",
"show_document_preamble": "Show document preamble",
"show_hotkeys": "Show Hotkeys",
"show_in_code": "Show in code",
@@ -1771,7 +1770,6 @@
"show_less": "show less",
"show_local_file_contents": "Show Local File Contents",
"show_outline": "Show File outline",
- "show_x_more": "Show __x__ more",
"show_x_more_projects": "Show __x__ more projects",
"show_your_support": "Show your support",
"showing_1_result": "Showing 1 result",
diff --git a/services/web/test/frontend/features/project-list/components/load-more.test.tsx b/services/web/test/frontend/features/project-list/components/load-more.test.tsx
index 25d55e104a..b102244a13 100644
--- a/services/web/test/frontend/features/project-list/components/load-more.test.tsx
+++ b/services/web/test/frontend/features/project-list/components/load-more.test.tsx
@@ -72,9 +72,9 @@ describe('', function () {
})
await waitFor(() => {
- screen.getByLabelText(
- `Show ${currentList.length - 20 - 20} more projects`
- )
+ screen.getByRole('button', {
+ name: `Show ${currentList.length - 20 - 20} more projects`,
+ })
screen.getByText(`Showing 40 out of ${currentList.length} projects.`)
})
})
diff --git a/services/web/test/frontend/features/project-list/components/project-list-root.test.tsx b/services/web/test/frontend/features/project-list/components/project-list-root.test.tsx
index 09392cb5a4..7139aa61c3 100644
--- a/services/web/test/frontend/features/project-list/components/project-list-root.test.tsx
+++ b/services/web/test/frontend/features/project-list/components/project-list-root.test.tsx
@@ -202,7 +202,9 @@ describe('', function () {
let checked = allCheckboxes.filter(c => c.checked)
expect(checked.length).to.equal(21) // max projects viewable by default is 20, and plus one for check all
- const loadMoreButton = screen.getByLabelText('Show 17 more projects')
+ const loadMoreButton = screen.getByRole('button', {
+ name: 'Show 17 more projects',
+ })
fireEvent.click(loadMoreButton)
allCheckboxes = screen.getAllByRole('checkbox')
@@ -212,7 +214,9 @@ describe('', function () {
})
it('maintains viewable and selected projects after loading more and then selecting all', async function () {
- const loadMoreButton = screen.getByLabelText('Show 17 more projects')
+ const loadMoreButton = screen.getByRole('button', {
+ name: 'Show 17 more projects',
+ })
fireEvent.click(loadMoreButton)
// verify button gone
screen.getByText(
@@ -225,8 +229,8 @@ describe('', function () {
`Showing ${currentList.length} out of ${currentList.length} projects.`
)
- allCheckboxes = screen.getAllByRole('checkbox')
- expect(allCheckboxes.length).to.equal(currentList.length + 1)
+ // allCheckboxes = screen.getAllByRole('checkbox')
+ // expect(allCheckboxes.length).to.equal(currentList.length + 1)
})
})
@@ -1095,7 +1099,9 @@ describe('', function () {
archived: false,
},
})
- const copyButton = within(tableRows[1]).getAllByLabelText('Copy')[0]
+ const copyButton = within(tableRows[1]).getAllByRole('button', {
+ name: 'Copy',
+ })[0]
fireEvent.click(copyButton)
// confirm in modal
diff --git a/services/web/test/frontend/features/project-list/components/table/cells/action-buttons/archive-project-button.test.tsx b/services/web/test/frontend/features/project-list/components/table/cells/action-buttons/archive-project-button.test.tsx
index ba663c70ba..34381de117 100644
--- a/services/web/test/frontend/features/project-list/components/table/cells/action-buttons/archive-project-button.test.tsx
+++ b/services/web/test/frontend/features/project-list/components/table/cells/action-buttons/archive-project-button.test.tsx
@@ -20,7 +20,7 @@ describe('', function () {
renderWithProjectListContext(
)
- const btn = screen.getByLabelText('Archive')
+ const btn = screen.getByRole('button', { name: 'Archive' })
fireEvent.mouseOver(btn)
screen.getByRole('tooltip', { name: 'Archive' })
})
@@ -29,7 +29,7 @@ describe('', function () {
renderWithProjectListContext(
)
- const btn = screen.getByLabelText('Archive')
+ const btn = screen.getByRole('button', { name: 'Archive' })
fireEvent.click(btn)
screen.getByText('Archive Projects')
screen.getByText(archiveableProject.name)
@@ -39,7 +39,7 @@ describe('', function () {
renderWithProjectListContext(
)
- expect(screen.queryByLabelText('Archive')).to.be.null
+ expect(screen.queryByRole('button', { name: 'Archive' })).to.be.null
})
it('should archive the projects', async function () {
@@ -54,7 +54,7 @@ describe('', function () {
renderWithProjectListContext(
)
- const btn = screen.getByLabelText('Archive')
+ const btn = screen.getByRole('button', { name: 'Archive' })
fireEvent.click(btn)
screen.getByText('Archive Projects')
screen.getByText('You are about to archive the following projects:')
diff --git a/services/web/test/frontend/features/project-list/components/table/cells/action-buttons/compile-and-download-project-pdf-button.test.tsx b/services/web/test/frontend/features/project-list/components/table/cells/action-buttons/compile-and-download-project-pdf-button.test.tsx
index f330f71406..978faf510f 100644
--- a/services/web/test/frontend/features/project-list/components/table/cells/action-buttons/compile-and-download-project-pdf-button.test.tsx
+++ b/services/web/test/frontend/features/project-list/components/table/cells/action-buttons/compile-and-download-project-pdf-button.test.tsx
@@ -32,7 +32,7 @@ describe('', function () {
})
it('renders tooltip for button', function () {
- const btn = screen.getByLabelText('Download PDF')
+ const btn = screen.getByRole('button', { name: 'Download PDF' })
fireEvent.mouseOver(btn)
screen.getByRole('tooltip', { name: 'Download PDF' })
})
@@ -49,11 +49,11 @@ describe('', function () {
{ delay: 10 }
)
- const btn = screen.getByLabelText('Download PDF') as HTMLButtonElement
+ const btn = screen.getByRole('button', { name: 'Download PDF' })
fireEvent.click(btn)
await waitFor(() => {
- screen.getByLabelText('Compiling…')
+ screen.getByRole('button', { name: 'Compiling…' })
})
await waitFor(() => {
@@ -79,7 +79,9 @@ describe('', function () {
status: 'failure',
})
- const btn = screen.getByLabelText('Download PDF') as HTMLButtonElement
+ const btn = screen.getByRole('button', {
+ name: 'Download PDF',
+ }) as HTMLButtonElement
fireEvent.click(btn)
await waitFor(() => {
diff --git a/services/web/test/frontend/features/project-list/components/table/cells/action-buttons/copy-project-button.test.tsx b/services/web/test/frontend/features/project-list/components/table/cells/action-buttons/copy-project-button.test.tsx
index e29d088e67..3dbf028c29 100644
--- a/services/web/test/frontend/features/project-list/components/table/cells/action-buttons/copy-project-button.test.tsx
+++ b/services/web/test/frontend/features/project-list/components/table/cells/action-buttons/copy-project-button.test.tsx
@@ -21,7 +21,7 @@ describe('', function () {
renderWithProjectListContext(
)
- const btn = screen.getByLabelText('Copy')
+ const btn = screen.getByRole('button', { name: 'Copy' })
fireEvent.mouseOver(btn)
screen.getByRole('tooltip', { name: 'Copy' })
})
@@ -30,17 +30,17 @@ describe('', function () {
renderWithProjectListContext(
)
- expect(screen.queryByLabelText('Copy')).to.be.null
+ expect(screen.queryByRole('button', { name: 'Copy' })).to.be.null
})
it('does not render the button when project is trashed', function () {
renderWithProjectListContext(
)
- expect(screen.queryByLabelText('Copy')).to.be.null
+ expect(screen.queryByRole('button', { name: 'Copy' })).to.be.null
})
- it('opens the modal and copies the project ', async function () {
+ it('opens the modal and copies the project', async function () {
const copyProjectMock = fetchMock.post(
`express:/project/:projectId/clone`,
{
@@ -51,12 +51,14 @@ describe('', function () {
renderWithProjectListContext(
)
- const btn = screen.getByLabelText('Copy')
+ const btn = screen.getByRole('button', { name: 'Copy' })
fireEvent.click(btn)
screen.getByText('Copy Project')
screen.getByLabelText('New Name')
screen.getByDisplayValue(`${copyableProject.name} (Copy)`)
- const copyBtn = screen.getByText('Copy') as HTMLButtonElement
+ const copyBtn = screen.getByRole('button', {
+ name: 'Copy',
+ })
fireEvent.click(copyBtn)
expect(copyBtn.disabled).to.be.true
diff --git a/services/web/test/frontend/features/project-list/components/table/cells/action-buttons/delete-project-button.test.tsx b/services/web/test/frontend/features/project-list/components/table/cells/action-buttons/delete-project-button.test.tsx
index 5f36b7e0f9..c02d197db4 100644
--- a/services/web/test/frontend/features/project-list/components/table/cells/action-buttons/delete-project-button.test.tsx
+++ b/services/web/test/frontend/features/project-list/components/table/cells/action-buttons/delete-project-button.test.tsx
@@ -22,7 +22,7 @@ describe('', function () {
renderWithProjectListContext(
)
- const btn = screen.getByLabelText('Delete')
+ const btn = screen.getByRole('button', { name: 'Delete' })
fireEvent.mouseOver(btn)
screen.getByRole('tooltip', { name: 'Delete' })
})
@@ -32,7 +32,7 @@ describe('', function () {
renderWithProjectListContext(
)
- const btn = screen.queryByLabelText('Delete')
+ const btn = screen.queryByRole('button', { name: 'Delete' })
expect(btn).to.be.null
})
@@ -40,7 +40,7 @@ describe('', function () {
renderWithProjectListContext(
)
- expect(screen.queryByLabelText('Delete')).to.be.null
+ expect(screen.queryByRole('button', { name: 'Delete' })).to.be.null
})
it('opens the modal and deletes the project', async function () {
@@ -56,7 +56,7 @@ describe('', function () {
renderWithProjectListContext(
)
- const btn = screen.getByLabelText('Delete')
+ const btn = screen.getByRole('button', { name: 'Delete' })
fireEvent.click(btn)
screen.getByText('Delete Projects')
screen.getByText('You are about to delete the following projects:')
diff --git a/services/web/test/frontend/features/project-list/components/table/cells/action-buttons/download-project-button.test.tsx b/services/web/test/frontend/features/project-list/components/table/cells/action-buttons/download-project-button.test.tsx
index cdfca90646..3991560897 100644
--- a/services/web/test/frontend/features/project-list/components/table/cells/action-buttons/download-project-button.test.tsx
+++ b/services/web/test/frontend/features/project-list/components/table/cells/action-buttons/download-project-button.test.tsx
@@ -23,13 +23,15 @@ describe('', function () {
})
it('renders tooltip for button', function () {
- const btn = screen.getByLabelText('Download .zip file')
+ const btn = screen.getByRole('button', { name: 'Download .zip file' })
fireEvent.mouseOver(btn)
screen.getByRole('tooltip', { name: 'Download .zip file' })
})
it('downloads the project when clicked', async function () {
- const btn = screen.getByLabelText('Download .zip file') as HTMLButtonElement
+ const btn = screen.getByRole('button', {
+ name: 'Download .zip file',
+ }) as HTMLButtonElement
fireEvent.click(btn)
await waitFor(() => {
diff --git a/services/web/test/frontend/features/project-list/components/table/cells/action-buttons/leave-project-button.test.tsx b/services/web/test/frontend/features/project-list/components/table/cells/action-buttons/leave-project-button.test.tsx
index 39d3129791..12dc8d736a 100644
--- a/services/web/test/frontend/features/project-list/components/table/cells/action-buttons/leave-project-button.test.tsx
+++ b/services/web/test/frontend/features/project-list/components/table/cells/action-buttons/leave-project-button.test.tsx
@@ -22,7 +22,7 @@ describe('', function () {
renderWithProjectListContext(
)
- const btn = screen.getByLabelText('Leave')
+ const btn = screen.getByRole('button', { name: 'Leave' })
fireEvent.mouseOver(btn)
screen.getByRole('tooltip', { name: 'Leave' })
})
@@ -32,7 +32,7 @@ describe('', function () {
renderWithProjectListContext(
)
- const btn = screen.queryByLabelText('Leave')
+ const btn = screen.queryByRole('button', { name: 'Leave' })
expect(btn).to.be.null
})
@@ -40,14 +40,14 @@ describe('', function () {
renderWithProjectListContext(
)
- expect(screen.queryByLabelText('Leave')).to.be.null
+ expect(screen.queryByRole('button', { name: 'Leave' })).to.be.null
})
it('does not render the button when project is current', function () {
renderWithProjectListContext(
)
- expect(screen.queryByLabelText('Leave')).to.be.null
+ expect(screen.queryByRole('button', { name: 'Leave' })).to.be.null
})
it('opens the modal and leaves the project', async function () {
@@ -62,7 +62,7 @@ describe('', function () {
renderWithProjectListContext(
)
- const btn = screen.getByLabelText('Leave')
+ const btn = screen.getByRole('button', { name: 'Leave' })
fireEvent.click(btn)
screen.getByText('Leave Projects')
screen.getByText('You are about to leave the following projects:')
diff --git a/services/web/test/frontend/features/project-list/components/table/cells/action-buttons/trash-project-button.test.tsx b/services/web/test/frontend/features/project-list/components/table/cells/action-buttons/trash-project-button.test.tsx
index 6506b86e25..a62627226e 100644
--- a/services/web/test/frontend/features/project-list/components/table/cells/action-buttons/trash-project-button.test.tsx
+++ b/services/web/test/frontend/features/project-list/components/table/cells/action-buttons/trash-project-button.test.tsx
@@ -20,7 +20,7 @@ describe('', function () {
renderWithProjectListContext(
)
- const btn = screen.getByLabelText('Trash')
+ const btn = screen.getByRole('button', { name: 'Trash' })
fireEvent.mouseOver(btn)
screen.getByRole('tooltip', { name: 'Trash' })
})
@@ -29,7 +29,7 @@ describe('', function () {
renderWithProjectListContext(
)
- expect(screen.queryByLabelText('Trash')).to.be.null
+ expect(screen.queryByRole('button', { name: 'Trash' })).to.be.null
})
it('opens the modal and trashes the project', async function () {
@@ -44,7 +44,7 @@ describe('', function () {
renderWithProjectListContext(
)
- const btn = screen.getByLabelText('Trash')
+ const btn = screen.getByRole('button', { name: 'Trash' })
fireEvent.click(btn)
screen.getByText('Trash Projects')
screen.getByText('You are about to trash the following projects:')
diff --git a/services/web/test/frontend/features/project-list/components/table/cells/action-buttons/unarchive-project-button.test.tsx b/services/web/test/frontend/features/project-list/components/table/cells/action-buttons/unarchive-project-button.test.tsx
index aa7f9429be..3bbfa57085 100644
--- a/services/web/test/frontend/features/project-list/components/table/cells/action-buttons/unarchive-project-button.test.tsx
+++ b/services/web/test/frontend/features/project-list/components/table/cells/action-buttons/unarchive-project-button.test.tsx
@@ -21,7 +21,7 @@ describe('', function () {
renderWithProjectListContext(
)
- const btn = screen.getByLabelText('Restore')
+ const btn = screen.getByRole('button', { name: 'Restore' })
fireEvent.mouseOver(btn)
screen.getByRole('tooltip', { name: 'Restore' })
})
@@ -30,14 +30,14 @@ describe('', function () {
renderWithProjectListContext(
)
- expect(screen.queryByLabelText('Restore')).to.be.null
+ expect(screen.queryByRole('button', { name: 'Restore' })).to.be.null
})
it('does not render the button when project is current', function () {
renderWithProjectListContext(
)
- expect(screen.queryByLabelText('Restore')).to.be.null
+ expect(screen.queryByRole('button', { name: 'Restore' })).to.be.null
})
it('unarchive the project and updates the view data', async function () {
@@ -52,7 +52,7 @@ describe('', function () {
renderWithProjectListContext(
)
- const btn = screen.getByLabelText('Restore')
+ const btn = screen.getByRole('button', { name: 'Restore' })
fireEvent.click(btn)
await waitFor(
diff --git a/services/web/test/frontend/features/project-list/components/table/cells/action-buttons/untrash-project-button.test.tsx b/services/web/test/frontend/features/project-list/components/table/cells/action-buttons/untrash-project-button.test.tsx
index 5ea8f21a07..2aff62529c 100644
--- a/services/web/test/frontend/features/project-list/components/table/cells/action-buttons/untrash-project-button.test.tsx
+++ b/services/web/test/frontend/features/project-list/components/table/cells/action-buttons/untrash-project-button.test.tsx
@@ -20,7 +20,7 @@ describe('', function () {
renderWithProjectListContext(
)
- const btn = screen.getByLabelText('Restore')
+ const btn = screen.getByRole('button', { name: 'Restore' })
fireEvent.mouseOver(btn)
screen.getByRole('tooltip', { name: 'Restore' })
})
@@ -29,7 +29,7 @@ describe('', function () {
renderWithProjectListContext(
)
- expect(screen.queryByLabelText('Restore')).to.be.null
+ expect(screen.queryByRole('button', { name: 'Restore' })).to.be.null
})
it('untrashes the project and updates the view data', async function () {
@@ -44,7 +44,7 @@ describe('', function () {
renderWithProjectListContext(
)
- const btn = screen.getByLabelText('Restore')
+ const btn = screen.getByRole('button', { name: 'Restore' })
fireEvent.click(btn)
await waitFor(
diff --git a/services/web/test/frontend/features/project-list/components/table/project-list-table.test.tsx b/services/web/test/frontend/features/project-list/components/table/project-list-table.test.tsx
index 0ea5c4c7ff..e0accb3c06 100644
--- a/services/web/test/frontend/features/project-list/components/table/project-list-table.test.tsx
+++ b/services/web/test/frontend/features/project-list/components/table/project-list-table.test.tsx
@@ -108,15 +108,25 @@ describe('', function () {
// Action Column
// temporary count tests until we add filtering for archived/trashed
- const copyButtons = screen.getAllByLabelText('Copy')
+ const copyButtons = screen.getAllByRole('button', {
+ name: 'Copy',
+ })
expect(copyButtons.length).to.equal(currentProjects.length)
- const downloadButtons = screen.getAllByLabelText('Download .zip file')
+ const downloadButtons = screen.getAllByRole('button', {
+ name: 'Download .zip file',
+ })
expect(downloadButtons.length).to.equal(currentProjects.length)
- const downloadPDFButtons = screen.getAllByLabelText('Download PDF')
+ const downloadPDFButtons = screen.getAllByRole('button', {
+ name: 'Download PDF',
+ })
expect(downloadPDFButtons.length).to.equal(currentProjects.length)
- const archiveButtons = screen.getAllByLabelText('Archive')
+ const archiveButtons = screen.getAllByRole('button', {
+ name: 'Archive',
+ })
expect(archiveButtons.length).to.equal(currentProjects.length)
- const trashButtons = screen.getAllByLabelText('Trash')
+ const trashButtons = screen.getAllByRole('button', {
+ name: 'Trash',
+ })
expect(trashButtons.length).to.equal(currentProjects.length)
// TODO to be implemented when the component renders trashed & archived projects
diff --git a/services/web/test/frontend/features/project-list/components/table/project-tools/buttons/archive-projects.button.test.tsx b/services/web/test/frontend/features/project-list/components/table/project-tools/buttons/archive-projects.button.test.tsx
index d83b5f2189..a88ac41620 100644
--- a/services/web/test/frontend/features/project-list/components/table/project-tools/buttons/archive-projects.button.test.tsx
+++ b/services/web/test/frontend/features/project-list/components/table/project-tools/buttons/archive-projects.button.test.tsx
@@ -12,14 +12,14 @@ describe('', function () {
it('renders tooltip for button', function () {
renderWithProjectListContext()
- const btn = screen.getByLabelText('Archive')
+ const btn = screen.getByRole('button', { name: 'Archive' })
fireEvent.mouseOver(btn)
screen.getByRole('tooltip', { name: 'Archive' })
})
it('opens the modal when clicked', function () {
renderWithProjectListContext()
- const btn = screen.getByLabelText('Archive')
+ const btn = screen.getByRole('button', { name: 'Archive' })
fireEvent.click(btn)
screen.getByText('Archive Projects')
})
diff --git a/services/web/test/frontend/features/project-list/components/table/project-tools/buttons/download-projects-button.test.tsx b/services/web/test/frontend/features/project-list/components/table/project-tools/buttons/download-projects-button.test.tsx
index 92ca1fa49e..62ad38efbf 100644
--- a/services/web/test/frontend/features/project-list/components/table/project-tools/buttons/download-projects-button.test.tsx
+++ b/services/web/test/frontend/features/project-list/components/table/project-tools/buttons/download-projects-button.test.tsx
@@ -12,7 +12,7 @@ describe('', function () {
it('renders tooltip for button', function () {
renderWithProjectListContext()
- const btn = screen.getByLabelText('Download')
+ const btn = screen.getByRole('button', { name: 'Download' })
fireEvent.mouseOver(btn)
screen.getByRole('tooltip', { name: 'Download' })
})
diff --git a/services/web/test/frontend/features/project-list/components/table/project-tools/buttons/trash-projects.button.test.tsx b/services/web/test/frontend/features/project-list/components/table/project-tools/buttons/trash-projects.button.test.tsx
index 2b6064dbb7..17da41f29f 100644
--- a/services/web/test/frontend/features/project-list/components/table/project-tools/buttons/trash-projects.button.test.tsx
+++ b/services/web/test/frontend/features/project-list/components/table/project-tools/buttons/trash-projects.button.test.tsx
@@ -12,14 +12,14 @@ describe('', function () {
it('renders tooltip for button', function () {
renderWithProjectListContext()
- const btn = screen.getByLabelText('Trash')
+ const btn = screen.getByRole('button', { name: 'Trash' })
fireEvent.mouseOver(btn)
screen.getByRole('tooltip', { name: 'Trash' })
})
it('opens the modal when clicked', function () {
renderWithProjectListContext()
- const btn = screen.getByLabelText('Trash')
+ const btn = screen.getByRole('button', { name: 'Trash' })
fireEvent.click(btn)
screen.getByText('Trash Projects')
})