fix(frontend): simplify report dialog controls and feedback
Use highlight-only reason choices, show the associated target on one line, and replace inline submission progress with shared toast feedback. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
1 parent
80aecab586
commit
b2811cc248
4 files changed
+86
-49
No files matched your search
@@ -399,7 +399,7 @@ test('report modal uses accessible reasons, automatic target association and opt
|
||||
assert.match(scripts, /report_settings\(\)/)
|
||||
|
||||
const reportJs = read('assets/js/report.js')
|
||||
for (const part of ['aksrt-report-target', 'name="report_reason"', 'type="radio"', '其他原因', '补充说明', '图片举证', '提交举报', '无需填写链接']) {
|
||||
for (const part of ['aksrt-report-target', 'name="report_reason"', 'type="radio"', '其他原因', '补充说明', '图片举证', '提交举报', '关联对象:']) {
|
||||
assert.ok(reportJs.includes(part), `report.js missing form contract: ${part}`)
|
||||
}
|
||||
assert.doesNotMatch(reportJs, /report_url|违规链接|data-aksrt-report-reason=|bindAutoHeight/)
|
||||
@@ -413,6 +413,10 @@ test('report modal uses accessible reasons, automatic target association and opt
|
||||
assert.match(css, /\.aksrt-report-modal:not\(\.aksrt-modal-runtime--bottom\) \.aksrt-modal-runtime__dialog/)
|
||||
assert.match(css, /600px/)
|
||||
assert.match(css, /:focus-visible/)
|
||||
assert.match(css, /\.aksrt-report-reason:has\(input:checked\)/)
|
||||
assert.match(css, /clip-path: inset\(50%\)/)
|
||||
assert.match(css, /text-overflow: ellipsis/)
|
||||
assert.doesNotMatch(reportJs, /aksrt-report-target__icon|无需填写链接|正在提交举报,请稍候/)
|
||||
assert.match(css, /safe-area-inset-bottom/)
|
||||
assert.match(css, /\[hidden\] \{ display: none !important;/)
|
||||
assert.doesNotMatch(modalRuntimeCss, /aksrt-report-modal/, 'retired report styles must not remain in the shared stylesheet')
|
||||
|
||||
@@ -208,6 +208,9 @@ test('opens from an article button, associates the target, omits URL input, and
|
||||
assert.equal(modal.shell.querySelector('[name="report_url"]'), null);
|
||||
assert.equal(modal.shell.querySelector('input[type="url"]'), null);
|
||||
assert.ok(modal.shell.querySelector('.aksrt-report-target'));
|
||||
assert.equal(modal.shell.querySelector('.aksrt-report-target__label').textContent, '关联对象:');
|
||||
assert.equal(modal.shell.querySelector('.aksrt-report-target__icon'), null);
|
||||
assert.equal(target.getAttribute('title'), target.textContent);
|
||||
});
|
||||
|
||||
test('requires a radio reason and other text, then sends canonical fields with description', async () => {
|
||||
@@ -373,7 +376,8 @@ test('server failure preserves values, reenables controls, and allows a retry',
|
||||
assert.equal(otherInput.disabled, false);
|
||||
assert.equal(description.disabled, false);
|
||||
assert.equal(submitButton.disabled, false);
|
||||
assert.equal(form.querySelector('[data-aksrt-report-error]').textContent, 'Server rejected report');
|
||||
assert.equal(form.querySelector('[data-aksrt-report-error]').hidden, true);
|
||||
assert.deepEqual(runtime.notyfCalls.at(-1).slice(0, 3), ['Server rejected report', 'danger', 5]);
|
||||
|
||||
submit(modal);
|
||||
const retry = callsFor(runtime, '/report/submit')[1];
|
||||
@@ -382,6 +386,40 @@ test('server failure preserves values, reenables controls, and allows a retry',
|
||||
await flushPromises();
|
||||
});
|
||||
|
||||
test('uses a persistent shared toast per submit attempt without inline submitting text', async () => {
|
||||
const runtime = createRuntime();
|
||||
const modal = runtime.openReport();
|
||||
selectReason(modal, 0);
|
||||
const form = modal.shell.querySelector('.aksrt-report-form');
|
||||
const button = form.querySelector('[data-aksrt-report-submit]');
|
||||
const status = form.querySelector('[data-aksrt-report-status]');
|
||||
|
||||
submit(modal);
|
||||
const firstToast = runtime.notyfCalls.at(-1);
|
||||
assert.deepEqual(firstToast.slice(0, 3), ['正在提交举报…', 'load', '']);
|
||||
assert.ok(firstToast[3].startsWith(form.id + '-submit-'));
|
||||
assert.equal(button.textContent, '提交举报');
|
||||
assert.equal(button.disabled, true);
|
||||
assert.equal(status.textContent, '');
|
||||
assert.equal(form.getAttribute('aria-busy'), 'true');
|
||||
|
||||
submit(modal);
|
||||
assert.equal(runtime.notyfCalls.length, 1);
|
||||
callsFor(runtime, '/report/submit')[0].resolve({ success: false, message: 'Retry report' });
|
||||
await flushPromises();
|
||||
assert.deepEqual(runtime.notyfCalls.at(-1), ['Retry report', 'danger', 5, firstToast[3]]);
|
||||
assert.equal(button.disabled, false);
|
||||
assert.equal(status.textContent, '');
|
||||
|
||||
submit(modal);
|
||||
const secondToast = runtime.notyfCalls.at(-1);
|
||||
assert.equal(secondToast[1], 'load');
|
||||
assert.notEqual(secondToast[3], firstToast[3]);
|
||||
callsFor(runtime, '/report/submit')[1].resolve({ success: true, message: 'Report accepted' });
|
||||
await flushPromises();
|
||||
assert.deepEqual(runtime.notyfCalls.at(-1), ['Report accepted', 'success', 5, secondToast[3]]);
|
||||
});
|
||||
|
||||
test('blocks duplicate submits and closing an old submitting dialog cannot close the new one', async () => {
|
||||
const runtime = createRuntime();
|
||||
const oldModal = runtime.openReport();
|
||||
|
||||
Reference in new issue
Block a user