Skip to content

Commit

Permalink
Add password option to text inputs (#167)
Browse files Browse the repository at this point in the history
  • Loading branch information
ahosgood authored Jan 13, 2025
1 parent 43f1dab commit 72865f4
Show file tree
Hide file tree
Showing 11 changed files with 125 additions and 11 deletions.
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
### Added

- Capture user preferences (such as dark mode, reduced motion etc.) in GA4
- Text inputs can be turned into password inputs with `password: true`

### Changed

Expand Down
2 changes: 2 additions & 0 deletions src/nationalarchives/all+analytics.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ import {
Picture,
SkipLink,
Tabs,
TextInput,
} from "./all.mjs";
import { EventTracker, GA4, helpers } from "./analytics.mjs";

Expand All @@ -29,6 +30,7 @@ export {
Picture,
SkipLink,
Tabs,
TextInput,
EventTracker,
GA4,
helpers,
Expand Down
7 changes: 7 additions & 0 deletions src/nationalarchives/all.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import { Header } from "./components/header/header.mjs";
import { Picture } from "./components/picture/picture.mjs";
import { SkipLink } from "./components/skip-link/skip-link.mjs";
import { Tabs } from "./components/tabs/tabs.mjs";
import { TextInput } from "./components/text-input/text-input.mjs";
import Cookies from "./lib/cookies.mjs";

const initAll = (options) => {
Expand Down Expand Up @@ -101,6 +102,11 @@ const initAll = (options) => {
new Tabs($tabModule);
});

const $textInputs = $scope.querySelectorAll('[data-module="tna-text-input"]');
$textInputs.forEach(($textInput) => {
new TextInput($textInput);
});

const checkTableForScroll = ($tableWrapper) => {
const scrollable = $tableWrapper.scrollWidth > $tableWrapper.clientWidth;
const $tableCaption = $tableWrapper.querySelector(".tna-table__caption");
Expand Down Expand Up @@ -172,4 +178,5 @@ export {
Picture,
SkipLink,
Tabs,
TextInput,
};
24 changes: 18 additions & 6 deletions src/nationalarchives/components/text-input/fixtures.json
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@
"id": "name",
"name": "name"
},
"html": "<div class=\"tna-form__group\"><div class=\"tna-form__group-contents\"><h4 class=\"tna-form__heading tna-form__heading--m\"><label class=\"tna-form__label\" for=\"name\">Enter your first name</label></h4></div><input type=\"text\" id=\"name\" class=\"tna-text-input \" name=\"name\" value=\"\" spellcheck=\"false\"></div>"
"html": "<div class=\"tna-form__group\" data-module=\"tna-text-input\"><div class=\"tna-form__group-contents\"><h4 class=\"tna-form__heading tna-form__heading--m\"><label class=\"tna-form__label\" for=\"name\">Enter your first name</label></h4></div><input id=\"name\" class=\"tna-text-input \" name=\"name\" value=\"\" type=\"text\" spellcheck=\"false\"></div>"
},
{
"name": "text input with a preselected value",
Expand All @@ -22,7 +22,7 @@
"name": "name",
"value": "John"
},
"html": "<div class=\"tna-form__group\"><div class=\"tna-form__group-contents\"><h4 class=\"tna-form__heading tna-form__heading--m\"><label class=\"tna-form__label\" for=\"name\">Enter your first name</label></h4></div><input type=\"text\" id=\"name\" class=\"tna-text-input \" name=\"name\" value=\"John\" spellcheck=\"false\"></div>"
"html": "<div class=\"tna-form__group\" data-module=\"tna-text-input\"><div class=\"tna-form__group-contents\"><h4 class=\"tna-form__heading tna-form__heading--m\"><label class=\"tna-form__label\" for=\"name\">Enter your first name</label></h4></div><input id=\"name\" class=\"tna-text-input \" name=\"name\" value=\"John\" type=\"text\" spellcheck=\"false\"></div>"
},
{
"name": "text input with a hint",
Expand All @@ -34,7 +34,7 @@
"name": "name",
"hint": "What people call you by."
},
"html": "<div class=\"tna-form__group\"><div class=\"tna-form__group-contents\"><h4 class=\"tna-form__heading tna-form__heading--m\"><label class=\"tna-form__label\" for=\"name\">Enter your first name</label></h4><p id=\"name-hint\" class=\"tna-form__hint\">What people call you by.</p></div><input type=\"text\" id=\"name\" class=\"tna-text-input \" name=\"name\" value=\"\" spellcheck=\"false\" aria-describedby=\"name-hint \"></div>"
"html": "<div class=\"tna-form__group\" data-module=\"tna-text-input\"><div class=\"tna-form__group-contents\"><h4 class=\"tna-form__heading tna-form__heading--m\"><label class=\"tna-form__label\" for=\"name\">Enter your first name</label></h4><p id=\"name-hint\" class=\"tna-form__hint\">What people call you by.</p></div><input id=\"name\" class=\"tna-text-input \" name=\"name\" value=\"\" aria-describedby=\"name-hint \" type=\"text\" spellcheck=\"false\"></div>"
},
{
"name": "text input with an error",
Expand All @@ -48,7 +48,7 @@
"text": "Enter a name"
}
},
"html": "<div class=\"tna-form__group tna-form__group--error\"><div class=\"tna-form__group-contents\"><h4 class=\"tna-form__heading tna-form__heading--m\"><label class=\"tna-form__label\" for=\"name\">Enter your first name</label></h4><p id=\"name-error\" class=\"tna-form__error-message\"><span class=\"tna-!--visually-hidden\">Error:</span> Enter a name</p></div><input type=\"text\" id=\"name\" class=\"tna-text-input \" name=\"name\" value=\"\" spellcheck=\"false\" aria-describedby=\" name-error\"></div>"
"html": "<div class=\"tna-form__group tna-form__group--error\" data-module=\"tna-text-input\"><div class=\"tna-form__group-contents\"><h4 class=\"tna-form__heading tna-form__heading--m\"><label class=\"tna-form__label\" for=\"name\">Enter your first name</label></h4><p id=\"name-error\" class=\"tna-form__error-message\"><span class=\"tna-!--visually-hidden\">Error:</span> Enter a name</p></div><input id=\"name\" class=\"tna-text-input \" name=\"name\" value=\"\" aria-describedby=\" name-error\" type=\"text\" spellcheck=\"false\"></div>"
},
{
"name": "inline text input",
Expand All @@ -60,7 +60,7 @@
"name": "name",
"inline": true
},
"html": "<div class=\"tna-form__group tna-form__group--inline\"><div class=\"tna-form__group-contents\"><h4 class=\"tna-form__heading tna-form__heading--m\"><label class=\"tna-form__label\" for=\"name\">Enter your first name</label></h4></div><input type=\"text\" id=\"name\" class=\"tna-text-input \" name=\"name\" value=\"\" spellcheck=\"false\"></div>"
"html": "<div class=\"tna-form__group tna-form__group--inline\" data-module=\"tna-text-input\"><div class=\"tna-form__group-contents\"><h4 class=\"tna-form__heading tna-form__heading--m\"><label class=\"tna-form__label\" for=\"name\">Enter your first name</label></h4></div><input id=\"name\" class=\"tna-text-input \" name=\"name\" value=\"\" type=\"text\" spellcheck=\"false\"></div>"
},
{
"name": "text input size",
Expand All @@ -72,7 +72,19 @@
"name": "name",
"size": "m"
},
"html": "<div class=\"tna-form__group\"><div class=\"tna-form__group-contents\"><h4 class=\"tna-form__heading tna-form__heading--m\"><label class=\"tna-form__label\" for=\"name\">Enter your first name</label></h4></div><input type=\"text\" id=\"name\" class=\"tna-text-input tna-text-input--m \" name=\"name\" value=\"\" spellcheck=\"false\"></div>"
"html": "<div class=\"tna-form__group\" data-module=\"tna-text-input\"><div class=\"tna-form__group-contents\"><h4 class=\"tna-form__heading tna-form__heading--m\"><label class=\"tna-form__label\" for=\"name\">Enter your first name</label></h4></div><input id=\"name\" class=\"tna-text-input tna-text-input--m \" name=\"name\" value=\"\" type=\"text\" spellcheck=\"false\"></div>"
},
{
"name": "password",
"options": {
"label": "Enter your password",
"headingLevel": 4,
"headingSize": "m",
"id": "password",
"name": "password",
"size": "m"
},
"html": "<div class=\"tna-form__group\" data-module=\"tna-text-input\"><div class=\"tna-form__group-contents\"><h4 class=\"tna-form__heading tna-form__heading--m\"><label class=\"tna-form__label\" for=\"password\">Enter your password</label></h4></div><input id=\"password\" class=\"tna-text-input tna-text-input--m \" name=\"password\" value=\"\" type=\"text\" spellcheck=\"false\"></div>"
}
]
}
6 changes: 6 additions & 0 deletions src/nationalarchives/components/text-input/macro-options.json
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,12 @@
}
]
},
{
"name": "password",
"type": "boolean",
"required": false,
"description": ""
},
{
"name": "spellcheck",
"type": "boolean",
Expand Down
2 changes: 1 addition & 1 deletion src/nationalarchives/components/text-input/template.njk
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
{%- set containerClasses = containerClasses.concat('tna-form__group--inline') -%}
{%- endif -%}
{%- set classes = containerClasses | join(' ') -%}
<div class="tna-form__group{% if params.error %} tna-form__group--error{% endif %}{% if classes %} {{ classes }}{% endif %}" {%- for attribute, value in params.formGroupAttributes %} {{ attribute }}{% if value !== '' %}="{{ value }}"{% endif %}{% endfor %}>
<div class="tna-form__group{% if params.error %} tna-form__group--error{% endif %}{% if classes %} {{ classes }}{% endif %}" data-module="tna-text-input" {%- for attribute, value in params.formGroupAttributes %} {{ attribute }}{% if value !== '' %}="{{ value }}"{% endif %}{% endfor %}>
<div class="tna-form__group-contents">
<h{{ params.headingLevel }} class="tna-form__heading tna-form__heading--{{ params.headingSize or 'm' }}">
<label class="tna-form__label" for="{{ params.id }}">
Expand Down
54 changes: 54 additions & 0 deletions src/nationalarchives/components/text-input/text-input.mjs
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
export class TextInput {
constructor($module) {
this.$module = $module;
this.$input = $module && $module.querySelector(".tna-text-input");

if (!this.$module || !this.$input) {
return;
}

if (this.$input.getAttribute("type") === "password") {
this.$textInputWrapper = document.createElement("div");
this.$textInputWrapper.classList.add("tna-text-input-wrapper");

this.$textInputPasswordToggle = document.createElement("button");
this.$textInputPasswordToggle.setAttribute("type", "button");
this.$textInputPasswordToggle.classList.add(
"tna-button",
"tna-button--small",
"tna-button--icon-only",
);
this.updateTogglePasswordButtonText();

this.$textInputWrapper.appendChild(this.$input);
this.$textInputWrapper.appendChild(this.$textInputPasswordToggle);
this.$module.appendChild(this.$textInputWrapper);

this.$textInputPasswordToggle.addEventListener("click", () => {
this.togglePasswordType();
this.$input.focus();
});
}
}

togglePasswordType() {
if (this.$input.getAttribute("type") === "password") {
this.$input.setAttribute("type", "text");
} else {
this.$input.setAttribute("type", "password");
}
this.updateTogglePasswordButtonText();
}

updateTogglePasswordButtonText() {
if (this.$input.getAttribute("type") === "password") {
this.$textInputPasswordToggle.innerHTML = `<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" height="32" aria-hidden="true" focusable="false"><!--!Font Awesome Free 6.7.2 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license/free Copyright 2025 Fonticons, Inc.--><path d="M288 32c-80.8 0-145.5 36.8-192.6 80.6C48.6 156 17.3 208 2.5 243.7c-3.3 7.9-3.3 16.7 0 24.6C17.3 304 48.6 356 95.4 399.4C142.5 443.2 207.2 480 288 480s145.5-36.8 192.6-80.6c46.8-43.5 78.1-95.4 93-131.1c3.3-7.9 3.3-16.7 0-24.6c-14.9-35.7-46.2-87.7-93-131.1C433.5 68.8 368.8 32 288 32zM144 256a144 144 0 1 1 288 0 144 144 0 1 1 -288 0zm144-64c0 35.3-28.7 64-64 64c-7.1 0-13.9-1.2-20.3-3.3c-5.5-1.8-11.9 1.6-11.7 7.4c.3 6.9 1.3 13.8 3.2 20.7c13.7 51.2 66.4 81.6 117.6 67.9s81.6-66.4 67.9-117.6c-11.1-41.5-47.8-69.4-88.6-71.1c-5.8-.2-9.2 6.1-7.4 11.7c2.1 6.4 3.3 13.2 3.3 20.3z"/></svg>Show password`;
this.$textInputPasswordToggle.setAttribute("title", "Show password");
this.$textInputPasswordToggle.setAttribute("aria-title", "Show password");
} else {
this.$textInputPasswordToggle.innerHTML = `<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 640 512" height="32" aria-hidden="true" focusable="false"><!--!Font Awesome Free 6.7.2 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license/free Copyright 2025 Fonticons, Inc.--><path d="M38.8 5.1C28.4-3.1 13.3-1.2 5.1 9.2S-1.2 34.7 9.2 42.9l592 464c10.4 8.2 25.5 6.3 33.7-4.1s6.3-25.5-4.1-33.7L525.6 386.7c39.6-40.6 66.4-86.1 79.9-118.4c3.3-7.9 3.3-16.7 0-24.6c-14.9-35.7-46.2-87.7-93-131.1C465.5 68.8 400.8 32 320 32c-68.2 0-125 26.3-169.3 60.8L38.8 5.1zM223.1 149.5C248.6 126.2 282.7 112 320 112c79.5 0 144 64.5 144 144c0 24.9-6.3 48.3-17.4 68.7L408 294.5c8.4-19.3 10.6-41.4 4.8-63.3c-11.1-41.5-47.8-69.4-88.6-71.1c-5.8-.2-9.2 6.1-7.4 11.7c2.1 6.4 3.3 13.2 3.3 20.3c0 10.2-2.4 19.8-6.6 28.3l-90.3-70.8zM373 389.9c-16.4 6.5-34.3 10.1-53 10.1c-79.5 0-144-64.5-144-144c0-6.9 .5-13.6 1.4-20.2L83.1 161.5C60.3 191.2 44 220.8 34.5 243.7c-3.3 7.9-3.3 16.7 0 24.6c14.9 35.7 46.2 87.7 93 131.1C174.5 443.2 239.2 480 320 480c47.8 0 89.9-12.9 126.2-32.5L373 389.9z"/></svg></i>Hide password`;
this.$textInputPasswordToggle.setAttribute("title", "Hide password");
this.$textInputPasswordToggle.setAttribute("aria-title", "Hide password");
}
}
}
6 changes: 4 additions & 2 deletions src/nationalarchives/components/text-input/text-input.njk
Original file line number Diff line number Diff line change
@@ -1,9 +1,11 @@
{%- macro tnaTextInputElement(params, extraAttributes) -%}
{%- set inputClasses = [params.classes] if params.classes else [] -%}
<input type="text" id="{{ params.id }}" class="tna-text-input{% if params.size %} tna-text-input--{{ params.size }}{% endif %} {{ inputClasses | join(' ') }}" name="{{ params.name }}" value="{{ params.value }}" spellcheck="{{ params.spellcheck if params.spellcheck else false }}"
<input id="{{ params.id }}" class="tna-text-input{% if params.size %} tna-text-input--{{ params.size }}{% endif %} {{ inputClasses | join(' ') }}" name="{{ params.name }}" value="{{ params.value }}"
{%- if params.hint or params.error %} aria-describedby="{%- if params.hint -%}{{ params.id }}-hint{%- endif %} {% if params.error -%}{{ params.id }}-error{%- endif -%}"{%- endif %}
{%- if params.maxLength %} maxlength="{{ params.maxLength }}"{% endif %}
{%- if params.autofill %} autofill="{{ params.autofill }}"{% endif %}
{%- if params.password %} type="password" autocapitalize="none" autocomplete="off" autocorrect="off" spellcheck="false"{%- else %} type="text" spellcheck="{{ params.spellcheck if params.spellcheck else false }}"
{%- if params.autofill %} autofill="{{ params.autofill }}"{% endif %}
{%- endif %}
{%- if params.inputmode %} inputmode="{{ params.inputmode }}"{% endif %}
{%- for attribute, value in params.attributes %} {{ attribute }}{% if value !== '' %}="{{ value }}"{% endif %}{% endfor %}
{%- for attribute, value in extraAttributes %} {{ attribute }}{% if value !== '' %}="{{ value }}"{% endif %}{% endfor %}>
Expand Down
18 changes: 17 additions & 1 deletion src/nationalarchives/components/text-input/text-input.scss
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,6 @@
@use "../../tools/spacing";

.tna-text-input {
width: 100%;
padding: 0 spacing.space(0.375);

display: block;
Expand All @@ -18,6 +17,11 @@
@include colour.colour-border("input-border", forms.$form-field-border-width);
border-radius: 0.1px;

&,
&-wrapper {
width: 100%;
}

.tna-form__group--error & {
@include colour.colour-border("form-error-border");
}
Expand All @@ -41,4 +45,16 @@
&--xl {
max-width: 80rem;
}

&-wrapper & {
border-right-width: 0;
}

&-wrapper {
display: flex;

.tna-button {
white-space: nowrap;
}
}
}
14 changes: 14 additions & 0 deletions src/nationalarchives/components/text-input/text-input.stories.js
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ const argTypes = {
hint: { control: "text" },
value: { control: "text" },
error: { control: "object" },
password: { control: "boolean" },
spellcheck: { control: "boolean" },
inputmode: {
control: "select",
Expand Down Expand Up @@ -97,6 +98,7 @@ const Template = ({
hint,
value,
error,
password,
spellcheck,
inputmode,
autofill,
Expand All @@ -118,6 +120,7 @@ const Template = ({
hint,
value,
error,
password,
spellcheck,
inputmode,
autofill,
Expand Down Expand Up @@ -186,3 +189,14 @@ Inline.args = {
inline: true,
classes: "tna-text-input--demo",
};

export const Password = Template.bind({});
Password.args = {
label: "Enter your password",
headingLevel: 4,
headingSize: "m",
id: "password",
name: "password",
password: true,
classes: "tna-text-input--demo",
};
2 changes: 1 addition & 1 deletion tasks/test-package.js
Original file line number Diff line number Diff line change
Expand Up @@ -113,7 +113,7 @@ const checkExists = [
...componentFiles("sidebar"),
...componentFiles("skip-link", "SkipLink"),
...componentFiles("tabs", "Tabs"),
...componentFiles("text-input"),
...componentFiles("text-input", "TextInput"),
...componentFiles("textarea"),
...componentFiles("warning"),
// Tools
Expand Down

0 comments on commit 72865f4

Please # to comment.