Skip to content

Commit be05d00

Browse files
authored
Merge pull request #225 from WebCoder49/tests-and-docs
Add some tests; add a case of bold text misalignment to documentation
2 parents cf58991 + 9c94cea commit be05d00

8 files changed

Lines changed: 125 additions & 23 deletions

File tree

LICENSE

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
MIT License
22

3-
Copyright (c) 2021-2025 Oliver Geer and contributors
3+
Copyright (c) 2021-2026 Oliver Geer and contributors
44

55
Permission is hereby granted, free of charge, to any person obtaining a copy
66
of this software and associated documentation files (the "Software"), to deal

code-input.css

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@
55
*
66
* License of whole library for bundlers:
77
*
8-
* Copyright 2021-2025 Oliver Geer and contributors
8+
* Copyright 2021-2026 Oliver Geer and contributors
99
* @license MIT
1010
*
1111
* **<https://code-input-js.org>**
@@ -284,5 +284,7 @@ code-input textarea[data-code-input-fallback] {
284284
color: var(--code-input_highlight-text-color, inherit);
285285

286286
/* Don't overlap with message */
287-
min-height: calc(100% - var(--padding-top, 16px) - 2em - var(--padding-bottom, 16px));
287+
min-height: calc(100% - var(--padding-top, 16px) - max(2em, var(--padding-bottom, 16px)));
288+
height: calc(100% - var(--padding-top, 16px) - max(2em, var(--padding-bottom, 16px)));
289+
padding-bottom: max(2em, var(--padding-bottom, 16px))!important;
288290
}

code-input.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@
55
*
66
* License of whole library for bundlers:
77
*
8-
* Copyright 2021-2025 Oliver Geer and contributors
8+
* Copyright 2021-2026 Oliver Geer and contributors
99
* @license MIT
1010
*
1111
* **<https://code-input-js.org>**

docs/interface/css/_index.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ title = 'Styling `code-input` elements with CSS'
99
`code-input` elements can be styled like `textarea` elements in most cases; however, there are some exceptions:
1010
* The CSS variable `--padding` should be used rather than the property `padding` (e.g. `<code-input style="--padding: 10px;">...`), or `--padding-left`, `--padding-right`, `--padding-top` and `--padding-bottom` instead of the CSS properties of the same names. For technical reasons, the value must have a unit (i.e. `0px`, not `0`). To avoid overcomplicating the code, this padding is always *included in* any width/heights of `code-input` elements, so if you want to style `textarea`s and `code-input` elements with best consistency set `box-sizing: border-box` on them.
1111
* Background colours set on `code-input` elements will not work with highlighters that set background colours themselves - use `(code-input's selector) pre[class*="language-"]` for Prism.js or `.hljs` for highlight.js to target the highlighted element with higher specificity than the highlighter's theme. You may also set the `background-color` of the code-input element for its appearance when its template is unregistered / there is no JavaScript.
12+
* To minimise the chance of [this bug](https://github.com/WebCoder49/code-input/issues/130): if you import a monospace font family from an online source (for example, Google Fonts or Bunny Fonts) to style `code-input` elements, and use a syntax highlighting theme that includes **bold text**, import both the standard (400) and bold (700) weights of the font.
1213
* The caret and placeholder colour by default follow and give good contrast with the highlighted theme. Setting a CSS rule for `color` and/or `caret-color` properties on the code-input element will override this behaviour.
1314
* For technical reasons, `code-input:focus` won't match anything. Use `code-input:has(textarea:focus)` instead.
1415
* For now, elements on top of `code-input` elements should have a CSS `z-index` at least 3 greater than the `code-input` element.

docs/modules-and-frameworks/custom/_index.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
+++
2-
title = 'Using code-input.js with custom highlighting algorihms in projects which use modules or frameworks'
2+
title = 'Using code-input.js with custom highlighting algorithms in projects which use modules or frameworks'
33
+++
44

55
# Using code-input.js with custom highlighting algorithms in projects which use modules or frameworks

plugins/autogrow.css

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -71,7 +71,7 @@ code-input.code-input_autogrow_height:has(textarea[data-code-input-fallback]) {
7171
--code-input_autogrow_internal-min-height: calc(1lh + 2em + var(--padding-top, 16px) + var(--padding-bottom, 16px)); /* So minimum height possible while containing highlighted code */
7272
height: var(--code-input_autogrow_true-min-height);
7373
}
74-
code-input textarea[data-code-input-fallback] {
75-
height: calc(var(--code-input_autogrow_true-min-height) - var(--padding-top, 16px) - var(--padding-bottom, 16px) - 2em)!important; /* So minimum height possible while containing highlighted code */
76-
min-height: calc(100% - var(--padding-top, 16px) - var(--padding-bottom, 16px) - 2em);
74+
code-input.code-input_autogrow_height textarea[data-code-input-fallback] {
75+
height: calc(var(--code-input_autogrow_true-min-height) - var(--padding-top, 16px) - max(2em, var(--padding-bottom, 16px)))!important;
76+
min-height: calc(var(--code-input_autogrow_true-min-height) - var(--padding-top, 16px) - max(2em, var(--padding-bottom, 16px)));
7777
}

tests/prism.html

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -41,7 +41,7 @@ <h4><a href="hljs.html">Test for highlight.js</a></h4>
4141
<p>This page carries out automated tests for the code-input library to check that both the core components and the plugins work in some ways. It doesn't fully cover every scenario so you should test any code you change by hand, but it's good for quickly checking a wide range of functionality works.</p>
4242

4343
<details id="collapse-results"><summary>Test Results (Click to Open)</summary><pre id="test-results"></pre></details>
44-
<form method="GET" class="match-braces line-numbers" action="afterform.html" target="_blank">
44+
<form method="GET" class="match-braces" action="afterform.html" target="_blank">
4545
<code-input><textarea data-code-input-fallback name="q">console.log("Hello, World!");
4646
// A second line
4747
// A third line with &lt;html> tags</textarea></code-input>

tests/tester.js

Lines changed: 113 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -105,16 +105,83 @@ function waitAsync(milliseconds) {
105105
/* --- Running the tests --- */
106106

107107
/* Start the test, for Prism.js if isHLJS is false, or for highlight.js if isHLJS is true. */
108-
function beginTest(isHLJS) {
108+
var loadEventFired = false; // Global variable so can check the load event is fired in startLoad function
109+
var popupClicked = false; // Global variable for Autocomplete plugin
110+
async function beginTest(isHLJS) {
109111
let codeInputElem = document.querySelector("code-input");
112+
codeInputElem.addEventListener("code-input_load", () => {
113+
loadEventFired = true;
114+
testAssertion("Load", "code-input_load Event Fired Late Enough", codeInputElem.querySelector("textarea:not([data-code-input-fallback])") != null, "code-input_load event fired before non-fallback textarea element appeared");
115+
});
116+
const fallbackTextarea = codeInputElem.querySelector("textarea[data-code-input-fallback]");
117+
118+
119+
startLoad(codeInputElem, isHLJS);
120+
codeInputElem.style.height = "calc(1lh + 2em)"; // 2em for the "No highlighting." message
121+
codeInputElem.style.setProperty("--padding", "0px");
122+
await waitAsync(50); // Wait for display to update
123+
// Select the "A" in the fallback textarea's last line
124+
fallbackTextarea.selectionStart = 50;
125+
fallbackTextarea.selectionEnd = 51;
126+
fallbackTextarea.focus();
127+
fallbackTextarea.scrollTo(0, codeInputElem.clientHeight * 3); // At least 3 lines
128+
129+
await waitAsync(50); // Wait for scroll to occur
130+
testAssertion("FallbackTextarea", "Scrolls Correctly", confirm("Is the phrase 'A third', with 'A' highlighted, visible? "), "user-judged");
131+
codeInputElem.style.removeProperty("--padding");
132+
codeInputElem.style.removeProperty("height");
133+
134+
// Select the "log" in the fallback textarea's initial line
135+
fallbackTextarea.selectionStart = 8;
136+
fallbackTextarea.selectionEnd = 11;
137+
fallbackTextarea.focus();
138+
139+
codeInputElem.style.setProperty("--padding", "100px");
140+
await waitAsync(50); // Wait for display to update
141+
testAssertion("FallbackTextarea", "Displayed Correctly With More Padding", confirm("Is the highlighted 'log' properly aligned after the 'console.', which is then properly aligned inside the visible (non-highlighted) textarea? Also, there should be a lot of padding."), "user-judged");
142+
codeInputElem.style.setProperty("--padding", "0px");
143+
await waitAsync(50); // Wait for display to update
144+
testAssertion("FallbackTextarea", "Displayed Correctly With No Padding", confirm("Does the element have zero padding, but otherwise the display remains correct?"), "user-judged");
145+
codeInputElem.style.removeProperty("--padding");
146+
await waitAsync(50); // Wait for display to update
147+
testAssertion("FallbackTextarea", "Displayed Correctly By Default", confirm("Is the highlighted 'log' properly aligned after the 'console.', which is then properly aligned inside the visible (non-highlighted) textarea?"), "user-judged");
148+
149+
150+
codeInputElem.classList.add("code-input_autogrow_height");
151+
await waitAsync(50); // Wait for display to update
152+
testAssertion("FallbackTextarea-Autogrow", "Displayed Correctly With Default Autogrow Height", confirm("Is the element not very tall (but tall enough to properly show some code), but otherwise the display remains correct?"), "user-judged");
153+
154+
codeInputElem.style.setProperty("--code-input_autogrow_min-height", "200px");
155+
await waitAsync(50); // Wait for display to update
156+
assertEqual("FallbackTextarea-Autogrow", "--code-input_autogrow_min-height Sets Height", codeInputElem.clientHeight, 200);
157+
codeInputElem.style.removeProperty("--code-input_autogrow_min-height");
158+
codeInputElem.classList.remove("code-input_autogrow_height");
159+
160+
codeInputElem.classList.add("code-input_autogrow_width");
161+
await waitAsync(50); // Wait for display to update
162+
testAssertion("FallbackTextarea-Autogrow", "Displayed Correctly With Default Autogrow Width", confirm("Is the element a sensible but narrow width, and otherwise the display remains correct?"), "user-judged");
163+
164+
codeInputElem.style.setProperty("--code-input_autogrow_min-width", "200px");
165+
await waitAsync(50); // Wait for display to update
166+
assertEqual("FallbackTextarea-Autogrow", "--code-input_autogrow_min-height Sets Height", codeInputElem.clientWidth, 200);
167+
codeInputElem.style.removeProperty("--code-input_autogrow_min-width");
168+
codeInputElem.classList.remove("code-input_autogrow_width");
169+
170+
171+
if(!isHLJS) {
172+
codeInputElem.classList.add("line-numbers");
173+
await waitAsync(50); // Wait for display to update
174+
testAssertion("FallbackTextarea-PrismLineNumbers", "Displayed Correctly With line-numbers Class", confirm("Is there more padding to the left now, but otherwise the display remains correct?"), "user-judged");
175+
}
176+
110177
if(isHLJS) {
111178
codeInput.registerTemplate("code-editor", new codeInput.templates.Hljs(hljs, [
112179
new codeInput.plugins.AutoCloseBrackets(),
113180
new codeInput.plugins.Autocomplete(function(popupElem, textarea, selectionEnd, selectionStart) {
114181
if(selectionStart == selectionEnd && textarea.value.substring(selectionEnd-5, selectionEnd) == "popup") {
115182
// Show popup
116183
popupElem.style.display = "block";
117-
popupElem.innerHTML = "Here's your popup!";
184+
popupElem.innerHTML = '<button onclick="event.preventDefault(); popupClicked = true;">This is the popup.</button>';
118185
} else {
119186
popupElem.style.display = "none";
120187
}
@@ -133,7 +200,7 @@ function beginTest(isHLJS) {
133200
if(selectionStart == selectionEnd && textarea.value.substring(selectionEnd-5, selectionEnd) == "popup") {
134201
// Show popup
135202
popupElem.style.display = "block";
136-
popupElem.innerHTML = "Here's your popup!";
203+
popupElem.innerHTML = '<button onclick="event.preventDefault(); popupClicked = true;">This is the popup.</button>';
137204
} else {
138205
popupElem.style.display = "none";
139206
}
@@ -145,19 +212,21 @@ function beginTest(isHLJS) {
145212
new codeInput.plugins.SpecialChars(true),
146213
]));
147214
}
148-
startLoad(codeInputElem, isHLJS);
149215
}
150216

151217
/* Start loading the tests, using the codeInput load time as one of the tests. */
152218
function startLoad(codeInputElem, isHLJS) {
153219
let textarea;
154220
let timeToLoad = 0;
155221
let interval = window.setInterval(() => {
156-
textarea = codeInputElem.querySelector("textarea");
157-
if(textarea != null) window.clearInterval(interval);
158-
timeToLoad += 10;
159-
testData("TimeTaken", "Textarea Appears", timeToLoad+"ms (nearest 10)");
160-
startTests(textarea, isHLJS);
222+
textarea = codeInputElem.querySelector("textarea:not([data-code-input-fallback])");
223+
if(textarea != null) {
224+
testAssertion("Load", "code-input_load Event Fired Early Enough", loadEventFired, "non-fallback textarea element appeared before code-input_load event");
225+
window.clearInterval(interval);
226+
timeToLoad += 10;
227+
testData("Load", "Time Taken for Textarea to Appear", timeToLoad+"ms (nearest 10)");
228+
beginTestsAfterLoad(textarea, isHLJS);
229+
}
161230
}, 10);
162231
}
163232

@@ -178,16 +247,27 @@ function allowInputEvents(inputElement, codeInputElement=undefined) {
178247
}
179248

180249
/* Start the tests using the textarea inside the code-input element and whether highlight.js is being used (as the Autodetect plugin only works with highlight.js, for example) */
181-
async function startTests(textarea, isHLJS) {
250+
async function beginTestsAfterLoad(textarea, isHLJS) {
182251
textarea.focus();
252+
if(!isHLJS) {
253+
await waitAsync(200); // Wait for display to update
254+
testAssertion("FallbackTextarea-PrismLineNumbers", "Alignment between Fallback and Loaded Texareas", confirm("Now with the highlighting, is all the code in the same horizontal position (don't mind the vertical offset by keyboard navigation instructions) as with the last question?"), "user-judged");
255+
256+
} else {
257+
await waitAsync(200); // Wait for display to update
258+
testAssertion("FallbackTextarea+PrismLineNumbers", "Alignment between Fallback and Loaded Texareas", confirm("Now with the highlighting, is all the code in the same horizontal position (don't mind the vertical offset by keyboard navigation instructions) as with the last question?"), "user-judged");
259+
}
183260

184261
codeInputElement = textarea.parentElement;
185262
allowInputEvents(textarea, codeInputElement);
186263

187264
/*--- Tests for core functionality ---*/
188265

189266
// Textarea's initial value should be correct.
190-
assertEqual("Core", "Initial Textarea Value", textarea.value, `console.log("Hello, World!");
267+
assertEqual("FallbackTextarea", "Textarea selectionStart Once Loaded same as Fallback Textarea's", textarea.selectionStart, 8);
268+
assertEqual("FallbackTextarea", "Textarea selectionEnd Once Loaded same as Fallback Textarea's", textarea.selectionEnd, 11);
269+
270+
assertEqual("FallbackTextarea", "Textarea Value Once Loaded same as Fallback Textarea's", textarea.value, `console.log("Hello, World!");
191271
// A second line
192272
// A third line with <html> tags`);
193273
// Code element's displayed value, ignoring appearance with HTML tags, should be the initial value but HTML-escaped
@@ -439,12 +519,31 @@ console.log("I've got another line!", 2 &lt; 3, "should be true.");
439519
await waitAsync(50); // Wait for popup to be rendered
440520

441521
testAssertion("Autocomplete", "Popup Shows on arrow key", confirm("Does the autocomplete popup display correctly? (OK=Yes)"), "user-judged");
442-
backspace(textarea);
443522

444-
await waitAsync(50); // Wait for popup disappearance to be rendered
445523

524+
backspace(textarea);
525+
await waitAsync(50); // Wait for popup disappearance to be rendered
446526
testAssertion("Autocomplete", "Popup Disappears on backspace", confirm("Has the popup disappeared? (OK=Yes)"), "user-judged");
447-
move(textarea, 1);
527+
528+
addText(textarea, "p");
529+
await waitAsync(50);
530+
531+
const beforeClickSelectionStart = textarea.selectionStart;
532+
const beforeClickSelectionEnd = textarea.selectionEnd;
533+
alert("Dismiss this alert, then click the popup in the next 3 seconds.");
534+
// To speed up test, wait until 3s have passed or a click has fired, whichever is earlier.
535+
let timePassed = 0;
536+
while(timePassed < 3000 && !popupClicked) {
537+
await waitAsync(10);
538+
timePassed += 10;
539+
}
540+
testAssertion("Autocomplete", "Popup Clickable", popupClicked, "The onclick event of the popup element didn't fire");
541+
// In case clicking the popup moved the caret inside the textarea:
542+
textarea.selectionStart = beforeClickSelectionStart;
543+
textarea.selectionEnd = beforeClickSelectionEnd;
544+
textarea.focus();
545+
546+
backspace(textarea);
448547
backspace(textarea);
449548
backspace(textarea);
450549
backspace(textarea);

0 commit comments

Comments
 (0)