refactor(ui): unify semantic presentation and table hierarchy
This commit is contained in:
@@ -0,0 +1,42 @@
|
||||
"""Bounded actual-component presentation matrix, loopback synthetic values only."""
|
||||
from pathlib import Path
|
||||
import json,re,sys
|
||||
from playwright.sync_api import sync_playwright
|
||||
sys.path.insert(0,'/Users/dohertj2/.codex/worktrees/scada-ui-consistency/scadaproj/components/ui-theme/validation/theme-1.0.0/canary')
|
||||
from browser_zoom import zoom_browser,set_browser_zoom
|
||||
out=Path('docs/ui-consistency/evidence/sb07-09/browser');out.mkdir(parents=True,exist_ok=True)
|
||||
def contrast(fg,bg):
|
||||
def parts(s):return [float(v) for v in re.findall(r'[\d.]+',s)]
|
||||
def lum(a):
|
||||
a=[v/255 for v in a[:3]];a=[v/12.92 if v<=.04045 else ((v+.055)/1.055)**2.4 for v in a];return sum(x*y for x,y in zip(a,[.2126,.7152,.0722]))
|
||||
a,b=parts(fg),parts(bg)
|
||||
if len(a)==4:a=[a[i]*a[3]+b[i]*(1-a[3]) for i in range(3)]
|
||||
lo,hi=sorted([lum(a),lum(b)]);return round((hi+.05)/(lo+.05),3)
|
||||
results=[]
|
||||
with sync_playwright() as p:
|
||||
with zoom_browser(p,width=1440,height=1000) as (context,worker):
|
||||
cases=[('presentation',w,s,1) for w in [1440,1024,390] for s in ['light','dark']]+[('presentation',1440,s,2) for s in ['light','dark']]+[(kind,w,s,1) for kind in ['dashboard','sms','smtp'] for w in [1440,390] for s in ['light','dark']]
|
||||
for kind,width,scheme,zoom in cases:
|
||||
page=context.new_page();page.set_viewport_size({'width':width,'height':1000});page.emulate_media(reduced_motion='reduce');errors=[];page.on('pageerror',lambda e:errors.append(str(e)))
|
||||
route='/presentation' if kind=='presentation' else '/batch-two?sample=dashboard' if kind=='dashboard' else '/form-samples?view='+kind
|
||||
page.goto('http://127.0.0.1:5201'+route,wait_until='networkidle');page.evaluate('s=>window.sbTheme.set(s)',scheme)
|
||||
if kind in ['sms','smtp']:page.get_by_role('button',name='Add '+kind.upper()+' configuration',exact=True).click();page.locator('input[type=password]').wait_for()
|
||||
record={'kind':kind,'width':width,'scheme':scheme,'zoom':zoom,'checks':{}}
|
||||
if zoom==2:record['browserZoom']=set_browser_zoom(worker,page,2)
|
||||
record['layout']=page.evaluate('({innerWidth,scrollWidth:document.documentElement.scrollWidth,DPR:devicePixelRatio})');record['checks']['noDocumentOverflow']=record['layout']['innerWidth']==record['layout']['scrollWidth'];record['checks']['onePageHeading']=page.locator('h1').count()==1
|
||||
if kind=='presentation':
|
||||
record['pairs']=page.evaluate('''()=>[...document.querySelectorAll('.fg-pill,.ft-pill,#utility-samples span')].map(e=>{let c=getComputedStyle(e),p=e,bg;while(p){let b=getComputedStyle(p).backgroundColor;if(b!=='rgba(0, 0, 0, 0)'&&b!=='transparent'){bg=b;break;}p=p.parentElement;}return {text:e.textContent.trim(),class:e.className,color:c.color,background:bg};})''')
|
||||
for pair in record['pairs']:pair['contrast']=contrast(pair['color'],pair['background'])
|
||||
record['checks']['readableStateAndUtilityText']=all(pair['contrast']>=4.5 for pair in record['pairs'])
|
||||
page.locator('.ft-row').first.focus();page.keyboard.press('Tab');record['focus']=page.locator('.ft-row').nth(1).evaluate('e=>{let c=getComputedStyle(e);return {active:e===document.activeElement,visible:e.matches(":focus-visible"),outlineWidth:c.outlineWidth,outlineStyle:c.outlineStyle,outlineColor:c.outlineColor}}');record['checks']['traceKeyboardFocus']=record['focus']['active'] and record['focus']['visible'] and record['focus']['outlineWidth']=='2px' and record['focus']['outlineStyle']=='solid'
|
||||
region=page.get_by_role('region',name='Synthetic records',exact=True);record['table']=region.evaluate('e=>({width:e.clientWidth,scrollWidth:e.scrollWidth,tabindex:e.tabIndex})')
|
||||
if record['table']['scrollWidth']>record['table']['width']:
|
||||
region.focus();page.keyboard.press('ArrowRight');page.wait_for_function('document.querySelector("[aria-label=\\"Synthetic records\\"]").scrollLeft>0')
|
||||
record['checks']['tableKeyboardRegion']=record['table']['tabindex']==0
|
||||
record['rowHeader']=region.locator('th[scope=row]').first.evaluate('e=>({position:getComputedStyle(e).position,textTransform:getComputedStyle(e).textTransform})');record['checks']['rowHeadersKeepSemantics']=record['rowHeader']=={'position':'static','textTransform':'none'}
|
||||
search=page.get_by_label('Search',exact=True);search.fill('no-match');page.wait_for_function('document.querySelector(".table-responsive + .text-muted")?.textContent.trim()==="0 items" || [...document.querySelectorAll(".text-muted.small")].some(e=>e.textContent.trim()==="0 items")');record['checks']['emptyCount']=page.get_by_text('0 items',exact=True).count()==1;page.get_by_role('button',name='Clear search',exact=True).click();page.wait_for_function('document.querySelector("[aria-label=\\"Synthetic records\\"] tbody").textContent.includes("Plant A")')
|
||||
else:record['headingStyle']=page.locator('h1').evaluate('e=>({font:getComputedStyle(e).fontFamily,size:getComputedStyle(e).fontSize})');record['checks']['sharedHeadingScale']=record['headingStyle']['size']=='20px'
|
||||
record['errors']=errors;record['checks']['noBrowserError']=not errors;page.screenshot(path=str(out/f'{kind}-{scheme}-{width}-zoom{zoom}.png'),full_page=zoom!=2);results.append(record)
|
||||
if zoom==2:set_browser_zoom(worker,page,1)
|
||||
page.close()
|
||||
(out/'results.json').write_text(json.dumps({'results':results,'passed':all(all(r['checks'].values()) for r in results)},indent=2)+'\n');print([(r['kind'],r['width'],r['scheme'],r['zoom'],all(r['checks'].values())) for r in results])
|
||||
Reference in New Issue
Block a user