Skip to content

Commit

Permalink
Initial work to add password visibility toggle button on Student Dash…
Browse files Browse the repository at this point in the history
…board login form (#2818)


---------

Co-authored-by: Kim Coleman <[email protected]>
  • Loading branch information
brianhogg and kimcoleman authored Jan 13, 2025
1 parent 1b9a28a commit aa3209d
Show file tree
Hide file tree
Showing 6 changed files with 204 additions and 60 deletions.
3 changes: 3 additions & 0 deletions .changelogs/password-toggle.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
significance: patch
type: added
entry: Ability to show typed password for verification.
76 changes: 76 additions & 0 deletions assets/js/app/llms-visibility-toggle.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,76 @@
/**
* Handle Password Visibility Toggle for LifterLMS Forms
*
* @package LifterLMS/Scripts
*
* @since TBD
*/

LLMS.PasswordVisibility = {

/**
* Initialize references and setup event binding
*
* @since TBD
* @return void
*/
init: function() {
this.$toggleButtons = $( '.llms-visibility-toggle button' );

if ( this.$toggleButtons.length ) {
this.$toggleButtons.removeClass( 'hide-if-no-js' );
this.bind();
}
},

/**
* Bind DOM events for toggle buttons
*
* @since TBD
* @return void
*/
bind: function() {
var self = this;

// Remove any previous click events and bind the new click event
this.$toggleButtons.off('click').on('click', function(event) {
self.toggleVisibility( $(this) );
});
},

/**
* Toggle visibility of password fields
*
* @since TBD
* @param {Object} $button The jQuery object of the clicked button
* @return void
*/
toggleVisibility: function( $button ) {
var isVisible = parseInt( $button.attr('data-toggle'), 10 );
var $form = $button.closest( '.llms-form-fields' );
var $passwordFields = $form.find( 'input.llms-field-input' );
var $icon = $button.find( 'i' );
var $stateText = $button.find( '.llms-visibility-toggle-state' );

// Toggle the visibility state
if ( isVisible === 1 ) {
// Show password
$passwordFields.filter('[type="password"]').attr('type', 'text');
$button.attr('data-toggle', 0);
$icon.removeClass('fa-eye').addClass('fa-eye-slash');
$stateText.text(LLMS.l10n.translate('Hide Password'));
} else {
// Hide password
$passwordFields.filter('[type="text"]').attr('type', 'password');
$button.attr('data-toggle', 1);
$icon.removeClass('fa-eye-slash').addClass('fa-eye');
$stateText.text(LLMS.l10n.translate('Show Password'));
}
}

};

// Initialize the Password Visibility module on document ready
jQuery(document).ready(function($) {
LLMS.PasswordVisibility.init();
});
25 changes: 25 additions & 0 deletions assets/scss/_includes/_buttons.scss
Original file line number Diff line number Diff line change
Expand Up @@ -165,6 +165,31 @@ a.llms-button-secondary {

}

.llms-button-plain {
background: transparent;
border: none;
border-radius: 3px;
color: #1D2327;
cursor: pointer;
font-size: 16px;
font-weight: 700;
text-decoration: none;
text-shadow: none;
line-height: 1;
margin: 0;
max-width: 100%;
padding: 1px 3px;
position: relative;

&:hover, &:active {
color: #1D2327;
}
&:focus {
color: #1D2327;
}

}

.llms-course-continue-button {
display: inline-block;
}
22 changes: 22 additions & 0 deletions assets/scss/_includes/_llms-form-field.scss
Original file line number Diff line number Diff line change
Expand Up @@ -215,6 +215,28 @@
height: 100%;
}

&:has(.llms-visibility-toggle) {
align-items: center;
display: grid;
grid-template-areas:
"label toggle"
"input input";
grid-template-columns: 1fr auto;

label {
grid-area: label;
}

input {
grid-area: input;
}

.llms-visibility-toggle {
grid-area: toggle;
justify-self: end;
}
}

}


Expand Down
30 changes: 16 additions & 14 deletions includes/class.llms.person.handler.php
Original file line number Diff line number Diff line change
Expand Up @@ -166,12 +166,13 @@ public static function get_login_fields( $layout = 'columns' ) {
'type' => ! $usernames ? 'email' : 'text',
),
array(
'columns' => ( 'columns' == $layout ) ? 6 : 12,
'id' => 'llms_password',
'label' => __( 'Password', 'lifterlms' ),
'last_column' => ( 'columns' == $layout ) ? true : true,
'required' => true,
'type' => 'password',
'columns' => ( 'columns' == $layout ) ? 6 : 12,
'id' => 'llms_password',
'label' => __( 'Password', 'lifterlms' ),
'last_column' => ( 'columns' == $layout ) ? true : true,
'required' => true,
'type' => 'password',
'visibility_toggle' => true,
),
array(
'columns' => ( 'columns' == $layout ) ? 3 : 12,
Expand Down Expand Up @@ -280,14 +281,15 @@ private static function get_password_fields() {
$fields = array();

$fields[] = array(
'columns' => 6,
'classes' => 'llms-password',
'id' => 'password',
'label' => __( 'Password', 'lifterlms' ),
'last_column' => false,
'match' => 'password_confirm',
'required' => true,
'type' => 'password',
'columns' => 6,
'classes' => 'llms-password',
'id' => 'password',
'label' => __( 'Password', 'lifterlms' ),
'last_column' => false,
'match' => 'password_confirm',
'required' => true,
'type' => 'password',
'visibility_toggle' => true,
);
$fields[] = array(
'columns' => 6,
Expand Down
108 changes: 62 additions & 46 deletions includes/forms/class-llms-form-field.php
Original file line number Diff line number Diff line change
Expand Up @@ -23,29 +23,30 @@ class LLMS_Form_Field {
* @var array {
* Array of field settings.
*
* @type array $attributes Associative array of HTML attributes to add to the field element.
* @type bool $checked Determines if radio and checkbox fields are checked.
* @type int $columns Number of columns the field wrapper should occupy when rendered. Accepts integers >= 1 and <= 12.
* @type string[]|string $classes Additional CSS classes to add to the field element. Accepts a string or an array of strings.
* @type string $data_store Determines where to store field values. Accepts "users" or "usermeta" to store on the respective WP core tables.
* @type string|false $data_store_key Determines the key name to use when storing the field value. Pass `false` to disable automatic storage. Defaults to the value of the `$name` property.
* @type string $description A string to use as the field's description or helper text.
* @type string $default The default value to use for the field.
* @type bool $disabled Whether or not the field is enabled.
* @type string $id The field's HTML "id" attribute. Must be unique. If not supplied, an ID is automatically generated.
* @type string $label Text to use in the label element associated with the field.
* @type bool $label_show_empty When true and no `$label` is supplied, will show an empty label element.
* @type bool $last_column When true, outputs a clearfix element following the element's wrapper. Allows ending a "row" of fields.
* @type bool $match Match this field to another field for validation purposes. Must be the `$id` of another field in the form.
* @type string $name The field's HTML "name" attribute. Default's to the value of `$id` when not supplied.
* @type array $options An associative array of options used for select, checkbox groups, and radio fields.
* @type string $options_preset A string representing a pre-defined set of `$options`. Accepts "countries" or "states". Custom presets can be defined using the filter "llms_form_field_options_preset_{$preset_id}".
* @type string $placeholder The field's HTML placeholder attribute.
* @type bool $required Determines if the field is marked as required.
* @type string $selected Alias of `$default`.
* @type string $type Field type. Accepts any HTML5 input type (text, email, tel, etc...), radio, checkbox, select, textarea, button, reset, submit, and html.
* @type string $value Value of the field.
* @type string[]|string $wrapper_classes Additional CSS classes to add to the field's wrapper element. Accepts a string or an array of strings.
* @type array $attributes Associative array of HTML attributes to add to the field element.
* @type bool $checked Determines if radio and checkbox fields are checked.
* @type int $columns Number of columns the field wrapper should occupy when rendered. Accepts integers >= 1 and <= 12.
* @type string[]|string $classes Additional CSS classes to add to the field element. Accepts a string or an array of strings.
* @type string $data_store Determines where to store field values. Accepts "users" or "usermeta" to store on the respective WP core tables.
* @type string|false $data_store_key Determines the key name to use when storing the field value. Pass `false` to disable automatic storage. Defaults to the value of the `$name` property.
* @type string $description A string to use as the field's description or helper text.
* @type string $default The default value to use for the field.
* @type bool $disabled Whether or not the field is enabled.
* @type string $id The field's HTML "id" attribute. Must be unique. If not supplied, an ID is automatically generated.
* @type string $label Text to use in the label element associated with the field.
* @type bool $label_show_empty When true and no `$label` is supplied, will show an empty label element.
* @type bool $last_column When true, outputs a clearfix element following the element's wrapper. Allows ending a "row" of fields.
* @type bool $match Match this field to another field for validation purposes. Must be the `$id` of another field in the form.
* @type string $name The field's HTML "name" attribute. Default's to the value of `$id` when not supplied.
* @type array $options An associative array of options used for select, checkbox groups, and radio fields.
* @type string $options_preset A string representing a pre-defined set of `$options`. Accepts "countries" or "states". Custom presets can be defined using the filter "llms_form_field_options_preset_{$preset_id}".
* @type string $placeholder The field's HTML placeholder attribute.
* @type bool $required Determines if the field is marked as required.
* @type string $selected Alias of `$default`.
* @type string $type Field type. Accepts any HTML5 input type (text, email, tel, etc...), radio, checkbox, select, textarea, button, reset, submit, and html.
* @type string $value Value of the field.
* @type string $visibility_toggle Determines if the field should show a button to toggle field masking (for password fields).
* @type string[]|string $wrapper_classes Additional CSS classes to add to the field's wrapper element. Accepts a string or an array of strings.
* }
*/
protected $settings = array();
Expand Down Expand Up @@ -202,29 +203,30 @@ public function explode_options_to_fields( $is_hidden = false ) {
protected function get_defaults() {

return array(
'attributes' => array(),
'checked' => false,
'columns' => 12,
'classes' => array(), // Or string of space-separated classes.
'data_store' => 'usermeta', // Users or usermeta.
'data_store_key' => '', // Defaults to value passed for "name".
'description' => '',
'default' => '',
'disabled' => false,
'id' => '',
'label' => '',
'label_show_empty' => false,
'last_column' => true,
'match' => '', // Test.
'name' => '', // Defaults to value passed for "id".
'options' => array(),
'options_preset' => '',
'placeholder' => '',
'required' => false,
'selected' => '', // Alias of "default".
'type' => 'text',
'value' => '',
'wrapper_classes' => array(), // Or string of space-separated classes.
'attributes' => array(),
'checked' => false,
'columns' => 12,
'classes' => array(), // Or string of space-separated classes.
'data_store' => 'usermeta', // Users or usermeta.
'data_store_key' => '', // Defaults to value passed for "name".
'description' => '',
'default' => '',
'disabled' => false,
'id' => '',
'label' => '',
'label_show_empty' => false,
'last_column' => true,
'match' => '', // Test.
'name' => '', // Defaults to value passed for "id".
'options' => array(),
'options_preset' => '',
'placeholder' => '',
'required' => false,
'selected' => '', // Alias of "default".
'type' => 'text',
'value' => '',
'visibility_toggle' => false,
'wrapper_classes' => array(), // Or string of space-separated classes.
);
}

Expand Down Expand Up @@ -262,6 +264,18 @@ protected function get_description_html() {
return $this->settings['description'] ? sprintf( '<span class="llms-description">%s</span>', $this->settings['description'] ) : '';
}

/**
* Retrieve HTML for the visibility toggle button
*
* @since TBD
*
* @return string
*/
protected function get_visibility_toggle_html() {

return $this->settings['visibility_toggle'] ? '<div class="llms-visibility-toggle"><button type="button" class="llms-button-plain hide-if-no-js" data-toggle="1"><i class="fa fa-eye"></i> <span class="llms-visibility-toggle-state">' . esc_html__( 'Show Password', 'lifterlms' ) . '</span></button></div>' : '';
}

/**
* Retrieve the full HTML for the field.
*
Expand Down Expand Up @@ -457,6 +471,8 @@ public function get_html() {
$after .= $this->get_description_html();
}

$after .= $this->get_visibility_toggle_html();

$after .= '</div>';

if ( $this->settings['last_column'] ) {
Expand Down

0 comments on commit aa3209d

Please sign in to comment.