651f959d7553ee0256b1e0b37ffa83d64709511d lrnassar Mon Mar 16 17:57:16 2026 -0700 Adding accessible labels to form controls across main CGI pages. Extends cheapcgi and hui libraries with aria-label support for track visibility dropdowns, and adds <label> elements to hgBlat, hgTables, hgPcr, and hgGateway form controls. Also adds Form Control Labels section to accessibility page. refs #37253 diff --git src/hg/hgTables/mainPage.c src/hg/hgTables/mainPage.c index f7140264f1a..fa9488268e2 100644 --- src/hg/hgTables/mainPage.c +++ src/hg/hgTables/mainPage.c @@ -65,31 +65,31 @@ } void makeRegionButton(char *val, char *selVal) /* Make region radio button including a little Javascript * to save selection state. */ { makeRegionButtonExtraHtml(val, selVal, NULL); } struct grp *showGroupField(char *groupVar, char *event, char *groupScript, struct sqlConnection *conn, boolean allTablesOk) /* Show group control. Returns selected group. */ { struct grp *group, *groupList = fullGroupList; struct grp *selGroup = findSelectedGroup(groupList, groupVar); -hPrintf("<B>Group:</B>\n"); +hPrintf("<label for='%s'><B>Group:</B></label>\n", groupVar); hPrintf("<SELECT NAME=%s id='%s'>\n", groupVar, groupVar); jsOnEventById(event,groupVar,groupScript); for (group = groupList; group != NULL; group = group->next) { if (allTablesOk || differentString(group->name, "allTables")) hPrintf(" <OPTION VALUE=%s%s>%s</OPTION>\n", group->name, (group == selGroup ? " SELECTED" : ""), group->label); } hPrintf("</SELECT>\n"); return selGroup; } static void addIfExists(struct hash *hash, struct slName **pList, char *name) /* Add name to tail of list if it exists in hash. */ @@ -123,45 +123,45 @@ slFreeList(&dbList); return selDb; } struct trackDb *showTrackField(struct grp *selGroup, char *trackVar, char *event, char *trackScript, boolean disableNoGenome) /* Show track control. Returns selected track. */ { struct trackDb *track, *selTrack = NULL; if (trackScript == NULL) trackScript = ""; if (sameString(selGroup->name, "allTables")) { char *selDb = findSelDb(); struct slName *dbList = getDbListForGenome(), *db; - hPrintf("<B>database:</B>\n"); + hPrintf("<label for='%s'><B>database:</B></label>\n", trackVar); hPrintf("<SELECT NAME=\"%s\" id='%s'>\n", trackVar, trackVar); jsOnEventById(event, trackVar, trackScript); for (db = dbList; db != NULL; db = db->next) { hPrintf(" <OPTION VALUE=%s%s>%s</OPTION>\n", db->name, (sameString(db->name, selDb) ? " SELECTED" : ""), db->name); } hPrintf("</SELECT>\n"); } else { boolean allTracks = sameString(selGroup->name, "allTracks"); - hPrintf("<B>Track:</B>\n"); + hPrintf("<label for='%s'><B>Track:</B></label>\n", trackVar); hPrintf("<SELECT NAME=\"%s\" id='%s'>\n", trackVar, trackVar); jsOnEventById(event, trackVar, trackScript); if (allTracks) { selTrack = findSelectedTrack(fullTrackList, NULL, trackVar); slSort(&fullTrackList, trackDbCmpShortLabel); } else { selTrack = findSelectedTrack(fullTrackList, selGroup, trackVar); } boolean selTrackIsDisabled = FALSE; struct trackDb *firstEnabled = NULL; for (track = fullTrackList; track != NULL; track = track->next) { @@ -252,31 +252,31 @@ struct slName *name, *nameList = NULL; char *selTable; if (track == NULL) nameList = tablesForDb(findSelDb()); else nameList = cartTrackDbTablesForTrack(database, track, useJoiner); /* Get currently selected table. If it isn't in our list * then revert to first in list. */ selTable = cartUsualString(cart, varName, nameList->name); if (!slNameInListUseCase(nameList, selTable)) selTable = nameList->name; /* Print out label and drop-down list. */ -hPrintf("<B>Table: </B>"); +hPrintf("<label for='%s'><B>Table: </B></label>", varName); hPrintf("<SELECT NAME=\"%s\" id='%s'>\n", varName, varName); jsOnEventById("change", varName, onChangeTable()); struct trackDb *selTdb = NULL; for (name = nameList; name != NULL; name = name->next) { struct trackDb *tdb = NULL; if (curTrack != NULL && isHubTrack(curTrack->track)) { if (sameString(curTrack->track, name->name)) tdb = curTrack; else if (curTrack->subtracks != NULL) { // maybe a subtrack is what we're looking for struct trackDb *sub = subTdbFind(curTrack, name->name); if (sub) @@ -482,31 +482,31 @@ struct outputType otWigData = { NULL, outWigData, "Data points", }; struct outputType otWigBed = { NULL, outWigBed, "Bed format", }; struct outputType otMaf = { NULL, outMaf, "MAF - multiple alignment format", }; struct outputType otChromGraphData = { NULL, outChromGraphData, "Data points", }; struct outputType otMicroarrayNames = { NULL, outMicroarrayNames, "Microarray names", }; struct outputType otMicroarrayGroupings = { NULL, outMicroarrayGroupings, "Microarray groupings", }; static void showOutputTypeRow(boolean isWig, boolean isBedGr, boolean isPositional, boolean isMaf, boolean isChromGraphCt, boolean isPal, boolean isMicroarray, boolean isHalSnake) /* Print output line. */ { struct outputType *otList = NULL, *otDefault = NULL; boolean bedifiedOnly = (anySubtrackMerge(database, curTable) || anyIntersection()); -hPrintf("<TR><TD><DIV ID=\"output-select\"><B>Output format:</B>\n"); +hPrintf("<TR><TD><DIV ID=\"output-select\"><label for='outputTypeDropdown'><B>Output format:</B></label>\n"); if (isBedGr) { if (! bedifiedOnly) { slAddTail(&otList, &otAllFields); slAddTail(&otList, &otSelected); } slAddTail(&otList, &otWigData); slAddTail(&otList, &otWigBed); slAddTail(&otList, &otCustomTrack); slAddTail(&otList, &otHyperlinks); } else if (isWig) { @@ -779,49 +779,49 @@ boolean disableGenome = ((curTrack && cartTrackDbIsNoGenome(database, curTrack->table)) || (curTable && cartTrackDbIsNoGenome(database, curTable))); // If "genome" is selected but not allowed, force it to "range": if (sameString(regionType, hgtaRegionTypeGenome) && disableGenome) regionType = hgtaRegionTypeRange; jsTrackingVar("regionType", regionType); if (disableGenome) { makeRegionButtonExtraHtml(hgtaRegionTypeGenome, regionType, "DISABLED"); hPrintf(" <span"NO_GENOME_CLASS">genome (unavailable for selected track)</span>" " "); } else { makeRegionButton(hgtaRegionTypeGenome, regionType); - hPrintf(" Genome "); + hPrintf(" <label for='%s_%s'>Genome</label> ", hgtaRegionType, hgtaRegionTypeGenome); } if (doEncode) { makeRegionButton(hgtaRegionTypeEncode, regionType); - hPrintf(" ENCODE Pilot regions "); + hPrintf(" <label for='%s_%s'>ENCODE Pilot regions</label> ", hgtaRegionType, hgtaRegionTypeEncode); } makeRegionButton(hgtaRegionTypeRange, regionType); - hPrintf(" Position "); + hPrintf(" <label for='%s'>Position</label> ", hgtaRange); hPrintf("<INPUT TYPE=TEXT NAME=\"%s\" id='%s' SIZE=26 VALUE=\"%s\">\n", hgtaRange, hgtaRange, range); jsOnEventById("focus", hgtaRange, jsRadioUpdate(hgtaRegionType, "regionType", "range")); cgiMakeButton(hgtaDoLookupPosition, "Lookup"); hPrintf(" "); if (userRegionsFileName() != NULL) { makeRegionButton(hgtaRegionTypeUserRegions, regionType); - hPrintf(" Defined regions "); + hPrintf(" <label for='%s_%s'>Defined regions</label> ", hgtaRegionType, hgtaRegionTypeUserRegions); cgiMakeButton(hgtaDoSetUserRegions, "Change"); hPrintf(" "); cgiMakeButton(hgtaDoClearUserRegions, "Clear"); } else cgiMakeButton(hgtaDoSetUserRegions, "Define regions"); hPrintf("</DIV>"); hPrintf("</TD></TR>\n"); if (disableGenome) { // no need to check curTrack for NULL, disableGenome can only be set if curTable is set hPrintf("<tr><td>"); printNoGenomeWarning(curTrack); hPrintf("</td></tr>"); } @@ -959,52 +959,56 @@ hPrintf("</TD></TR>\n"); } /* Print output type line. */ printStep(stepNumber++); showOutputTypeRow(isWig, isBedGr, isPositional, isMaf, isChromGraphCt, isPal, isArray, isHalSnake); /* Print output destination line. */ { char *compressType = cartUsualString(cart, hgtaCompressType, textOutCompressNone); char *fieldSep = cartUsualString(cart, hgtaOutSep, outTab); char *fileName = cartUsualString(cart, hgtaOutFileName, ""); hPrintf("<TR><TD>\n"); - hPrintf("<DIV ID=\"filename-select\"><B>Output filename:</B> "); + hPrintf("<DIV ID=\"filename-select\"><label for='%s'><B>Output filename:</B></label> ", hgtaOutFileName); cgiMakeTextVar(hgtaOutFileName, fileName, 29); hPrintf(" (<span id='excelOutNote' style='display:none'>add .csv extension if opening in Excel, </span>leave blank to keep output in browser)</DIV></TD></TR>\n"); hPrintf("<TR><TD>\n"); hPrintf("<B>Output field separator: </B>"); // tab or csv output + hPrintf("<label>"); cgiMakeRadioButton(hgtaOutSep, outTab, sameWord(outTab, fieldSep)); - hPrintf(" tsv (tab-separated)  "); + hPrintf(" tsv (tab-separated)</label>  "); + hPrintf("<label>"); cgiMakeRadioButton(hgtaOutSep, outCsv, sameWord(outCsv, fieldSep)); - hPrintf(" csv (for excel) "); + hPrintf(" csv (for excel)</label> "); hPrintf("</TD></TR>\n"); hPrintf("<TR><TD>\n"); hPrintf("<B>File type returned: </B>"); + hPrintf("<label>"); cgiMakeRadioButton(hgtaCompressType, textOutCompressNone, sameWord(textOutCompressNone, compressType)); - hPrintf(" Plain text "); + hPrintf(" Plain text</label> "); + hPrintf("<label>"); cgiMakeRadioButton(hgtaCompressType, textOutCompressGzip, sameWord(textOutCompressGzip, compressType)); - hPrintf(" Gzip compressed"); + hPrintf(" Gzip compressed</label>"); hPrintf("</TD></TR>\n"); } hPrintf("</TABLE>\n"); /* Submit buttons. */ { hPrintf("<BR><DIV ID=\"submit-select\">\n"); if (isWig || isBam || isVcf || isLongTabix || isHic) { char *name; extern char *maxOutMenu[]; char *maxOutput = maxOutMenu[0];