Skip to content

Commit 7ec42a6

Browse files
authored
Merge pull request #1700 from SundeepChand/feat/kbd-access
Improved keyboard accessibility of the find-replace popup
2 parents 85c8441 + c43fb9f commit 7ec42a6

File tree

5 files changed

+116
-108
lines changed

5 files changed

+116
-108
lines changed

client/styles/components/_editor.scss

Lines changed: 11 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -121,14 +121,6 @@ pre.CodeMirror-line {
121121
height: 100%;
122122
}
123123

124-
.CodeMirror-find-div {
125-
padding: 0;
126-
display: flex;
127-
justify-content: flex-start;
128-
align-items: center;
129-
flex-wrap: nowrap;
130-
}
131-
132124
.CodeMirror-search-modifiers {
133125
margin-left: #{10 / $base-font-size}rem;
134126
}
@@ -142,12 +134,22 @@ pre.CodeMirror-line {
142134
.CodeMirror-find-controls {
143135
display: flex;
144136
}
145-
137+
.CodeMirror-search-inputs {
138+
width: 30%;
139+
margin-left: 10px;
140+
}
146141
.CodeMirror-replace-div {
147142
display: flex;
148143
justify-content: flex-start;
149144
align-items: center;
150145
}
146+
.CodeMirror-search-controls {
147+
width: 60%;
148+
display: flex;
149+
flex-wrap: wrap-reverse;
150+
justify-content: flex-start;
151+
align-items: flex-end;
152+
}
151153
.CodeMirror-replace-controls {
152154
display: flex;
153155
margin-left: #{10 / $base-font-size}rem;

client/utils/codemirror-search.js

Lines changed: 102 additions & 99 deletions
Original file line numberDiff line numberDiff line change
@@ -82,6 +82,7 @@ function persistentDialog(cm, text, deflt, onEnter, replaceOpened, onKeyDown) {
8282
var state = getSearchState(cm);
8383

8484
CodeMirror.on(searchField, "keyup", function (e) {
85+
state.replaceStarted = false;
8586
if (e.keyCode !== 13 && searchField.value.length > 1) { // not enter and more than 1 character to search
8687
startSearch(cm, getSearchState(cm), searchField.value);
8788
} else if (searchField.value.length < 1) {
@@ -154,30 +155,30 @@ function persistentDialog(cm, text, deflt, onEnter, replaceOpened, onKeyDown) {
154155
}
155156

156157
function toggleReplace(open) {
157-
var replaceDivHeightOpened = "45px", replaceDivHeightClosed = "0px";
158158
var toggleButtonHeightOpened = "80px", toggleButtonHeightClosed = "40px";
159159

160160
if (open) {
161-
replaceDiv.style.height = replaceDivHeightOpened;
161+
replaceFieldDiv.style.display = replaceControlsDiv.style.display = '';
162162
toggleReplaceBtnDiv.style.height = toggleButtonHeightOpened;
163-
showReplaceButton.style.height = toggleButtonHeightOpened;
164-
showReplaceButton.innerHTML = triangleArrowDown;
163+
toggleReplaceBtn.style.height = toggleButtonHeightOpened;
164+
toggleReplaceBtn.innerHTML = triangleArrowDown;
165165
} else {
166-
replaceDiv.style.height = replaceDivHeightClosed;
166+
replaceFieldDiv.style.display = replaceControlsDiv.style.display = 'none';
167167
toggleReplaceBtnDiv.style.height = toggleButtonHeightClosed;
168-
showReplaceButton.style.height = toggleButtonHeightClosed;
169-
showReplaceButton.innerHTML = triangleArrowRight;
168+
toggleReplaceBtn.style.height = toggleButtonHeightClosed;
169+
toggleReplaceBtn.innerHTML = triangleArrowRight;
170170
}
171171
}
172172

173-
var showReplaceButton = dialog.getElementsByClassName("CodeMirror-replace-toggle-button")[0];
174-
var toggleReplaceBtnDiv = dialog.getElementsByClassName("Toggle-replace-btn-div")[0];
175-
var replaceDiv = dialog.getElementsByClassName("CodeMirror-replace-div")[0];
173+
var toggleReplaceBtnDiv = document.getElementById('Btn-Toggle-replace-div');
174+
var toggleReplaceBtn = document.getElementById('Btn-Toggle-replace')
175+
var replaceFieldDiv = document.getElementById('Replace-input-div');
176+
var replaceControlsDiv = document.getElementById('Replace-controls-div');
176177
if (replaceOpened) {
177178
toggleReplace(true);
178179
}
179-
CodeMirror.on(showReplaceButton, "click", function () {
180-
if (replaceDiv.style.height === "0px") {
180+
CodeMirror.on(toggleReplaceBtn, "click", function () {
181+
if (replaceFieldDiv.style.display === "none") {
181182
toggleReplace(true);
182183
} else {
183184
toggleReplace(false);
@@ -186,10 +187,6 @@ function persistentDialog(cm, text, deflt, onEnter, replaceOpened, onKeyDown) {
186187

187188
var replaceField = document.getElementById('Replace-input-field');
188189
CodeMirror.on(replaceField, "keyup", function (e) {
189-
if (!searchField.value) {
190-
searchField.focus();
191-
return;
192-
}
193190
var state = getSearchState(cm);
194191
var query = parseQuery(searchField.value, state);
195192
var withText = parseString(replaceField.value);
@@ -232,13 +229,15 @@ function persistentDialog(cm, text, deflt, onEnter, replaceOpened, onKeyDown) {
232229
if (match) {
233230
cm.setSelection(cursor.from(), cursor.to());
234231
doReplace(match, cursor, query, withText);
232+
doReplaceButton.focus();
235233
}
236234
} else {
237235
startSearch(cm, state, searchField.value);
238236
state.replaceStarted = true;
239237
cm.focus();
240238
CodeMirror.commands.findNext(cm);
241239
searchField.blur();
240+
doReplaceButton.focus();
242241
}
243242
})
244243

@@ -251,6 +250,9 @@ function persistentDialog(cm, text, deflt, onEnter, replaceOpened, onKeyDown) {
251250
var state = getSearchState(cm);
252251
var query = parseQuery(searchField.value, state);
253252
var withText = parseString(replaceField.value);
253+
if (searchField.value.length > 1) {
254+
state.replaceStarted = true;
255+
}
254256
if (state.replaceStarted) {
255257
replaceAll(cm, query, withText);
256258
state.replaceStarted = false;
@@ -491,104 +493,105 @@ function replaceAll(cm, query, text) {
491493
var getQueryDialog = function() {
492494
return (`
493495
<div class="CodeMirror-find-popup-container">
494-
<div class="Toggle-replace-btn-div">
496+
<div id="Btn-Toggle-replace-div" class="Toggle-replace-btn-div">
495497
<button
496-
title="${i18n.t('CodemirrorFindAndReplace.Replace')}"
497-
aria-label="${i18n.t('CodemirrorFindAndReplace.Replace')}"
498-
role="button"
498+
title="${i18n.t('CodemirrorFindAndReplace.ToggleReplace')}"
499+
aria-label="${i18n.t('CodemirrorFindAndReplace.ToggleReplace')}"
500+
role="button" id="Btn-Toggle-replace"
499501
class="CodeMirror-search-modifier-button CodeMirror-replace-toggle-button"
500502
>
501503
<span aria-hidden="true" class="button">
502504
${triangleArrowRight}
503505
</span>
504506
</button>
505507
</div>
506-
<div class="CodeMirror-find-input-fields">
507-
<div class="CodeMirror-find-div">
508-
<div class="CodeMirror-find-input">
509-
<input id="Find-input-field" type="text" class="search-input CodeMirror-search-field" placeholder="${i18n.t('CodemirrorFindAndReplace.FindPlaceholder')}" />
510-
</div>
511-
<div class="CodeMirror-find-controls">
512-
<div class="CodeMirror-search-modifiers button-wrap">
513-
<button
514-
title="${i18n.t('CodemirrorFindAndReplace.Regex')}"
515-
aria-label="${i18n.t('CodemirrorFindAndReplace.Regex')}"
516-
role="checkbox"
517-
class="CodeMirror-search-modifier-button CodeMirror-regexp-button"
518-
>
519-
<span aria-hidden="true" class="button">.*</span>
520-
</button>
521-
<button
522-
title="${i18n.t('CodemirrorFindAndReplace.CaseSensitive')}"
523-
aria-label="${i18n.t('CodemirrorFindAndReplace.CaseSensitive')}"
524-
role="checkbox"
525-
class="CodeMirror-search-modifier-button CodeMirror-case-button"
526-
>
527-
<span aria-hidden="true" class="button">Aa</span>
528-
</button>
529-
<button
530-
title="${i18n.t('CodemirrorFindAndReplace.WholeWords')}"
531-
aria-label="${i18n.t('CodemirrorFindAndReplace.WholeWords')}"
532-
role="checkbox"
533-
class="CodeMirror-search-modifier-button CodeMirror-word-button"
534-
>
535-
<span aria-hidden="true" class="button">" "</span>
536-
</button>
537-
</div>
538-
<div class="CodeMirror-search-nav">
539-
<p class="CodeMirror-search-results">${i18n.t('CodemirrorFindAndReplace.NoResults')}</p>
540-
<button
541-
title="${i18n.t('CodemirrorFindAndReplace.Previous')}"
542-
aria-label="${i18n.t('CodemirrorFindAndReplace.Previous')}"
543-
class="CodeMirror-search-button icon up-arrow prev"
544-
>
545-
<span aria-hidden="true">
546-
${upArrow}
547-
</span>
548-
</button>
549-
<button
550-
title="${i18n.t('CodemirrorFindAndReplace.Next')}"
551-
aria-label="${i18n.t('CodemirrorFindAndReplace.Next')}"
552-
class="CodeMirror-search-button icon down-arrow next"
553-
>
554-
<span aria-hidden="true">
555-
${downArrow}
556-
</span>
557-
</button>
558-
</div>
559-
<div class="CodeMirror-close-button-container">
560-
<button
561-
title="${i18n.t('CodemirrorFindAndReplace.Close')}"
562-
aria-label="${i18n.t('CodemirrorFindAndReplace.Close')}"
563-
class="CodeMirror-close-button close icon"
564-
>
565-
<span aria-hidden="true">
566-
${exitIcon}
567-
</span>
568-
</button>
569-
</div>
570-
</div>
508+
<div class="CodeMirror-search-inputs">
509+
<div class="CodeMirror-find-input">
510+
<input id="Find-input-field" type="text" class="search-input CodeMirror-search-field" placeholder="${i18n.t('CodemirrorFindAndReplace.FindPlaceholder')}" />
571511
</div>
572-
<div style="height: 0px; overflow: hidden;" class="CodeMirror-replace-div">
512+
<div style="display: none;" id="Replace-input-div"
513+
class="CodeMirror-replace-input">
573514
<input id="Replace-input-field" type="text" placeholder="${i18n.t('CodemirrorFindAndReplace.ReplacePlaceholder')}" class="search-input CodeMirror-search-field"/>
574-
<div class="CodeMirror-replace-controls">
515+
</div>
516+
</div>
517+
<div class="CodeMirror-search-controls">
518+
<div style="display: none;" id="Replace-controls-div" class="CodeMirror-replace-controls">
519+
<button
520+
title="${i18n.t('CodemirrorFindAndReplace.Replace')}"
521+
aria-label="${i18n.t('CodemirrorFindAndReplace.Replace')}"
522+
role="button"
523+
id="Btn-replace"
524+
class="CodeMirror-search-modifier-button CodeMirror-replace-button"
525+
>
526+
${i18n.t('CodemirrorFindAndReplace.Replace')}
527+
</button>
528+
<button
529+
title="${i18n.t('CodemirrorFindAndReplace.ReplaceAll')}"
530+
aria-label="${i18n.t('CodemirrorFindAndReplace.ReplaceAll')}"
531+
role="button"
532+
id="Btn-replace-all"
533+
class="CodeMirror-search-modifier-button CodeMirror-replace-button"
534+
>
535+
${i18n.t('CodemirrorFindAndReplace.ReplaceAll')}
536+
</button>
537+
</div>
538+
<div class="CodeMirror-find-controls">
539+
<div class="CodeMirror-search-modifiers button-wrap">
575540
<button
576-
title="${i18n.t('CodemirrorFindAndReplace.Replace')}"
577-
aria-label="${i18n.t('CodemirrorFindAndReplace.Replace')}"
578-
role="button"
579-
id="Btn-replace"
580-
class="CodeMirror-search-modifier-button CodeMirror-replace-button"
541+
title="${i18n.t('CodemirrorFindAndReplace.Regex')}"
542+
aria-label="${i18n.t('CodemirrorFindAndReplace.Regex')}"
543+
role="checkbox"
544+
class="CodeMirror-search-modifier-button CodeMirror-regexp-button"
581545
>
582-
${i18n.t('CodemirrorFindAndReplace.Replace')}
546+
<span aria-hidden="true" class="button">.*</span>
583547
</button>
584548
<button
585-
title="${i18n.t('CodemirrorFindAndReplace.ReplaceAll')}"
586-
aria-label="${i18n.t('CodemirrorFindAndReplace.ReplaceAll')}"
587-
role="button"
588-
id="Btn-replace-all"
589-
class="CodeMirror-search-modifier-button CodeMirror-replace-button"
549+
title="${i18n.t('CodemirrorFindAndReplace.CaseSensitive')}"
550+
aria-label="${i18n.t('CodemirrorFindAndReplace.CaseSensitive')}"
551+
role="checkbox"
552+
class="CodeMirror-search-modifier-button CodeMirror-case-button"
553+
>
554+
<span aria-hidden="true" class="button">Aa</span>
555+
</button>
556+
<button
557+
title="${i18n.t('CodemirrorFindAndReplace.WholeWords')}"
558+
aria-label="${i18n.t('CodemirrorFindAndReplace.WholeWords')}"
559+
role="checkbox"
560+
class="CodeMirror-search-modifier-button CodeMirror-word-button"
561+
>
562+
<span aria-hidden="true" class="button">" "</span>
563+
</button>
564+
</div>
565+
<div class="CodeMirror-search-nav">
566+
<p class="CodeMirror-search-results">${i18n.t('CodemirrorFindAndReplace.NoResults')}</p>
567+
<button
568+
title="${i18n.t('CodemirrorFindAndReplace.Previous')}"
569+
aria-label="${i18n.t('CodemirrorFindAndReplace.Previous')}"
570+
class="CodeMirror-search-button icon up-arrow prev"
571+
>
572+
<span aria-hidden="true">
573+
${upArrow}
574+
</span>
575+
</button>
576+
<button
577+
title="${i18n.t('CodemirrorFindAndReplace.Next')}"
578+
aria-label="${i18n.t('CodemirrorFindAndReplace.Next')}"
579+
class="CodeMirror-search-button icon down-arrow next"
580+
>
581+
<span aria-hidden="true">
582+
${downArrow}
583+
</span>
584+
</button>
585+
</div>
586+
<div class="CodeMirror-close-button-container">
587+
<button
588+
title="${i18n.t('CodemirrorFindAndReplace.Close')}"
589+
aria-label="${i18n.t('CodemirrorFindAndReplace.Close')}"
590+
class="CodeMirror-close-button close icon"
590591
>
591-
${i18n.t('CodemirrorFindAndReplace.ReplaceAll')}
592+
<span aria-hidden="true">
593+
${exitIcon}
594+
</span>
592595
</button>
593596
</div>
594597
</div>

translations/locales/en-US/translations.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -50,6 +50,7 @@
5050
}
5151
},
5252
"CodemirrorFindAndReplace": {
53+
"ToggleReplace": "Toggle Replace",
5354
"Find": "Find",
5455
"FindPlaceholder": "Find in files",
5556
"Replace": "Replace",

translations/locales/es-419/translations.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -50,6 +50,7 @@
5050
}
5151
},
5252
"CodemirrorFindAndReplace": {
53+
"ToggleReplace": "Alternar reemplazar",
5354
"Find": "Buscar",
5455
"FindPlaceholder": "Buscar en archivos",
5556
"Replace": "Reemplazar",

translations/locales/ja/translations.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -50,6 +50,7 @@
5050
}
5151
},
5252
"CodemirrorFindAndReplace": {
53+
"ToggleReplace": "置換の切り替え",
5354
"Find": "検索",
5455
"FindPlaceholder": "ファイル内検索",
5556
"Replace": "置換",

0 commit comments

Comments
 (0)