@remit/ui 0.0.134 → 0.0.136

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@remit/ui",
3
- "version": "0.0.134",
3
+ "version": "0.0.136",
4
4
  "type": "module",
5
5
  "files": [
6
6
  "src"
@@ -0,0 +1,88 @@
1
+ /**
2
+ * `isWritingElsewhere` against a real focused element (#839) — mounted on jsdom
3
+ * rather than a stub, so the `type` fallback is the platform's own and not one
4
+ * the test agrees with.
5
+ */
6
+ import "@remit/test-dom";
7
+ import assert from "node:assert/strict";
8
+ import { afterEach, describe, it } from "node:test";
9
+ import { isWritingElsewhere } from "./editor-focus.js";
10
+
11
+ const editor = document.createElement("div");
12
+
13
+ const focusInput = (type: string | null): void => {
14
+ const input = document.createElement("input");
15
+ if (type !== null) input.setAttribute("type", type);
16
+ document.body.append(input);
17
+ input.focus();
18
+ };
19
+
20
+ afterEach(() => {
21
+ document.body.replaceChildren();
22
+ });
23
+
24
+ describe("isWritingElsewhere", () => {
25
+ it("reads an input the platform renders as text as writing", () => {
26
+ for (const type of [
27
+ "text",
28
+ "search",
29
+ "email",
30
+ "url",
31
+ "tel",
32
+ "password",
33
+ "number",
34
+ "date",
35
+ "datetime-local",
36
+ "month",
37
+ "time",
38
+ "week",
39
+ ]) {
40
+ document.body.replaceChildren();
41
+ focusInput(type);
42
+ assert.equal(isWritingElsewhere(editor), true, type);
43
+ }
44
+ });
45
+
46
+ it("reads an empty type as writing", () => {
47
+ // HTML falls back to a text field, so the reader may be mid-sentence in it.
48
+ focusInput("");
49
+ assert.equal(isWritingElsewhere(editor), true);
50
+ });
51
+
52
+ it("reads an unrecognised type as writing", () => {
53
+ focusInput("wibble");
54
+ assert.equal(isWritingElsewhere(editor), true);
55
+ });
56
+
57
+ it("reads a missing type as writing", () => {
58
+ focusInput(null);
59
+ assert.equal(isWritingElsewhere(editor), true);
60
+ });
61
+
62
+ it("leaves the controls alone", () => {
63
+ for (const type of [
64
+ "button",
65
+ "checkbox",
66
+ "color",
67
+ "file",
68
+ "image",
69
+ "radio",
70
+ "range",
71
+ "reset",
72
+ "submit",
73
+ ]) {
74
+ document.body.replaceChildren();
75
+ focusInput(type);
76
+ assert.equal(isWritingElsewhere(editor), false, type);
77
+ }
78
+ });
79
+
80
+ it("ignores the case the type is written in", () => {
81
+ focusInput("CheckBox");
82
+ assert.equal(isWritingElsewhere(editor), false);
83
+ });
84
+
85
+ it("is not writing elsewhere when nothing holds the caret", () => {
86
+ assert.equal(isWritingElsewhere(editor), false);
87
+ });
88
+ });
@@ -13,23 +13,26 @@
13
13
  */
14
14
 
15
15
  /**
16
- * The types an `<input>` accepts prose in. Everything else it can be — a
17
- * checkbox, a radio, a button, a file or colour picker is a control the
18
- * reader clicks, not one they are mid-word in.
16
+ * The types an `<input>` is a control rather than a writing surface in — a
17
+ * checkbox, a radio, a button, a file or colour picker, something the reader
18
+ * clicks and is never mid-word in.
19
+ *
20
+ * Named the other way round on purpose. An `<input>` with an empty or
21
+ * unrecognised `type` renders as a text field, so listing the ones that accept
22
+ * prose would read every new or misspelt type as a control and take the caret
23
+ * out of a field the reader is typing in.
19
24
  */
20
- const TEXT_INPUT_TYPES = new Set([
21
- "text",
22
- "search",
23
- "email",
24
- "url",
25
- "tel",
26
- "password",
27
- "number",
28
- "date",
29
- "datetime-local",
30
- "month",
31
- "time",
32
- "week",
25
+ const NON_TEXT_INPUT_TYPES = new Set([
26
+ "button",
27
+ "checkbox",
28
+ "color",
29
+ "file",
30
+ "hidden",
31
+ "image",
32
+ "radio",
33
+ "range",
34
+ "reset",
35
+ "submit",
33
36
  ]);
34
37
 
35
38
  /**
@@ -59,7 +62,7 @@ const takesTyping = (element: Element): boolean => {
59
62
  if (tag === "SELECT") return true;
60
63
  if (tag === "INPUT") {
61
64
  const type = (element.getAttribute("type") ?? "text").toLowerCase();
62
- return TEXT_INPUT_TYPES.has(type);
65
+ return !NON_TEXT_INPUT_TYPES.has(type);
63
66
  }
64
67
  // The attribute as well as the property: `isContentEditable` is computed by
65
68
  // the engine, and a document that never lays out does not answer for it.
@@ -655,11 +655,11 @@ describe("RunStepBody", () => {
655
655
  verb: "junk",
656
656
  scope: "once",
657
657
  failureReason:
658
- "This account has no Junk folder appointed, so there is nowhere to file these. Appoint one under Settings › Folders.",
658
+ "This account has no Junk folder appointed, so there is nowhere to file these. Appoint one under Settings › Folder roles.",
659
659
  }),
660
660
  );
661
661
  assert.match(text(html), /no Junk folder appointed/);
662
- assert.match(text(html), /Settings › Folders/);
662
+ assert.match(text(html), /Settings › Folder roles/);
663
663
  assert.doesNotMatch(html, /Nothing has changed\./);
664
664
  });
665
665
 
@@ -606,7 +606,7 @@ describe("runCopy", () => {
606
606
  // that would.
607
607
  it("carries the reason a commit could not start, in place of a retry", () => {
608
608
  const reason =
609
- "This account has no Junk folder appointed, so there is nowhere to file these. Appoint one under Settings › Folders.";
609
+ "This account has no Junk folder appointed, so there is nowhere to file these. Appoint one under Settings › Folder roles.";
610
610
  const blocked = runCopy({
611
611
  state: "commitFailed",
612
612
  verb: "junk",