Make report coverage calendar expandable

This commit is contained in:
Wyndham ARR committed 2026-10-09 13:55:41 +08:00
1 parent 4356160cc9
commit 6d1a98743f
11 files changed
+283 -57

No files matched your search

+129 -2
View File
@@ -76,7 +76,7 @@ test('loading and failed reads never label days ungenerated and a retry can rest
resolve(response(503,null,'DATABASE_QUERY_FAILED'));
await reading;
assert.equal(h.state.reportCalendarError,true);
assert.match(block(h,`${month}-01`),/report_calendar.day_unknown/);
assert.match(block(h,`${month}-01`),/report_calendar.unknown/);
assert.doesNotMatch(h.element('#report-calendar-days').innerHTML,/is-ungenerated/);
assert.equal(h.element('#report-calendar-retry').hidden,false);
fail=false;
@@ -272,7 +272,7 @@ test('calendar labels, status legends and read failures are translated in all th
const context=vm.createContext({window:{},document:{readyState:'loading',addEventListener(){}}});
vm.runInContext(fs.readFileSync(path.resolve(__dirname,'../../arr_web/static/i18n.js'),'utf8'),context);
const catalog=context.window.ARRI18n.CATALOG;
for(const key of ['title','widget_aria','generated','not_generated','unknown','loading_day',
for(const key of ['title','widget_aria','open','loading_summary','failed_summary','generated','not_generated','unknown','loading_day',
'day_generated','day_not_generated','day_unknown','day_loading_day','loading','failed','coverage','help','locked','day_group','legend','retry']) {
const entry=catalog[`report_calendar.${key}`];
assert.equal(entry?.length,3,key);
@@ -324,3 +324,130 @@ test('same-date selections across an environment A to B to A transition reject t
assert.equal(h.state.dailyOverviewDate,date);
assert.equal(h.state.jobsMonth,month);
});
test('the compact calendar starts closed and its popup and ARR date picker are mutually exclusive',async()=>{
const h=setup(()=>page([row(16)]));
await h.loadReportCalendar();
assert.equal(h.element('#report-calendar-popup').hidden,true);
assert.equal(h.element('#report-calendar-toggle').getAttribute('aria-expanded'),'false');
assert.equal(h.element('#report-calendar-summary').textContent,'report_calendar.coverage');
const reads=h.calls.length;
h.openARRCalendar();
assert.equal(h.arrCalendar.open,true);
h.openReportCalendar();
assert.equal(h.arrCalendar.open,false);
assert.equal(h.state.reportCalendarOpen,true);
assert.equal(h.element('#report-calendar-popup').hidden,false);
assert.equal(h.element('#report-calendar-toggle').getAttribute('aria-expanded'),'true');
assert.match(block(h,`${month}-16`),/report-calendar-day-check/,'a generated day has a non-color check mark');
assert.doesNotMatch(block(h,`${month}-17`),/report-calendar-day-check|report-calendar-day-state/);
h.openARRCalendar();
assert.equal(h.state.reportCalendarOpen,false);
assert.equal(h.element('#report-calendar-popup').hidden,true);
assert.equal(h.arrCalendar.open,true);
assert.equal(h.calls.length,reads,'opening either calendar is a view action');
});
test('Escape and close return focus to the trigger while outside click or keyboard focus preserve the destination',()=>{
const h=setup(()=>{throw new Error('Closing must not request data');});
h.bindReportCalendar();
h.openReportCalendar();
let prevented=false;
h.emitDocumentEvent({type:'keydown',key:'Escape',preventDefault(){prevented=true;}});
assert.equal(prevented,true);
assert.equal(h.state.reportCalendarOpen,false);
assert.equal(h.document.activeElement,h.element('#report-calendar-toggle'));
h.openReportCalendar();
h.element('#report-calendar-close').dispatchEvent({type:'click'});
assert.equal(h.state.reportCalendarOpen,false);
assert.equal(h.document.activeElement,h.element('#report-calendar-toggle'));
for(const type of ['click','focusin']) {
h.openReportCalendar();
const destination=h.element('#xml-file');
destination.focus();
h.emitDocumentEvent({type,target:destination});
assert.equal(h.state.reportCalendarOpen,false);
assert.equal(h.document.activeElement,destination,'dismissal cannot steal focus from the next operation');
}
});
test('successful read-only selection closes the popup but busy or unsaved prices keep it open without discarding input',async()=>{
const date=`${month}-17`;
const h=setup(url=>url===`/api/daily-reports/${date}`?response(200,{business_date:date,current_job:null,latest_job:null}):page());
await h.loadReportCalendar();
h.openReportCalendar();
h.state.uploadInFlight=true;
assert.equal(await h.selectReportCalendarDate(date),false);
assert.equal(h.state.reportCalendarOpen,true);
h.state.uploadInFlight=false;
h.state.dailyPriceReview={case_status:'open',items:[{item_id:1,real_price:null}]};
const input={value:'0',closest:()=>({dataset:{dailyPriceReviewItemId:'1'}})};
h.element('#daily-price-review-body').querySelectorAll=()=>[input];
assert.equal(await h.selectReportCalendarDate(date),false);
assert.equal(h.state.reportCalendarOpen,true);
assert.equal(input.value,'0');
assert.equal(h.element('#arr-download-date').value,`${month}-16`);
h.state.dailyPriceReview=null;
assert.equal(await h.selectReportCalendarDate(date),true);
assert.equal(h.state.reportCalendarOpen,false);
assert.equal(h.element('#report-calendar-popup').hidden,true);
assert.equal(h.document.activeElement,h.element('#report-calendar-toggle'));
assert.equal(h.calls.every(call=>call.method==='GET'),true);
assert.equal(h.calls.some(call=>call.url.includes('/arr-downloads')),false);
});
test('a hidden popup never captures focus during a late month refresh or keyboard-render request',async()=>{
let resolveRead;
const h=setup(()=>new Promise(resolve=>{resolveRead=resolve;}));
h.openReportCalendar();
const pending=h.loadReportCalendar();
const hiddenDay=h.element(`[data-report-calendar-day="${h.state.reportCalendarFocusDate}"]`);
hiddenDay.closest=()=>hiddenDay;
h.closeReportCalendar();
let focusAttempts=0;
hiddenDay.focus=()=>{focusAttempts+=1;};
h.document.activeElement=hiddenDay;
resolveRead(page());
await pending;
h.renderReportCalendar(true);
assert.equal(focusAttempts,0);
assert.equal(h.element('#report-calendar-popup').hidden,true);
});
test('a late day selection cannot close a popup opened again after the original popup was dismissed',async()=>{
let resolveSelection;
const date=`${month}-17`;
const h=setup(url=>url===`/api/daily-reports/${date}`?new Promise(resolve=>{resolveSelection=resolve;}):page());
await h.loadReportCalendar();
h.openReportCalendar();
const selecting=h.selectReportCalendarDate(date);
h.closeReportCalendar();
h.openReportCalendar();
resolveSelection(response(200,{business_date:date,current_job:null,latest_job:null}));
assert.equal(await selecting,true);
assert.equal(h.state.reportCalendarOpen,true);
assert.equal(h.element('#report-calendar-popup').hidden,false);
});
test('a delegated day click stays internal after synchronous rendering removes its target and restores trigger focus on success',async()=>{
let resolveSelection;
const date=`${month}-17`;
const h=setup(url=>url===`/api/daily-reports/${date}`?new Promise(resolve=>{resolveSelection=resolve;}):page());
await h.loadReportCalendar();
h.bindReportCalendar();
h.openReportCalendar();
const originalButton={dataset:{reportCalendarDay:date},closest(){return this;}};
const control=h.element('#report-calendar');
const event={type:'click',target:originalButton,composedPath:()=>[
originalButton,h.element('#report-calendar-days'),h.element('#report-calendar-popup'),control]};
h.element('#report-calendar-days').dispatchEvent(event);
assert.equal(h.state.dailyOverviewDate,date,'the delegated handler has already synchronously rerendered the days');
assert.equal(control.contains(originalButton),false,'the original click target is no longer a current DOM descendant');
h.emitDocumentEvent(event);
assert.equal(h.state.reportCalendarOpen,true,'document must honor the event origin instead of the removed target');
resolveSelection(response(200,{business_date:date,current_job:null,latest_job:null}));
await new Promise(resolve=>setImmediate(resolve));
assert.equal(h.state.reportCalendarOpen,false);
assert.equal(h.document.activeElement,h.element('#report-calendar-toggle'));
assert.equal(h.calls.every(call=>call.method==='GET'),true);
});