Improve routing rule drag handling and controls
Build and Deploy Gateway / build-and-push (push) Successful in 34s
Build and Deploy Gateway / deploy (push) Successful in 6s

This commit is contained in:
2026-08-17 16:48:46 +03:00
parent f3be0b2fd0
commit 286a89051a
9 changed files with 112 additions and 92 deletions
+7 -4
View File
@@ -102,12 +102,14 @@ test('copy feedback, drawers and Gateway access actions expose complete semantic
});
test('ordered rules use one accessible drag handle and a compact icon route control', () => {
const handle = /<button\s+className="client-rule-handle"([\s\S]*?)<\/button>/.exec(routing)?.[0] || '';
assert.match(routing, /className="client-rule-handle"[\s\S]*type="button"[\s\S]*aria-label=\{`Переместить правило, позиция/);
assert.match(routing, /aria-describedby="client-rule-reorder-instructions"[\s\S]*aria-pressed=\{lifted\}/);
assert.match(routing, /onPointerDown=\{\(event\) => feature\.startPointerReorder\(event, rule\._key\)\}/);
assert.doesNotMatch(handle, /onPointerMove|onPointerUp|onPointerCancel|onLostPointerCapture/);
assert.doesNotMatch(routing, /data-rule-key[^>]*onPointerDown/);
assert.match(routing, /onClick=\{\(event\) => feature\.handleReorderClick\(event, rule\._key\)\}/);
assert.match(routing, /<svg viewBox="0 0 12 28" aria-hidden="true">[\s\S]*<circle[\s\S]*<circle[\s\S]*<circle/);
assert.match(routing, /<svg viewBox="0 0 16 28" aria-hidden="true">[\s\S]*cx="5" cy="5"[\s\S]*cx="11" cy="23"/);
assert.match(routing, /client-rule-reorder-instructions[\s\S]*стрелки вверх и вниз[\s\S]*Escape отменяет/);
assert.match(routing, /client-rule-reorder-live[\s\S]*aria-live="polite"/);
assert.match(routing, /className="client-rule-outbound client-tooltip-anchor"[\s\S]*className=\{direct \? 'is-direct' : 'is-vpn'\}/);
@@ -116,16 +118,17 @@ test('ordered rules use one accessible drag handle and a compact icon route cont
assert.doesNotMatch(routing, /client-rule-outbound" role="group"/);
assert.match(routing, /className=\{`client-local-rules-list\$\{draftRules\.length > 1 \? ' has-order-flow' : ''\}`\}[\s\S]*aria-label="Правила применяются сверху вниз"/);
assert.match(routing, /className="client-rule-status-dot"/);
assert.match(routing, /role="switch"[\s\S]*aria-checked=\{rule\.enabled\}[\s\S]*client-rule-switch-track[\s\S]*client-rule-switch-thumb/);
assert.match(routing, /Отключить правило'[\s\S]*Включить правило'[\s\S]*<Tooltip>/);
assert.match(routing, /role="switch"[\s\S]*aria-checked=\{rule\.enabled\}[\s\S]*client-rule-check/);
assert.match(routing, /Отключить: правило перестанет участвовать в маршрутизации[\s\S]*Включить: правило снова будет участвовать в маршрутизации/);
assert.match(routing, /data-route=\{rule\.outbound\}/);
assert.match(styles, /\.client-local-rule\[data-route='direct'\] \{[\s\S]*--client-rule-route-color/);
assert.match(styles, /\.client-local-rule::after \{[\s\S]*background: var\(--client-rule-route-color\)/);
assert.doesNotMatch(styles, /\.client-local-rule::after/);
assert.match(styles, /\.client-local-rule\.is-active \.client-rule-status-dot \{[\s\S]*background: currentColor/);
assert.match(styles, /\.client-local-rule\.is-unsaved \.client-rule-status-dot \{[\s\S]*border-radius: 1px[\s\S]*rotate\(45deg\)/);
assert.match(styles, /\.client-local-rule\.is-pending \.client-rule-status-dot \{[\s\S]*border: 1\.5px dashed/);
assert.doesNotMatch(routing, />\s*[↑↓]\s*</);
assert.match(styles, /\.client-rule-handle \{[\s\S]*width: 44px;[\s\S]*height: 44px;[\s\S]*touch-action: none/);
assert.match(styles, /\.client-row-delete \{[\s\S]*color: oklch\(0\.62 0\.16 28\)/);
assert.match(styles, /\.client-rule-outbound \{[\s\S]*width: 34px/);
assert.match(styles, /\.client-rule-outbound svg \{[\s\S]*width: 20px/);
assert.match(styles, /\.client-local-rules-list\.has-order-flow::before[\s\S]*linear-gradient\(to bottom/);