ci: merge main to release (#7871)

ci: merge main to release
This commit is contained in:
Robert Sparks 2024-08-28 15:47:18 -05:00 committed by GitHub
commit 4635e3dd42
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
24 changed files with 297 additions and 98 deletions

View file

@ -434,7 +434,7 @@ function reconnectScrollObservers () {
scrollObserver.disconnect()
visibleDays.length = 0
for (const mDay of agendaStore.meetingDays) {
const el = document.getElementById(`agenda-day-${mDay.slug}`)
const el = document.getElementById(mDay.slug)
el.dataset.dayId = mDay.slug.toString()
el.dataset.dayTs = mDay.ts
scrollObserver.observe(el)

View file

@ -29,7 +29,6 @@
<script setup>
import { computed, h } from 'vue'
import {
NBadge,
NDropdown,
@ -51,21 +50,48 @@ const siteStore = useSiteStore()
// Meeting Days
function optionToLink(opts){
const { key, label, icon } = opts
return {
...opts,
type: 'render',
render: () => h(
'a',
{
class: 'dropdown-link',
'data-testid': 'mobile-link',
href: `#${key}`
},
[
h(
'span',
icon()
),
h(
'span',
label
)
]
)
}
}
const jumpToDayOptions = computed(() => {
const days = []
if (agendaStore.isMeetingLive) {
days.push({
days.push(optionToLink({
label: 'Jump to Now',
key: 'now',
icon: () => h('i', { class: 'bi bi-arrow-down-right-square text-red' })
})
}))
}
for (const day of agendaStore.meetingDays) {
days.push({
days.push(optionToLink({
label: `Jump to ${day.label}`,
key: day.slug,
icon: () => h('i', { class: 'bi bi-arrow-down-right-square' })
})
}))
}
return days
})
@ -90,14 +116,13 @@ const downloadIcsOptions = [
function jumpToDay (dayId) {
if (dayId === 'now') {
const lastEventId = agendaStore.findCurrentEventId()
if (lastEventId) {
document.getElementById(`agenda-rowid-${lastEventId}`)?.scrollIntoView(true)
} else {
message.warning('There is no event happening right now.')
}
} else {
document.getElementById(`agenda-day-${dayId}`)?.scrollIntoView(true)
document.getElementById(dayId)?.scrollIntoView(true)
}
}
@ -162,4 +187,19 @@ function downloadIcs (key) {
}
}
}
.dropdown-link {
display: flex;
text-decoration:none;
gap: 0.2rem 0.5rem;
padding: 0.5em;
color: var(--bs-body-color);
&:hover,
&:focus {
background-color: var(--bs-dark-bg-subtle);
text-decoration: underline;
}
}
</style>

View file

@ -99,7 +99,7 @@
li.nav-item(v-for='day of agendaStore.meetingDays')
a.nav-link(
:class='agendaStore.dayIntersectId === day.slug ? `active` : ``'
:href='`#slot-` + day.slug'
:href='`#${day.slug}`'
@click='scrollToDay(day.slug, $event)'
)
i.bi.bi-arrow-right-short.d-none.d-xxl-inline.me-2
@ -109,7 +109,6 @@
<script setup>
import { computed, h } from 'vue'
import { useRoute, useRouter } from 'vue-router'
import { DateTime } from 'luxon'
import {
NAffix,
NBadge,
@ -200,14 +199,11 @@ function pickerDiscard () {
}
}
function scrollToDay (dayId, ev) {
ev.preventDefault()
document.getElementById(`agenda-day-${dayId}`)?.scrollIntoView(true)
function scrollToDay (daySlug, ev) {
document.getElementById(daySlug)?.scrollIntoView(true)
}
function scrollToNow (ev) {
ev.preventDefault()
const lastEventId = agendaStore.findCurrentEventId()
if (lastEventId) {

View file

@ -24,7 +24,7 @@
)
//- ROW - DAY HEADING -----------------------
template(v-if='item.displayType === `day`')
td(:id='`agenda-day-` + item.id', :colspan='pickerModeActive ? 6 : 5') {{item.date}}
td(:id='item.slug', :colspan='pickerModeActive ? 6 : 5') {{item.date}}
//- ROW - SESSION HEADING -------------------
template(v-else-if='item.displayType === `session-head`')
td.agenda-table-cell-check(v-if='pickerModeActive') &nbsp;
@ -200,7 +200,7 @@ import {
import AgendaDetailsModal from './AgendaDetailsModal.vue'
import { useAgendaStore } from './store'
import { useAgendaStore, daySlugPrefix, daySlug } from './store'
import { useSiteStore } from '../shared/store'
import { getUrl } from '../shared/urls'
@ -248,6 +248,7 @@ const meetingEvents = computed(() => {
if (itemDate.toISODate() !== acc.lastDate) {
acc.result.push({
id: item.id,
slug: daySlug(item),
key: `day-${itemDate.toISODate()}`,
displayType: 'day',
date: itemDate.toLocaleString(DateTime.DATE_HUGE),
@ -575,6 +576,30 @@ function recalculateRedLine () {
}
}
/**
* On page load when browser location hash contains '#now' or '#agenda-day-*' then scroll accordingly
*/
;(function scrollToHashInit() {
if (!window.location.hash) {
return
}
if (!(window.location.hash === "#now" || window.location.hash.startsWith(`#${daySlugPrefix}`))) {
return
}
const unsubscribe = agendaStore.$subscribe((_mutation, agendaStoreState) => {
if (agendaStoreState.schedule.length === 0) {
return
}
unsubscribe() // we only need to scroll once, so unsubscribe from future updates
if(window.location.hash === "#now") {
const lastEventId = agendaStore.findCurrentEventId()
document.getElementById(`agenda-rowid-${lastEventId}`)?.scrollIntoView(true)
} else if(window.location.hash.startsWith(`#${daySlugPrefix}`)) {
document.getElementById(window.location.hash.substring(1))?.scrollIntoView(true)
}
})
})()
// MOUNTED
onMounted(() => {

View file

@ -121,7 +121,7 @@ export const useAgendaStore = defineStore('agenda', {
meetingDays () {
const siteStore = useSiteStore()
return uniqBy(this.scheduleAdjusted, 'adjustedStartDate').sort().map(s => ({
slug: s.id.toString(),
slug: daySlug(s),
ts: s.adjustedStartDate,
label: siteStore.viewport < 1350 ? DateTime.fromISO(s.adjustedStartDate).toFormat('ccc LLL d') : DateTime.fromISO(s.adjustedStartDate).toLocaleString(DateTime.DATE_HUGE)
}))
@ -292,3 +292,8 @@ function findFirstConferenceUrl (txt) {
} catch (err) { }
return null
}
export const daySlugPrefix = 'agenda-day-'
export function daySlug(s) {
return `${daySlugPrefix}${s.adjustedStartDate}` // eg 'agenda-day-2024-08-13'
}

View file

@ -1,11 +1,11 @@
import { createApp } from 'vue'
import { createPinia } from 'pinia'
import piniaPersist from 'pinia-plugin-persist'
import Embedded from './Embedded.vue'
import { createPiniaSingleton } from './shared/create-pinia-singleton'
// Initialize store (Pinia)
const pinia = createPinia()
const pinia = createPiniaSingleton()
pinia.use(piniaPersist)
// Mount App

View file

@ -1,14 +1,14 @@
import { createApp } from 'vue'
import { createPinia } from 'pinia'
import piniaPersist from 'pinia-plugin-persist'
import App from './App.vue'
import router from './router'
import { createPiniaSingleton } from './shared/create-pinia-singleton'
const app = createApp(App, {})
// Initialize store (Pinia)
const pinia = createPinia()
const pinia = createPiniaSingleton()
pinia.use(piniaPersist)
app.use(pinia)

View file

@ -0,0 +1,6 @@
import { createPinia } from 'pinia'
export function createPiniaSingleton(){
window.pinia = window.pinia ?? createPinia()
return window.pinia
}

View file

@ -0,0 +1,30 @@
# Copyright The IETF Trust 2024, All Rights Reserved
from django.db import migrations
def forward(apps, schema_editor):
State = apps.get_model("doc", "State")
State.objects.get_or_create(
type_id="bofreq",
slug="spam",
defaults={"name": "Spam", "desc": "The BOF request is spam", "order": 5},
)
def reverse(apps, schema_editor):
State = apps.get_model("doc", "State")
Document = apps.get_model("doc", "Document")
assert not Document.objects.filter(
states__type="bofreq", states__slug="spam"
).exists()
State.objects.filter(type_id="bofreq", slug="spam").delete()
class Migration(migrations.Migration):
dependencies = [
("doc", "0022_remove_dochistory_internal_comments_and_more"),
]
operations = [migrations.RunPython(forward, reverse)]

View file

@ -96,9 +96,14 @@ def ballot_icon(context, doc):
positions = list(ballot.active_balloter_positions().items())
positions.sort(key=sort_key)
request = context.get("request")
ballot_edit_return_point_param = f"ballot_edit_return_point={request.path}"
right_click_string = ''
if has_role(user, "Area Director"):
right_click_string = 'oncontextmenu="window.location.href=\'%s\';return false;"' % urlreverse('ietf.doc.views_ballot.edit_position', kwargs=dict(name=doc.name, ballot_id=ballot.pk))
right_click_string = 'oncontextmenu="window.location.href=\'{}?{}\';return false;"'.format(
urlreverse('ietf.doc.views_ballot.edit_position', kwargs=dict(name=doc.name, ballot_id=ballot.pk)),
ballot_edit_return_point_param)
my_blocking = False
for i, (balloter, pos) in enumerate(positions):
@ -114,9 +119,13 @@ def ballot_icon(context, doc):
else:
typename = "IESG"
modal_url = "{}?{}".format(
urlreverse("ietf.doc.views_doc.ballot_popup", kwargs=dict(name=doc.name, ballot_id=ballot.pk)),
ballot_edit_return_point_param)
res = ['<a %s href="%s" data-bs-toggle="modal" data-bs-target="#modal-%d" aria-label="%s positions" title="%s positions (click to show more)" class="ballot-icon"><table' % (
right_click_string,
urlreverse("ietf.doc.views_doc.ballot_popup", kwargs=dict(name=doc.name, ballot_id=ballot.pk)),
modal_url,
ballot.pk,
typename,
typename,)]

View file

@ -0,0 +1,29 @@
# Copyright The IETF Trust 2015-2020, All Rights Reserved
from django import template
from django.conf import settings
from django.template.loader import render_to_string
from ietf.utils.log import log
register = template.Library()
@register.simple_tag
def document_type_badge(doc, snapshot, submission, resurrected_by):
context = {"doc": doc, "snapshot": snapshot, "submission": submission, "resurrected_by": resurrected_by}
if doc.type_id == "rfc":
return render_to_string(
"doc/badge/doc-badge-rfc.html",
context,
)
elif doc.type_id == "draft":
return render_to_string(
"doc/badge/doc-badge-draft.html",
context,
)
else:
error_message = f"Unsupported document type {doc.type_id}."
if settings.SERVER_MODE != 'production':
raise ValueError(error_message)
else:
log(error_message)
return ""

View file

@ -230,6 +230,9 @@ class EditPositionTests(TestCase):
r = self.client.post(url, dict(position="discuss", discuss="Test discuss text"))
self.assertEqual(r.status_code, 403)
# N.B. This test needs to be rewritten to exercise all types of ballots (iesg, irsg, rsab)
# and test against the output of the mailtriggers instead of looking for hardcoded values
# in the To and CC results. See #7864
def test_send_ballot_comment(self):
ad = Person.objects.get(user__username="ad")
draft = WgDraftFactory(ad=ad,group__acronym='mars')
@ -1455,18 +1458,14 @@ class BallotContentTests(TestCase):
class ReturnToUrlTests(TestCase):
def test_invalid_return_to_url(self):
self.assertRaises(
Exception,
lambda: parse_ballot_edit_return_point('/doc/', 'draft-ietf-opsawg-ipfix-tcpo-v6eh', '998718'),
)
self.assertRaises(
Exception,
lambda: parse_ballot_edit_return_point('/a-route-that-does-not-exist/', 'draft-ietf-opsawg-ipfix-tcpo-v6eh', '998718'),
)
self.assertRaises(
Exception,
lambda: parse_ballot_edit_return_point('https://example.com/phishing', 'draft-ietf-opsawg-ipfix-tcpo-v6eh', '998718'),
)
with self.assertRaises(ValueError):
parse_ballot_edit_return_point('/', 'draft-ietf-opsawg-ipfix-tcpo-v6eh', '998718')
with self.assertRaises(ValueError):
parse_ballot_edit_return_point('/a-route-that-does-not-exist/', 'draft-ietf-opsawg-ipfix-tcpo-v6eh', '998718')
with self.assertRaises(ValueError):
parse_ballot_edit_return_point('https://example.com/phishing', 'draft-ietf-opsawg-ipfix-tcpo-v6eh', '998718')
def test_valid_default_return_to_url(self):
self.assertEqual(parse_ballot_edit_return_point(

View file

@ -54,8 +54,8 @@ This test section has some text.
self.assertEqual(r.status_code, 200)
q = PyQuery(r.content)
for state in states:
self.assertEqual(len(q(f'#bofreqs-{state.slug}')), 1)
self.assertEqual(len(q(f'#bofreqs-{state.slug} tbody tr')), 3)
self.assertEqual(len(q(f'#bofreqs-{state.slug}')), 1 if state.slug!="spam" else 0)
self.assertEqual(len(q(f'#bofreqs-{state.slug} tbody tr')), 3 if state.slug!="spam" else 0)
self.assertFalse(q('#start_button'))
PersonFactory(user__username='nobody')
self.client.login(username='nobody', password='nobody+password')
@ -63,6 +63,13 @@ This test section has some text.
self.assertEqual(r.status_code, 200)
q = PyQuery(r.content)
self.assertTrue(q('#start_button'))
self.client.logout()
self.client.login(username='secretary', password='secretary+password')
r = self.client.get(url)
q = PyQuery(r.content)
for state in states:
self.assertEqual(len(q(f'#bofreqs-{state.slug}')), 1)
self.assertEqual(len(q(f'#bofreqs-{state.slug} tbody tr')), 3)
def test_bofreq_main_page(self):

View file

@ -323,6 +323,8 @@ def build_position_email(balloter, doc, pos):
if doc.stream_id == "irtf":
addrs = gather_address_lists('irsg_ballot_saved',doc=doc)
elif doc.stream_id == "editorial":
addrs = gather_address_lists('rsab_ballot_saved',doc=doc)
else:
addrs = gather_address_lists('iesg_ballot_saved',doc=doc)
@ -1314,10 +1316,23 @@ def rsab_ballot_status(request):
def parse_ballot_edit_return_point(path, doc_name, ballot_id):
get_default_path = lambda: urlreverse("ietf.doc.views_doc.document_ballot", kwargs=dict(name=doc_name, ballot_id=ballot_id))
allowed_path_handlers = {
"ietf.community.views.view_list",
"ietf.doc.views_doc.document_ballot",
"ietf.doc.views_doc.document_irsg_ballot",
"ietf.doc.views_doc.document_rsab_ballot",
"ietf.doc.views_ballot.irsg_ballot_status",
"ietf.doc.views_ballot.rsab_ballot_status",
"ietf.doc.views_search.search",
"ietf.doc.views_search.docs_for_ad",
"ietf.doc.views_search.drafts_in_last_call",
"ietf.doc.views_search.recent_drafts",
"ietf.group.views.chartering_groups",
"ietf.group.views.group_documents",
"ietf.group.views.stream_documents",
"ietf.iesg.views.agenda",
"ietf.iesg.views.agenda_documents",
"ietf.iesg.views.discusses",
"ietf.iesg.views.past_documents",
}
return validate_return_to_path(path, get_default_path, allowed_path_handlers)

View file

@ -43,7 +43,7 @@ from pathlib import Path
from django.core.cache import caches
from django.db.models import Max
from django.http import HttpResponse, Http404
from django.http import HttpResponse, Http404, HttpResponseBadRequest
from django.shortcuts import render, get_object_or_404, redirect
from django.template.loader import render_to_string
from django.urls import reverse as urlreverse
@ -73,6 +73,7 @@ from ietf.ietfauth.utils import ( has_role, is_authorized_in_doc_stream, user_is
role_required, is_individual_draft_author, can_request_rfc_publication)
from ietf.name.models import StreamName, BallotPositionName
from ietf.utils.history import find_history_active_at
from ietf.doc.views_ballot import parse_ballot_edit_return_point
from ietf.doc.forms import InvestigateForm, TelechatForm, NotifyForm, ActionHoldersForm, DocAuthorForm, DocAuthorChangeBasisForm
from ietf.doc.mails import email_comment, email_remind_action_holders
from ietf.mailtrigger.utils import gather_relevant_expansions
@ -1586,11 +1587,18 @@ def ballot_popup(request, name, ballot_id):
doc = get_object_or_404(Document, name=name)
c = document_ballot_content(request, doc, ballot_id=ballot_id, editable=False)
ballot = get_object_or_404(BallotDocEvent,id=ballot_id)
try:
return_to_url = parse_ballot_edit_return_point(request.GET.get('ballot_edit_return_point'), name, ballot_id)
except ValueError:
return HttpResponseBadRequest('ballot_edit_return_point is invalid')
return render(request, "doc/ballot_popup.html",
dict(doc=doc,
ballot_content=c,
ballot_id=ballot_id,
ballot_type_slug=ballot.ballot_type.slug,
ballot_edit_return_point=return_to_url,
editable=True,
))

View file

@ -2617,6 +2617,19 @@
"model": "doc.state",
"pk": 180
},
{
"fields": {
"desc": "The BOF request is spam",
"name": "Spam",
"next_states": [],
"order": 5,
"slug": "spam",
"type": "bofreq",
"used": true
},
"model": "doc.state",
"pk": 182
},
{
"fields": {
"label": "State"

View file

@ -320,6 +320,11 @@ tbody.meta tr {
background-color: $danger;
}
.badge-generic {
color: white;
background-color: $danger;
}
#toc-nav {
width: inherit;
overscroll-behavior-y: none; // Prevent overscrolling from scrolling the main content

View file

@ -0,0 +1,16 @@
{% load origin %}
{% load static %}
{% load ietf_filters %}
{% load person_filters %}
{% origin %}
{# Non-RFC #}
{% if doc.became_rfc %}
<div{% if document_html %} class="alert alert-warning small"{% endif %}>This is an older version of an Internet-Draft that was ultimately published as <a href="{% if document_html %}{% url 'ietf.doc.views_doc.document_html' name=doc.became_rfc.name %}{% else %}{% url 'ietf.doc.views_doc.document_main' name=doc.became_rfc.name %}{% endif %}">{{doc.became_rfc.name|prettystdname}}</a>.</div>
{% elif snapshot and doc.rev != latest_rev %}
<div{% if document_html %} class="alert alert-warning small p-2 mt-2"{% endif %}>This is an older version of an Internet-Draft whose latest revision state is "{{ doc.doc.get_state }}".</div>
{% else %}
<span class="{% if doc.get_state_slug == 'active' %}text-success{% elif doc.get_state_slug == 'expired' or doc.get_state_slug == 'repl' %}text-danger{% endif %}">{% if snapshot and doc.rev == latest_rev %}{{ doc.doc.get_state }}{% else %}{{ doc.get_state }}{% endif %} Internet-Draft</span>
{% if submission %}({{ submission|safe }}){% endif %}
{% if resurrected_by %}- resurrect requested by {{ resurrected_by }}{% endif %}
{% endif %}

View file

@ -0,0 +1,13 @@
{% load origin %}
{% load static %}
{% load ietf_filters %}
{% load person_filters %}
{% origin %}
<span class="text-success">RFC
{% if not document_html %}
- {{ doc.std_level }}
{% else %}
<span class="badge rounded-pill badge-{% if not snapshot %}{{ doc|std_level_to_label_format }}{% else %}generic{% endif %}">{{ doc.std_level }}</span>
{% endif %}
</span>

View file

@ -27,7 +27,7 @@
{% if editable and user|has_role:"Area Director,Secretariat,IRSG Member,RSAB Member" %}
{% if user|can_ballot:doc %}
<a class="btn btn-primary"
href="{% url "ietf.doc.views_ballot.edit_position" name=doc.name ballot_id=ballot_id %}?ballot_edit_return_point={{ request.path|urlencode }}">
href="{% url "ietf.doc.views_ballot.edit_position" name=doc.name ballot_id=ballot_id %}?ballot_edit_return_point={{ ballot_edit_return_point|urlencode }}">
Edit position
</a>
{% endif %}

View file

@ -1,7 +1,7 @@
{% extends "base.html" %}
{# Copyright The IETF Trust 2021 All Rights Reserved #}
{% load origin %}
{% load person_filters %}
{% load person_filters ietf_filters %}
{% load static %}
{% block pagehead %}
<link rel="stylesheet" href="{% static "ietf/css/list.css" %}">
@ -26,40 +26,42 @@
{% else %}
{% regroup reqs by get_state_slug as grouped_reqs %}
{% for req_group in grouped_reqs %}
<h2 class="mt-5">{{ req_group.grouper|capfirst }} BOF Requests</h2>
<table id="bofreqs-{{ req_group.grouper }}"
class="table table-sm table-striped tablesorter">
<thead>
<tr>
<th scope="col" data-sort="name">Name</th>
<th scope="col" class="d-none d-sm-table-cell" data-sort="date">Date</th>
<th scope="col" data-sort="title">Title</th>
<th scope="col" data-sort="responsible">Responsible</th>
<th scope="col" data-sort="editors">Editors</th>
</tr>
</thead>
<tbody>
{% for req in req_group.list %}
{% if req_group.grouper != "spam" or request.user|has_role:"Secretariat" %}
<h2 class="mt-5">{{ req_group.grouper|capfirst }} BOF Requests</h2>
<table id="bofreqs-{{ req_group.grouper }}"
class="table table-sm table-striped tablesorter">
<thead>
<tr>
<td>
<a href="{% url 'ietf.doc.views_doc.document_main' name=req.name %}">{{ req.name }}-{{ req.rev }}</a>
</td>
<td class="d-none d-sm-table-cell">{{ req.latest_revision_event.time|date:"Y-m-d" }}</td>
<td>{{ req.title }}</td>
<td>
{% for person in req.responsible %}
{% person_link person %}{% if not forloop.last %},{% endif %}
{% endfor %}
</td>
<td>
{% for person in req.editors %}
{% person_link person %}{% if not forloop.last %},{% endif %}
{% endfor %}
</td>
<th scope="col" data-sort="name">Name</th>
<th scope="col" class="d-none d-sm-table-cell" data-sort="date">Date</th>
<th scope="col" data-sort="title">Title</th>
<th scope="col" data-sort="responsible">Responsible</th>
<th scope="col" data-sort="editors">Editors</th>
</tr>
{% endfor %}
</tbody>
</table>
</thead>
<tbody>
{% for req in req_group.list %}
<tr>
<td>
<a href="{% url 'ietf.doc.views_doc.document_main' name=req.name %}">{{ req.name }}-{{ req.rev }}</a>
</td>
<td class="d-none d-sm-table-cell">{{ req.latest_revision_event.time|date:"Y-m-d" }}</td>
<td>{{ req.title }}</td>
<td>
{% for person in req.responsible %}
{% person_link person %}{% if not forloop.last %},{% endif %}
{% endfor %}
</td>
<td>
{% for person in req.editors %}
{% person_link person %}{% if not forloop.last %},{% endif %}
{% endfor %}
</td>
</tr>
{% endfor %}
</tbody>
</table>
{% endif %}
{% endfor %}
{% endif %}
{% endblock %}

View file

@ -4,6 +4,7 @@
{% load origin %}
{% load static %}
{% load ietf_filters textfilters %}
{% load document_type_badge %}
{% load django_vite %}
{% origin %}
<html data-bs-theme="auto" lang="en">
@ -107,6 +108,7 @@
{{ doc.name }}-{{ doc.rev }}
{% endif %}
<br class="d-sm-none">
<span class="ms-sm-3 badge rounded-pill badge-{% if not snapshot %}{{ doc|std_level_to_label_format }}{% else %}draft{% endif %}">
{% if not snapshot %}
{{ doc.std_level }}
@ -185,13 +187,7 @@
{{ doc.name }}-{{ doc.rev }}
{% endif %}
<br>
<span class="badge rounded-pill badge-{% if not snapshot %}{{ doc|std_level_to_label_format }}{% else %}draft{% endif %}">
{% if not snapshot %}
{{ doc.std_level }}
{% else %}
Internet-Draft
{% endif %}
</span>
{% document_type_badge doc snapshot submission resurrected_by %}
</p>
</div>
{% if request.COOKIES.htmlconf and request.COOKIES.htmlconf != 'html' and html %}

View file

@ -3,6 +3,7 @@
{% load static %}
{% load ietf_filters %}
{% load person_filters %}
{% load document_type_badge %}
{% origin %}
<tbody class="meta align-top {% if not document_html %} border-top{% endif %}">
@ -11,14 +12,8 @@
<th scope="row">{% if document_html %}Document type{% else %}Type{% endif %}</th>
<td class="edit"></td>
<td>
{% document_type_badge doc snapshot submission resurrected_by %}
{% if doc.type_id == "rfc" %}
<span class="text-success">RFC
{% if not document_html %}
- {{ doc.std_level }}
{% else %}
<span class="badge rounded-pill badge-{% if not snapshot %}{{ doc|std_level_to_label_format }}{% else %}draft{% endif %}">{{ doc.std_level }}</span>
{% endif %}
</span>
{% if doc.pub_date %}
{% if document_html %}<br>{% else %}({% endif %}{{ doc.pub_date|date:"F Y" }}{% if not document_html %}){% endif %}
{% else %}
@ -59,16 +54,6 @@
{% if submission %}({{ submission|safe }}){% endif %}
</div>
{% endif %}
{% else %}
{% if doc.became_rfc %}
<div{% if document_html %} class="alert alert-warning small"{% endif %}>This is an older version of an Internet-Draft that was ultimately published as <a href="{% if document_html %}{% url 'ietf.doc.views_doc.document_html' name=doc.became_rfc.name %}{% else %}{% url 'ietf.doc.views_doc.document_main' name=doc.became_rfc.name %}{% endif %}">{{doc.became_rfc.name|prettystdname}}</a>.</div>
{% elif snapshot and doc.rev != latest_rev %}
<div{% if document_html %} class="alert alert-warning small p-2 mt-2"{% endif %}>This is an older version of an Internet-Draft whose latest revision state is "{{ doc.doc.get_state }}".</div>
{% else %}
<span class="{% if doc.get_state_slug == 'active' %}text-success{% elif doc.get_state_slug == 'expired' or doc.get_state_slug == 'repl' %}text-danger{% endif %}">{% if snapshot and doc.rev == latest_rev %}{{ doc.doc.get_state }}{% else %}{{ doc.get_state }}{% endif %} Internet-Draft</span>
{% if submission %}({{ submission|safe }}){% endif %}
{% if resurrected_by %}- resurrect requested by {{ resurrected_by }}{% endif %}
{% endif %}
{% endif %}
{% if doc.get_state_slug != "active" and doc.get_state_slug != "rfc" and doc.type_id != "rfc" %}
<div class="badge rounded-pill text-bg-warning{% if not document_html %} float-end{% endif %}">

View file

@ -1431,7 +1431,7 @@ test.describe('past - small screens', () => {
// can open the jump to day dropdown
await barBtnLocator.first().click()
const jumpDayDdnLocator = page.locator('.n-dropdown-menu > .n-dropdown-option')
const jumpDayDdnLocator = page.locator('.n-dropdown-menu [data-testid=mobile-link]')
await expect(jumpDayDdnLocator).toHaveCount(7)
for (let idx = 0; idx < 7; idx++) {
const localDateTime = DateTime.fromISO(meetingData.meeting.startDate, { zone: meetingData.meeting.timezone })