fix: keep guide accessible when browser storage is blocked
This commit is contained in:
1 parent
d9420db50c
commit
0f2d0db3c7
8 files changed
+44
-15
No files matched your search
+26
-6
@@ -70,6 +70,7 @@
|
||||
|
||||
const main = document.querySelector('#main-content');
|
||||
const trigger = document.querySelector('[data-guide-open]');
|
||||
const fallback = document.querySelector('[data-guide-fallback]');
|
||||
const heading = guide.querySelector('#guide-title');
|
||||
const progress = guide.querySelector('[data-guide-progress]');
|
||||
const intro = guide.querySelector('[data-guide-intro]');
|
||||
@@ -152,7 +153,10 @@
|
||||
}
|
||||
|
||||
function goTo(number, path = steps[number].path || '/admin/account') {
|
||||
sessionStorage.setItem(storageKey, steps[number].id);
|
||||
if (!saveStep(steps[number].id)) {
|
||||
location.assign(trigger.href);
|
||||
return;
|
||||
}
|
||||
if (path !== location.pathname) {
|
||||
location.assign(path);
|
||||
return;
|
||||
@@ -164,11 +168,20 @@
|
||||
function hide(remember = true) {
|
||||
clearTips();
|
||||
guide.hidden = true;
|
||||
if (remember) sessionStorage.setItem(storageKey, 'closed');
|
||||
else sessionStorage.removeItem(storageKey);
|
||||
saveStep(remember ? 'closed' : null);
|
||||
trigger?.focus();
|
||||
}
|
||||
|
||||
function saveStep(step) {
|
||||
try {
|
||||
if (step === null) sessionStorage.removeItem(storageKey);
|
||||
else sessionStorage.setItem(storageKey, step);
|
||||
return true;
|
||||
} catch {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
async function finish() {
|
||||
if (saving) return;
|
||||
if (guide.dataset.guidePending !== 'true') {
|
||||
@@ -219,13 +232,20 @@
|
||||
finish();
|
||||
});
|
||||
function restore(allowAutoStart) {
|
||||
const active = sessionStorage.getItem(storageKey);
|
||||
let active;
|
||||
try {
|
||||
active = sessionStorage.getItem(storageKey);
|
||||
} catch {
|
||||
return;
|
||||
}
|
||||
const requested = steps.findIndex(step => step.id === active);
|
||||
const autoStart = allowAutoStart && requested < 0 && active !== 'closed' && guide.dataset.guidePending === 'true';
|
||||
if (autoStart && !saveStep(steps[0].id)) return;
|
||||
if (fallback) fallback.hidden = true;
|
||||
if (requested >= 0) {
|
||||
index = requested;
|
||||
showStep(!allowAutoStart || index > 0);
|
||||
} else if (allowAutoStart && active !== 'closed' && guide.dataset.guidePending === 'true') {
|
||||
sessionStorage.setItem(storageKey, steps[0].id);
|
||||
} else if (autoStart) {
|
||||
showStep(false);
|
||||
} else {
|
||||
clearTips();
|
||||
|
||||
@@ -16,10 +16,10 @@
|
||||
<button class="button button--ghost" type="button" data-guide-unsaved hidden>Продолжить без сохранения</button>
|
||||
</section>
|
||||
{% if request.state.guide_pending|default(false) %}
|
||||
<noscript><aside class="panel guide--page" aria-label="Подсказки первого входа">
|
||||
<aside class="panel guide--page" aria-label="Подсказки первого входа" data-guide-fallback>
|
||||
<h2 class="panel__title">Как пользоваться сайтом</h2>
|
||||
<p>Откройте справку о сотрудниках, диссовете и публикациях.</p>
|
||||
<a class="button" href="/admin/guide">Открыть подсказки</a>
|
||||
<form method="post" action="/admin/guide/dismiss"><input type="hidden" name="csrf_token" value="{{ csrf_token }}"><button class="button button--ghost" type="submit">Пропустить подсказки</button></form>
|
||||
</aside></noscript>
|
||||
</aside>
|
||||
{% endif %}
|
||||
+3
-3
@@ -1,3 +1,3 @@
|
||||
APP_VERSION = "1.6.1"
|
||||
FRONTEND_VERSION = "1.6.1"
|
||||
BACKEND_VERSION = "1.6.1"
|
||||
APP_VERSION = "1.6.2"
|
||||
FRONTEND_VERSION = "1.6.2"
|
||||
BACKEND_VERSION = "1.6.2"
|
||||
+1
-1
@@ -1,6 +1,6 @@
|
||||
[project]
|
||||
name = "miem-workers"
|
||||
version = "1.6.1"
|
||||
version = "1.6.2"
|
||||
description = "MIEM employees parser, admin API, and web admin"
|
||||
requires-python = ">=3.11"
|
||||
dependencies = [
|
||||
|
||||
@@ -103,6 +103,15 @@ def main():
|
||||
guide = page.locator('[data-guide]')
|
||||
expect(guide).to_be_visible()
|
||||
expect(guide.get_by_role('heading', name='Познакомьтесь с Workers')).to_be_visible()
|
||||
restricted = context.new_page()
|
||||
restricted.add_init_script("""Object.defineProperty(window, 'sessionStorage', {
|
||||
get() { throw new DOMException('Storage is disabled', 'SecurityError'); },
|
||||
});""")
|
||||
restricted.goto(base_url + '/admin/account')
|
||||
expect(restricted.get_by_role('link', name='Открыть подсказки', exact=True)).to_be_visible()
|
||||
restricted.get_by_role('link', name='Как пользоваться', exact=True).click()
|
||||
expect(restricted.get_by_role('heading', name='Как пользоваться сайтом')).to_be_visible()
|
||||
restricted.close()
|
||||
for width in (1440, 320):
|
||||
page.set_viewport_size({'width': width, 'height': 900})
|
||||
assert guide.evaluate('node => node.scrollWidth <= node.clientWidth')
|
||||
|
||||
+1
-1
@@ -18,7 +18,7 @@ def test_health_returns_versions():
|
||||
response = TestClient(app).get("/api/health")
|
||||
|
||||
assert response.status_code == 200
|
||||
assert response.json()["backend_version"] == "1.6.1"
|
||||
assert response.json()["backend_version"] == "1.6.2"
|
||||
|
||||
|
||||
def test_api_employees_and_stats_require_admin_session():
|
||||
|
||||
@@ -230,7 +230,7 @@ def test_public_page_data_minimization_and_safe_links(db_session, export):
|
||||
assert "javascript:" not in fragment.text and "private@example.org" not in fragment.text
|
||||
assert "javascript:" not in response.text and "private@example.org" not in response.text
|
||||
assert "/admin/employees/" not in response.text and "/admin/logout" not in response.text
|
||||
assert "Backend 1.6.1 · Frontend 1.6.1" in response.text
|
||||
assert "Backend 1.6.2 · Frontend 1.6.2" in response.text
|
||||
assert "Вход в систему" in response.text
|
||||
assert client.get("/admin/directory", follow_redirects=False).status_code in (302, 303, 401, 403)
|
||||
assert "Сотрудники не найдены" in client.get("/admin/council?q=missing").text
|
||||
|
||||
+1
-1
@@ -342,7 +342,7 @@ def test_guide_access_and_no_javascript_completion(accounts):
|
||||
db.add(UserAction(user_id=user.id, action='guide_start'))
|
||||
db.commit()
|
||||
page = BeautifulSoup(client.get('/admin').text, 'html.parser')
|
||||
assert page.select_one('noscript a[href="/admin/guide"]')
|
||||
assert page.select_one('[data-guide-fallback] a[href="/admin/guide"]')
|
||||
response = client.post('/admin/guide/dismiss', headers=headers, follow_redirects=False)
|
||||
assert response.status_code == 303 and response.headers['location'] == '/admin'
|
||||
assert 'data-guide-pending="false"' in client.get('/admin/account').text
|
||||
|
||||
Reference in new issue
Block a user