-
Notifications
You must be signed in to change notification settings - Fork 334
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
This is a (mostly) straight port of the JavaScript in GOV.UK Publishing Components combined with the text input component, with some minor alterations: - JavaScript has been modified to fit the Design System's coding and documentation conventions. - Support for configuration and localisation strings has been added. - The function to move specific data-attributes has been omitted for the time being. - Custom button styles have not been ported, instead using those from the Design System. - Configuration options (via JS or data-attributes) haven't been hooked up just yet.
- Loading branch information
1 parent
4d13a75
commit 83a98bb
Showing
11 changed files
with
391 additions
and
1 deletion.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
26 changes: 26 additions & 0 deletions
26
packages/govuk-frontend/src/govuk/components/password-input/_index.scss
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,26 @@ | ||
@import "../button/index"; | ||
@import "../error-message/index"; | ||
@import "../hint/index"; | ||
@import "../input/index"; | ||
@import "../label/index"; | ||
|
||
@include govuk-exports("govuk/component/password-input") { | ||
.govuk-password-input { | ||
display: flex; | ||
width: 100%; | ||
flex-direction: column; | ||
|
||
@include govuk-media-query($from: mobile) { | ||
flex-direction: row; | ||
} | ||
} | ||
|
||
.govuk-password-input__toggle { | ||
margin-bottom: 0; | ||
|
||
// Buttons are normally 100% width on this breakpoint, but we don't want that in this case | ||
@include govuk-media-query($from: mobile) { | ||
width: auto; | ||
} | ||
} | ||
} |
2 changes: 2 additions & 0 deletions
2
packages/govuk-frontend/src/govuk/components/password-input/_password-input.scss
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,2 @@ | ||
@import "../../base"; | ||
@import "./index"; |
15 changes: 15 additions & 0 deletions
15
packages/govuk-frontend/src/govuk/components/password-input/accessibility.puppeteer.test.mjs
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,15 @@ | ||
import { axe, render } from '@govuk-frontend/helpers/puppeteer' | ||
import { getExamples } from '@govuk-frontend/lib/components' | ||
|
||
describe('/components/password-input', () => { | ||
describe('component examples', () => { | ||
it('passes accessibility tests', async () => { | ||
const examples = await getExamples('password-input') | ||
|
||
for (const exampleName in examples) { | ||
await render(page, 'password-input', examples[exampleName]) | ||
await expect(axe(page)).resolves.toHaveNoViolations() | ||
} | ||
}, 120000) | ||
}) | ||
}) |
3 changes: 3 additions & 0 deletions
3
packages/govuk-frontend/src/govuk/components/password-input/macro.njk
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,3 @@ | ||
{% macro govukPasswordInput(params) %} | ||
{%- include "./template.njk" -%} | ||
{% endmacro %} |
229 changes: 229 additions & 0 deletions
229
packages/govuk-frontend/src/govuk/components/password-input/password-input.mjs
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,229 @@ | ||
import { closestAttributeValue } from '../../common/closest-attribute-value.mjs' | ||
import { | ||
extractConfigByNamespace, | ||
mergeConfigs, | ||
validateConfig | ||
} from '../../common/index.mjs' | ||
import { normaliseDataset } from '../../common/normalise-dataset.mjs' | ||
import { ConfigError, ElementError } from '../../errors/index.mjs' | ||
import { GOVUKFrontendComponent } from '../../govuk-frontend-component.mjs' | ||
import { I18n } from '../../i18n.mjs' | ||
|
||
/** | ||
* Password input component | ||
* | ||
* @preserve | ||
*/ | ||
export class PasswordInput extends GOVUKFrontendComponent { | ||
/** @private */ | ||
$module | ||
|
||
/** | ||
* @private | ||
* @type {PasswordInputConfig} | ||
*/ | ||
config | ||
|
||
/** | ||
* @private | ||
* @type {HTMLElement | null} | ||
*/ | ||
$showHideButton = null | ||
|
||
/** | ||
* @private | ||
* @type {HTMLElement | null} | ||
*/ | ||
$statusText = null | ||
|
||
/** | ||
* @param {Element} $module - HTML element to use for password input | ||
* @param {PasswordInputConfig} [config] - Password input config | ||
*/ | ||
constructor($module, config = {}) { | ||
super() | ||
|
||
if (!($module instanceof HTMLElement)) { | ||
throw new ElementError({ | ||
componentName: 'Password input', | ||
element: $module, | ||
identifier: 'Root element (`$module`)' | ||
}) | ||
} | ||
|
||
this.$wrapper = $module | ||
this.$input = $module.querySelector('input') | ||
|
||
if (!(this.$input instanceof HTMLInputElement)) { | ||
throw new ElementError({ | ||
componentName: 'Password input', | ||
element: this.$input, | ||
expectedType: 'HTMLInputElement', | ||
identifier: 'Form field (`.govuk-password-input`)' | ||
}) | ||
} | ||
|
||
this.config = mergeConfigs( | ||
PasswordInput.defaults, | ||
config || {}, | ||
normaliseDataset($module.dataset) | ||
) | ||
|
||
// Check for valid config | ||
const errors = validateConfig(PasswordInput.schema, this.config) | ||
if (errors[0]) { | ||
throw new ConfigError(`Password input: ${errors[0]}`) | ||
} | ||
|
||
this.i18n = new I18n(extractConfigByNamespace(this.config, 'i18n'), { | ||
// Read the fallback if necessary rather than have it set in the defaults | ||
locale: closestAttributeValue($module, 'lang') | ||
}) | ||
|
||
// Create and append the button element | ||
const $showHideButton = document.createElement('button') | ||
$showHideButton.className = | ||
'govuk-button govuk-button--secondary govuk-password-input__toggle' | ||
$showHideButton.setAttribute( | ||
'aria-controls', | ||
this.$input.getAttribute('id') | ||
) | ||
$showHideButton.setAttribute('type', 'button') | ||
$showHideButton.setAttribute( | ||
'aria-label', | ||
this.i18n.t('showPasswordFullText') | ||
) | ||
$showHideButton.innerHTML = this.i18n.t('showPassword') | ||
this.$showHideButton = $showHideButton | ||
this.$wrapper.insertBefore($showHideButton, this.$input.nextSibling) | ||
|
||
// Create and append the status text for screen readers | ||
const $statusText = document.createElement('span') | ||
$statusText.className = 'govuk-visually-hidden' | ||
$statusText.innerText = this.i18n.t('hiddenPasswordAnnouncement') | ||
$statusText.setAttribute('aria-live', 'polite') | ||
this.$statusText = $statusText | ||
this.$wrapper.insertBefore($statusText, this.$input.nextSibling) | ||
|
||
// Bind toggle button | ||
this.$showHideButton.addEventListener( | ||
'click', | ||
this.togglePassword.bind(this) | ||
) | ||
|
||
// Bind form submit check, unless it's been disabled | ||
if (this.$input.form && !this.config.disableFormSubmitCheck) { | ||
this.$input.form.addEventListener('submit', () => | ||
this.revertToPasswordOnFormSubmit() | ||
) | ||
} | ||
} | ||
|
||
/** | ||
* @param {MouseEvent} event - | ||
*/ | ||
togglePassword(event) { | ||
event.preventDefault() | ||
this.$input.setAttribute( | ||
'type', | ||
this.$input.type === 'password' ? 'text' : 'password' | ||
) | ||
const passwordIsHidden = this.$input.type === 'password' | ||
this.$showHideButton.innerHTML = passwordIsHidden | ||
? this.i18n.t('showPassword') | ||
: this.i18n.t('hidePassword') | ||
this.$showHideButton.setAttribute( | ||
'aria-label', | ||
passwordIsHidden | ||
? this.i18n.t('showPasswordFullText') | ||
: this.i18n.t('hidePasswordFullText') | ||
) | ||
this.$statusText.innerText = passwordIsHidden | ||
? this.i18n.t('hiddenPasswordAnnouncement') | ||
: this.i18n.t('shownPasswordAnnouncement') | ||
} | ||
|
||
/** | ||
* Revert the input to type=password when the form is submitted. This prevents | ||
* user agents potentially saving or caching the plain text password. | ||
*/ | ||
revertToPasswordOnFormSubmit() { | ||
this.$showHideButton.setAttribute( | ||
'aria-label', | ||
this.i18n.t('showPasswordFullText') | ||
) | ||
this.$showHideButton.innerHTML = this.i18n.t('showPassword') | ||
this.$statusText.innerText = this.i18n.t('hiddenPasswordAnnouncement') | ||
this.$input.setAttribute('type', 'password') | ||
} | ||
|
||
/** | ||
* Name for the component used when initialising using data-module attributes. | ||
*/ | ||
static moduleName = 'govuk-password-input' | ||
|
||
/** | ||
* Password input default config | ||
* | ||
* @see {@link PasswordInputConfig} | ||
* @constant | ||
* @default | ||
* @type {PasswordInputConfig} | ||
*/ | ||
static defaults = Object.freeze({ | ||
disableFormSubmitCheck: false, | ||
i18n: { | ||
showPassword: 'Show', | ||
hidePassword: 'Hide', | ||
showPasswordFullText: 'Show password', | ||
hidePasswordFullText: 'Hide password', | ||
shownPasswordAnnouncement: 'Your password is visible', | ||
hiddenPasswordAnnouncement: 'Your password is hidden' | ||
} | ||
}) | ||
|
||
/** | ||
* Character count config schema | ||
* | ||
* @constant | ||
* @satisfies {Schema} | ||
*/ | ||
static schema = Object.freeze({}) | ||
} | ||
|
||
/** | ||
* Password input config | ||
* | ||
* @typedef {object} PasswordInputConfig | ||
* @property {boolean} [disableFormSubmitCheck=false] - If set to `true` the | ||
* password input will not automatically change back to the `password` type | ||
* upon submission of the parent form. | ||
* @property {PasswordInputTranslations} [i18n=PasswordInput.defaults.i18n] - Password input translations | ||
*/ | ||
|
||
/** | ||
* Password input translations | ||
* | ||
* @see {@link PasswordInput.defaults.i18n} | ||
* @typedef {object} PasswordInputTranslations | ||
* | ||
* Messages displayed to the user indicating the state of the show/hide toggle. | ||
* @property {string} [showPassword] - Visible text of the button when the | ||
* password is currently hidden. HTML is acceptable. | ||
* @property {string} [hidePassword] - Visible text of the button when the | ||
* password is currently visible. HTML is acceptable. | ||
* @property {string} [showPasswordFullText] - aria-label of the button when | ||
* the password is currently hidden. Plain text only. | ||
* @property {string} [hidePasswordFullText] - aria-label of the button when | ||
* the password is currently visible. Plain text only. | ||
* @property {string} [shownPasswordAnnouncement] - Screen reader | ||
* announcement to make when the password has just become visible. | ||
* Plain text only. | ||
* @property {string} [hiddenPasswordAnnouncement] - Screen reader | ||
* announcement to make when the password has just been hidden. | ||
* Plain text only. | ||
*/ | ||
|
||
/** | ||
* @typedef {import('../../common/index.mjs').Schema} Schema | ||
*/ |
Oops, something went wrong.