diff --git a/src/tasks/tables/columns/AllTasksColumns.test.tsx b/src/tasks/tables/columns/AllTasksColumns.test.tsx
new file mode 100644
index 00000000..ec855cac
--- /dev/null
+++ b/src/tasks/tables/columns/AllTasksColumns.test.tsx
@@ -0,0 +1,63 @@
+import React from "react"
+import { describe, expect, test, vi } from "vitest"
+import { cleanup, render, screen } from "@testing-library/react"
+
+// The generated route helpers only exist after a Blitz build, so stand in for the two used here
+vi.mock("@blitzjs/next", () => ({
+ Routes: {
+ ShowTaskPage: ({ projectId, taskId }: { projectId: number; taskId: number }) =>
+ `/projects/${projectId}/tasks/${taskId}`,
+ ShowProjectPage: ({ projectId }: { projectId: number }) => `/projects/${projectId}`,
+ },
+}))
+
+// Next's Link needs a router to render; a plain anchor is enough to check where it points
+vi.mock("next/link", () => ({
+ default: ({ href, children, ...rest }: any) => (
+
+ {children}
+
+ ),
+}))
+
+import { AllTasksColumns } from "./AllTasksColumns"
+
+const row = {
+ name: "Collect data",
+ projectName: "Big Study",
+ view: { taskId: 3, projectId: 9 },
+}
+
+const renderCell = (accessorKey: string, value: string) => {
+ const column: any = AllTasksColumns.find((col: any) => col.accessorKey === accessorKey)
+ render(column.cell({ getValue: () => value, row: { original: row } }))
+}
+
+describe("AllTasksColumns", () => {
+ test("the task name links to the task page", () => {
+ renderCell("name", "Collect data")
+ expect(screen.getByRole("link", { name: "Collect data" }).getAttribute("href")).toBe(
+ "/projects/9/tasks/3"
+ )
+ })
+
+ test("they are buttons: primary for the task, secondary for the project", () => {
+ renderCell("name", "Collect data")
+ expect(screen.getByRole("link", { name: "Collect data" }).className).toContain("btn-primary")
+ cleanup()
+ renderCell("projectName", "Big Study")
+ expect(screen.getByRole("link", { name: "Big Study" }).className).toContain("btn-secondary")
+ })
+
+ test("a long name is shortened on the button but kept in full as a tooltip", () => {
+ const longName = "A very long task name that would not fit"
+ renderCell("name", longName)
+ const link = screen.getByRole("link", { name: "A very long task nam..." })
+ expect(link.getAttribute("title")).toBe(longName)
+ })
+
+ test("the project name links to the project page", () => {
+ renderCell("projectName", "Big Study")
+ expect(screen.getByRole("link", { name: "Big Study" }).getAttribute("href")).toBe("/projects/9")
+ })
+})
diff --git a/src/tasks/tables/columns/AllTasksColumns.tsx b/src/tasks/tables/columns/AllTasksColumns.tsx
index 0cbabf96..d1ecb1ae 100644
--- a/src/tasks/tables/columns/AllTasksColumns.tsx
+++ b/src/tasks/tables/columns/AllTasksColumns.tsx
@@ -10,6 +10,9 @@ import { createDateTextFilter } from "src/core/utils/tableFilters"
// Column helper
const columnHelperAll = createColumnHelper()
+// Long names are shortened so the button keeps its height (same rule as the project tasks table)
+const truncateName = (name: string) => (name.length > 20 ? `${name.slice(0, 20)}...` : name)
+
const dueDateTextFilter = createDateTextFilter({ emptyLabel: "no due date" })
// ColumnDefs
@@ -17,7 +20,19 @@ export const AllTasksColumns = [
columnHelperAll.accessor("name", {
enableColumnFilter: true,
enableSorting: true,
- cell: (info) => {info.getValue()},
+ cell: (info) => {
+ const { taskId, projectId } = info.row.original.view
+ const name = info.getValue()
+ return (
+
+ {truncateName(name)}
+
+ )
+ },
header: "Name",
meta: {
filterVariant: "text",
@@ -26,7 +41,15 @@ export const AllTasksColumns = [
columnHelperAll.accessor("projectName", {
enableColumnFilter: true,
enableSorting: true,
- cell: (info) => {info.getValue()},
+ cell: (info) => (
+
+ {truncateName(info.getValue())}
+
+ ),
header: "Project",
meta: {
filterVariant: "select",