Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Update "Try another way" input to use a disclosure menu #15145

Merged
2 changes: 2 additions & 0 deletions CHANGELOG-WIP.md
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,8 @@
- Darkened the color of footer links to meet the minimum contrast for text.
- Set the language of the Craft edition in the footer, to improve screen reader pronunciation for non-English languages.
- The accessible name of “Select site” buttons is now translated to the current language.
- Updated the “Try another way” menu for 2FA login options to use a disclosure menu.
- Added a loading spinner when switching between 2FA login methods that is accessible to screen reader users.

### Administration
- Added the `--format` option to the `db/backup` and `db/restore` commands for PostgreSQL installs. ([#14931](https://github.com/craftcms/cms/pull/14931))
Expand Down
2 changes: 1 addition & 1 deletion src/web/assets/cp/dist/cp.js

Large diffs are not rendered by default.

2 changes: 1 addition & 1 deletion src/web/assets/cp/dist/cp.js.map

Large diffs are not rendered by default.

2 changes: 1 addition & 1 deletion src/web/assets/cp/dist/css/cp.css

Large diffs are not rendered by default.

2 changes: 1 addition & 1 deletion src/web/assets/cp/dist/css/cp.css.map

Large diffs are not rendered by default.

1 change: 1 addition & 0 deletions src/web/assets/cp/src/Craft.js
Original file line number Diff line number Diff line change
Expand Up @@ -99,6 +99,7 @@ import './js/UserIndex.js';
// Custom elements
import './js/CraftGlobalSidebar.js';
import './js/CraftDisclosure.js';
import './js/CraftSpinner.js';
import './js/CraftTooltip.js';
import './js/CraftElementLabel';
import './js/CraftProxyScrollbar';
10 changes: 10 additions & 0 deletions src/web/assets/cp/src/css/_craft-spinner.scss
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
craft-spinner {
display: flex;
justify-content: center;

.wrapper {
display: flex;
flex-direction: column;
align-items: center;
}
}
1 change: 1 addition & 0 deletions src/web/assets/cp/src/css/craft.scss
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
@import 'main';
@import 'cp';
@import 'global-sidebar';
@import 'craft-spinner';
@import 'craft-tooltip';
@import 'preview';
@import 'login';
Expand Down
73 changes: 73 additions & 0 deletions src/web/assets/cp/src/js/CraftSpinner.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,73 @@
/**
*
* Spinner
*
*
@property {boolean} visible - Whether the spinner is initially visible
*/
const template = document.createElement('template');
template.innerHTML = `
<div class="wrapper hidden" style="" tabindex="-1">
<div class="spinner"></div>
<span class="message visually-hidden">${Craft.t('app', 'Loading')}</span>
</div>
`;

class CraftSpinner extends HTMLElement {
connectedCallback() {
this.root = this;
let clone = template.content.cloneNode(true);
this.root.append(clone);

if (this.visible === 'true') {
this.wrapper.classList.remove('hidden');
}

this.initialized = true;
}

static get observedAttributes() {
return ['visible'];
}

get visible() {
return this.getAttribute('visible');
}

set visible(value) {
this.setAttribute('visible', value);
}

get messageWrapper() {
return this.querySelector('.message');
}

get wrapper() {
return this.querySelector('.wrapper');
}

attributeChangedCallback(attrName, oldVal, newVal) {
if (!this.initialized) return;

if (attrName.toLowerCase() === 'visible') {
return newVal === 'true' ? this.show() : this.hide();
}
}
disconnectedCallback() {}

show() {
this.wrapper.classList.remove('hidden');
this.dispatchEvent(new CustomEvent('show'));
}

hide() {
this.wrapper.classList.add('hidden');
this.dispatchEvent(new CustomEvent('hide'));
}

focus() {
this.wrapper.focus();
}
}

customElements.define('craft-spinner', CraftSpinner);
70 changes: 40 additions & 30 deletions src/web/assets/cp/src/js/LoginForm.js
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ Craft.LoginForm = Garnish.Base.extend(
{
$container: null,
$form: null,
$spinner: null,
$usernameInput: null,
$passwordInput: null,
$rememberMeCheckbox: null,
Expand Down Expand Up @@ -46,6 +47,11 @@ Craft.LoginForm = Garnish.Base.extend(
changeButtonText: true,
});

this.$spinner = document.createElement('craft-spinner');
this.$spinner.setAttribute('visible', false);

$(this.$spinner).insertAfter(this.$form);

new Craft.PasswordInput(this.$passwordInput, {
onToggleInput: ($newPasswordInput) => {
this.removeListener(this.$passwordInput, 'input');
Expand Down Expand Up @@ -226,47 +232,51 @@ Craft.LoginForm = Garnish.Base.extend(
const $altContainer = $(
'<div class="login-alt-container"/>'
).insertAfter($hr);
const $button = Craft.ui
.createButton({
label: Craft.t('app', 'Try another way'),
spinner: true,
})
.addClass('menubtn')
.appendTo($altContainer);
const $menu = $('<div class="menu login-alt-menu"/>').appendTo(
$altContainer
);

const $menu = $(
'<div id="login-alt-menu" class="login-alt-menu menu menu--disclosure"/>'
).appendTo($altContainer);
const $ul = $('<ul/>').appendTo($menu);
for (let method of data.otherMethods) {
$('<li/>')
.append(
$('<a/>', {
$('<button/>', {
text: method.name,
'data-method': method.class,
class: 'menu-item',
})
)
.appendTo($ul);
}
new Garnish.MenuBtn($button, {
onOptionSelect: (option) => {
$button.addClass('loading');

Craft.sendActionRequest('post', 'users/auth-form', {
data: {
method: $(option).data('method'),
},

const $button = $('<button/>', {
type: 'button',
'aria-controls': 'login-alt-menu',
class: 'menu-toggle',
html: Craft.t('app', 'Try another way'),
}).appendTo($altContainer);

const $methodDisclosure = new Garnish.DisclosureMenu($button);

$ul.find('button').on('activate', (event) => {
this.$spinner.visible = true;
this.$spinner.focus();
$methodDisclosure.hide();
$authForm.remove();
$hr.remove();
$altContainer.remove();

Craft.sendActionRequest('post', 'users/auth-form', {
data: {
method: $(event.target).data('method'),
},
})
.then(({data}) => {
this.show2faForm(data);
})
.then(({data}) => {
$authForm.remove();
$hr.remove();
$altContainer.remove();
console.log(data);
this.show2faForm(data);
})
.finally(() => {
$button.removeClass('loading');
});
},
.finally(() => {
this.$spinner.visible = false;
});
});
}

Expand Down
2 changes: 1 addition & 1 deletion src/web/assets/pluginstore/dist/css/app.css

Large diffs are not rendered by default.

2 changes: 1 addition & 1 deletion src/web/assets/pluginstore/dist/css/app.css.map

Large diffs are not rendered by default.