DRY cleanup of branchesAndTagsResults function

7e91e5198a99904b6eb94e965a3113afc3b78984

Tucker McKnight <tmcknight@instructure.com> | Sat Jun 20 2026

DRY cleanup of branchesAndTagsResults function

Also fix bug where I was calling ref.type, which is undefined. It's
data.currentRefType on that page.
js_templates/common/branchesListItems.ts:1
Before
0
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37

38
39
40
41
42
43
44
45



46







47

48

49
50
51


52

53
54
55
56
57
58



59
60

61
62
63
64
65
66
67
68
69
70
71
72
73
export const branchesAndTagsResults = (
  rels: {
    branches: Array<{name: string, href: string, date: string}>,
    tags: Array<{name: string, href: string, date: string}>,
  },
  defaultBranch: string,
  currentRef: string,
  currentRefType: "branch" | "tag",
  sortBy: 'name' | 'date',
): string => {
  return `<div class="branch">` + rels.branches.sort((a, b) => {
    let comparison = 0
    if (a[sortBy] < b[sortBy]) { comparison = -1 }
    if (a[sortBy] > b[sortBy]) { comparison = 1 }
    // we want reverse order if sorting by date (making newest first)
    if (sortBy === 'date') { comparison = comparison * -1 }

    return comparison
  }).map((branch) => {
    const currentBadge = currentRef === branch.name && currentRefType === "branch"
      ? '<div class="badge rounded-pill bg-secondary mx-1">current</div>'
      : ''

    const defaultBadge = defaultBranch === branch.name
      ? '<div class="badge rounded-pill bg-info text-dark mx-1">default</div>'
      : ''

    return `
      <a href='${branch.href}' class='dropdown-item my-1'>
        <span class="branch-dropdown-branch-name me-1">
          ${branch.name}
        </span>${currentBadge}${defaultBadge}
        <span class="text-body d-block ms-2">
          updated ${new Date(branch.date).toDateString()}
        </span>
      </a>
    `
  }).join('') + `</div>  <div class="tag">` + rels.tags.sort((a, b) => {
    let comparison = 0
    if (a[sortBy] < b[sortBy]) { comparison = -1 }
    if (a[sortBy] > b[sortBy]) { comparison = 1 }
    // we want reverse order if sorting by date (making newest first)
    if (sortBy === 'date') { comparison = comparison * -1 }

    return comparison
⁣
⁣
⁣
  }).map((tag) => {
⁣
⁣
⁣
⁣
⁣
⁣
⁣
    const currentBadge = currentRef === tag.name && currentRefType === "tag"      ? '<div class="badge rounded-pill bg-secondary mx-1">current</div>'
⁣
      : ''

    return `
⁣
⁣
      <a href='${tag.href}' class='dropdown-item my-1'>        <span class="branch-dropdown-branch-name me-1">
          ${tag.name}
        </span>${currentBadge}
        <span class="text-body d-block ms-2">
          updated ${new Date(tag.date).toDateString()}
        </span>
⁣
⁣
⁣
      </a>
    `  }).join('') + `</div>`
}

export default (
  rels: {
    branches: Array<{name: string, href: string, date: string}>,
    tags: Array<{name: string, href: string, date: string}>,
  },
  defaultBranch: string,
  currentRef: string,
  currentRefType: "branch" | "tag",
  sortBy: 'name' | 'date',
): string => {
  return `<div class="rel-dropdown" data-selected="${currentRefType}">
After


0




1
2


3





4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45

46
47
48

49
50
51
52





53
54
55
56
57
58
59
60


61
62
63
64
65
66
67
68
69
70
<⁣
⁣
⁣
mark>type Ref = { name: string, href: string, date: string }
⁣
⁣
⁣
⁣
type RefType = 'branch' | 'tag'

⁣
⁣
// These are the menu items that show up in the ref: dropdown menu.
⁣
⁣
⁣
⁣
⁣
const htmlForRef = (ref: Ref, refType: RefType, currentPageRef: string, currentPageRefType: RefType, defaultBranch: string) => {
  const currentBadge = currentPageRef === ref.name && currentPageRefType === refType
    ? '<div class="badge rounded-pill bg-secondary mx-1">current</div>'
    : ''

  const defaultBadge = defaultBranch === ref.name && refType === 'branch'
    ? '<div class="badge rounded-pill bg-info text-dark mx-1">default</div>'
    : ''

  return `
    <a href='${ref.href}' class='dropdown-item my-1'>
      <span class="branch-dropdown-branch-name me-1">
        ${ref.name}
      </span>${currentBadge}${defaultBadge}
      <span class="text-body d-block ms-2">
        updated ${new Date(ref.date).toDateString()}
      </span>
    </a>
  `
}

const sortFunction = (a, b, sortBy) => {
  let comparison = 0
  if (a[sortBy] < b[sortBy]) { comparison = -1 }
  if (a[sortBy] > b[sortBy]) { comparison = 1 }
  // we want reverse order if sorting by date (making newest first)
  if (sortBy === 'date') { comparison = comparison * -1 }

  return comparison
}
// This is the full html for the contents of the ref: dropdown menu.
export const branchesAndTagsResults = (
  refs: {
    branches: Array<Ref>,
    tags: Array<Ref>,
  },
  defaultBranch: string,
  currentRef: string,
  currentRefType: RefType,
  sortBy: 'name' | 'date',
): string => {
  const branchItemsHtml = refs.branches.sort((a, b) => sortFunction(a, b, sortBy))
⁣
    .map(branch => htmlForRef(branch, 'branch', currentRef, currentRefType, defaultBranch))
    .join('')  const tagItemsHtml = refs.tags.sort((a, b) => sortFunction(a, b, sortBy))
    .map(tag => htmlForRef(tag, 'tag', currentRef, currentRefType, defaultBranch))
    .join('')
  
⁣
⁣
⁣
⁣
⁣
  return `<div class="branch">${branchItemsHtml}</div><div class="tag">${tagItemsHtml}</div>`
}


// Note: this function returns a plain string of HTML, instead of using mithril, because
// it is called directly by both the backend *and* the frontend, and the frontend does not
// include mithril. Ideally, the frontend should not need mithril; there are very few
// dynamic pieces on the frontend.
⁣
⁣
export default (
  refs: {
    branches: Array<Ref>,
    tags: Array<Ref>,
  },
  defaultBranch: string,
  currentRef: string,
  currentRefType: RefType,
  sortBy: 'name' | 'date',
): string => {
  return `<div class="rel-dropdown" data-selected="${currentRefType}">
js_templates/common/branchesListItems.ts:82
Before
81
82
83
84
85
86
      </li>
    </ul>
    <div id="branches-and-tags-list">
      ${branchesAndTagsResults(rels, defaultBranch, currentRef, currentRefType, sortBy)}
    </div>
  </div>`
}
After
81
82
83
84
85
86
      </li>
    </ul>
    <div id="branches-and-tags-list">
      ${branchesAndTagsResults(refs, defaultBranch, currentRef, currentRefType, sortBy)}
    </div>
  </div>`
}
js_templates/common/htmlPage.ts:47
Before
46
47
48
49
50
51
        window.branchesWithHrefs = ${JSON.stringify(branchesWithHrefs)};
        window.defaultBranch = "${repo.defaultBranch}";
        window.currentRef = "${ref.name}";
        window.currentRefType = "${ref.type}";
        window.cloneUrl = "${repo.cloneUrl}";
      `)),
      m('link', {
After
46
47
48
49
50
51
        window.branchesWithHrefs = ${JSON.stringify(branchesWithHrefs)};
        window.defaultBranch = "${repo.defaultBranch}";
        window.currentRef = "${ref.name}";
        window.currentRefType = "${data.currentRefType}";
        window.cloneUrl = "${repo.cloneUrl}";
      `)),
      m('link', {