[Small Wins] Show character limits for profile inputs (#15255)

* Start implementation

* Extend raw implementation; will refactor later

* still building

* basic implementation

* create JS pack

* Rename JS pack; use existing CSS property

* complete implementation for profile fields

* Add guard againt irrelevant profile fields

* extract view logic to helper and add specs
This commit is contained in:
Arit Amana 2021-11-05 09:11:42 -04:00 committed by GitHub
parent 2f42bc46d4
commit 4f1bdcda22
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
4 changed files with 116 additions and 8 deletions

View file

@ -15,4 +15,13 @@ module ProfileHelper
{ github: urls["GitHub"], twitter: urls["Twitter"], facebook: urls["Facebook"] }.compact
end
def character_count_denominator(field_type)
case field_type
when "text_field"
"100"
when "text_area"
"200"
end
end
end

View file

@ -0,0 +1,32 @@
const userFieldIds = ['user[name]', 'user[email]', 'user[username]'];
const profileFieldIds = Array.from(
document.querySelectorAll('[id^="profile["]'),
).map((node) => node.id);
const allFieldIds = [...userFieldIds, ...profileFieldIds];
export function fieldCharacterLimits() {
window.addEventListener('load', () => {
allFieldIds.forEach((field_id) => {
const field = document.getElementById(field_id);
const fieldValueLength = field.value.length;
const fieldCharacterSpan = document.getElementById(
field.dataset.characterSpanId,
);
fieldCharacterSpan.innerHTML = fieldValueLength;
});
document
.getElementById('user-profile-form')
.addEventListener('keyup', (event) => {
if (!event.target.dataset.characterSpanId) {
return;
}
document.getElementById(
event.target.dataset.characterSpanId,
).innerHTML = event.target.value.length;
});
});
}
fieldCharacterLimits();

View file

@ -1,20 +1,40 @@
<%= javascript_packs_with_chunks_tag "colorPreview", "validateFileInputs", "stickySaveFooter", defer: true %>
<%= javascript_packs_with_chunks_tag "colorPreview", "stickySaveFooter", "userProfileSettings", "validateFileInputs", defer: true %>
<%= render "users/additional_authentication" %>
<%= form_with(url: profile_path(@user.profile_id), method: "put", html: { class: "sticky-footer-form" }) do |f| %>
<%= form_with(url: profile_path(@user.profile_id), method: "put", html: { class: "sticky-footer-form", id: "user-profile-form" }) do |f| %>
<div class="crayons-card crayons-card--content-rows">
<h2>User</h2>
<div class="crayons-field">
<%= f.label :name, for: "user[name]", class: "crayons-field__label" %>
<%= f.text_field "user[name]", maxlength: 30, class: "crayons-textfield", placeholder: "John Doe", value: @user.name %>
<%= f.text_field "user[name]",
maxlength: 30,
class: "crayons-textfield",
placeholder: "John Doe",
value: @user.name,
aria: { describedby: "name-description" },
data: { character_span_id: "name-characters" } %>
<div id="name-description" class="self-end fs-s">
<span class="screen-reader-only">Available characters used: </span>
<span id="name-characters"></span>/30
</div>
</div>
<div class="crayons-field">
<%= f.label :email, for: "user[email]", class: "crayons-field__label" %>
<%= f.text_field "user[email]", class: "crayons-textfield", placeholder: "john.doe@example.com", value: @user.email %>
<%= f.text_field "user[email]",
maxlength: 50,
class: "crayons-textfield",
placeholder: "john.doe@example.com",
value: @user.email,
aria: { describedby: "email-description" },
data: { character_span_id: "email-characters" } %>
<div id="email-description" class="self-end fs-s">
<span class="screen-reader-only">Available characters used: </span>
<span id="email-characters"></span>/50
</div>
</div>
<div class="crayons-field crayons-field--checkbox">
@ -24,7 +44,17 @@
<div class="crayons-field">
<%= f.label :username, for: "user[username]", class: "crayons-field__label" %>
<%= f.text_field "user[username]", maxlength: 30, class: "crayons-textfield", placeholder: "johndoe", value: @user.username %>
<%= f.text_field "user[username]",
maxlength: 30,
class: "crayons-textfield",
placeholder: "johndoe",
value: @user.username,
aria: { describedby: "username-description" },
data: { character_span_id: "username-characters" } %>
<div id="username-description" class="self-end fs-s">
<span class="screen-reader-only">Available characters used: </span>
<span id="username-characters"></span>/30
</div>
</div>
<div class="crayons-field">
@ -51,9 +81,16 @@
<%= t("views.users.profile_fields.website.label") %>
</label>
<%= f.url_field "profile[website_url]",
maxlength: 100,
value: profile.website_url,
placeholder: "https://yoursite.com",
class: "crayons-textfield js-color-field" %>
class: "crayons-textfield",
aria: { describedby: "url-description" },
data: { character_span_id: "url-characters" } %>
<div id="url-description" class="self-end fs-s">
<span class="screen-reader-only">Available characters used: </span>
<span id="url-characters"></span>/100
</div>
</div>
<div class="crayons-field">
@ -61,9 +98,16 @@
<%= t("views.users.profile_fields.location.label") %>
</label>
<%= f.text_field "profile[location]",
maxlength: 100,
value: profile.location,
placeholder: t("views.users.profile_fields.location.placeholder"),
class: "crayons-textfield js-color-field" %>
class: "crayons-textfield",
aria: { describedby: "location-description" },
data: { character_span_id: "location-characters" } %>
<div id="location-description" class="self-end fs-s">
<span class="screen-reader-only">Available characters used: </span>
<span id="location-characters"></span>/100
</div>
</div>
<div class="crayons-field">
@ -71,9 +115,16 @@
<%= t("views.users.profile_fields.bio.label") %>
</label>
<%= f.text_area "profile[summary]",
maxlength: 200,
value: profile.summary,
placeholder: t("views.users.profile_fields.bio.placeholder"),
class: "crayons-textfield js-color-field" %>
class: "crayons-textfield",
aria: { describedby: "summary-description" },
data: { character_span_id: "summary-characters" } %>
<div id="summary-description" class="self-end fs-s">
<span class="screen-reader-only">Available characters used: </span>
<span id="summary-characters"></span>/200
</div>
</div>
</div>
@ -103,7 +154,13 @@
"profile[#{field.attribute_name}]",
value: profile.public_send(field.attribute_name),
placeholder: field["placeholder_text"],
aria: { describedby: "#{field.attribute_name}-description" },
data: { character_span_id: "#{field.attribute_name}-characters" },
class: "crayons-textfield") %>
<div id="<%= field.attribute_name %>-description" class="self-end fs-s">
<span class="screen-reader-only">Available characters used: </span>
<span id="<%= field.attribute_name %>-characters"></span>/<%= character_count_denominator(field["input_type"]) %>
</div>
<% end %>
<% if field.description.present? %>
<p class="crayons-field__description"><%= field.description %></p>

View file

@ -68,4 +68,14 @@ describe ProfileHelper do
end
end
end
describe "character_count_denominator" do
it "returns '100' when the field_type is 'text_field'" do
expect(helper.character_count_denominator("text_field")).to eq("100")
end
it "returns '200' when the field_type is 'text_area'" do
expect(helper.character_count_denominator("text_area")).to eq("200")
end
end
end