custom_profile_fields: Add "Alphabetize choices" button.

This commit adds a "Alphabetize choices" button for SELECT also known as
"List of options" type custom profile fields. The
options themselves can get out of order from either user text input, or
from list modifications like addition or drag.

We follow case-insensitive alphabetization such that "vim" comes before
"VS Code". This is similar to how we alphabetize elsewhere like stream
lists.

Fixes: #28607.

Co-authored-by: Tanmay Kumar <tnmdotkr@gmail.com>
This commit is contained in:
Maneesh Shukla
2024-12-11 04:12:30 +05:30
committed by Tim Abbott
parent bb8bda0bf7
commit 015bd7e6c9
4 changed files with 52 additions and 2 deletions

View File

@@ -406,6 +406,36 @@ function disable_submit_button_if_no_property_changed(
);
}
function alphabetize_profile_field_choices($sortable_element: JQuery): void {
assert($sortable_element[0] !== undefined);
const sortable_instance = SortableJS.get($sortable_element[0]);
assert(sortable_instance !== undefined);
const choices_array: [string, string][] = [];
const empty_choices_array: [string, string][] = [];
const choices_id_array = sortable_instance.toArray();
for (const choice_id of choices_id_array) {
const choice_value = $(sortable_instance.el)
.find<HTMLInputElement>(`div[data-value="${choice_id}"] input`)
.val()!;
// Remove empty choices from the array that we will sort. After sorting, we append these
// to the sorted array.;
if (choice_value.length === 0) {
empty_choices_array.push(["", choice_id]);
continue;
}
choices_array.push([choice_value, choice_id]);
}
choices_array.sort((a, b) => util.strcmp(a[0], b[0]));
choices_array.push(...empty_choices_array);
sortable_instance.sort(choices_array.map((v) => v[1]));
}
function set_up_select_field_edit_form(
$profile_field_form: JQuery,
field: CustomProfileField,
@@ -437,6 +467,7 @@ function set_up_select_field_edit_form(
},
filter: "input",
preventOnFilter: false,
dataIdAttr: "data-value",
onSort() {
disable_submit_button_if_no_property_changed($profile_field_form, field);
},
@@ -521,6 +552,14 @@ function open_edit_form_modal(this: HTMLElement): void {
delete_choice_row_for_edit(this, $profile_field_form, field);
},
);
$profile_field_form.on(
"click",
".profile-field-choices-wrapper > button.alphabetize-choices-button",
() => {
alphabetize_profile_field_choices($edit_profile_field_choices_container);
disable_submit_button_if_no_property_changed($profile_field_form, field);
},
);
$("#edit-custom-profile-field-form-modal .dialog_submit_button").prop("disabled", true);
// Setup onInput event listeners to disable/enable submit button,
@@ -756,6 +795,7 @@ function set_up_select_field(): void {
},
filter: "input",
preventOnFilter: false,
dataIdAttr: "data-value",
});
}
@@ -782,6 +822,9 @@ function set_up_select_field(): void {
$profile_field_choices.on("click", "button.delete-choice", function (this: HTMLElement) {
delete_choice_row(this);
});
$("#profile_field_choices_row").on("click", "button.alphabetize-choices-button", () => {
alphabetize_profile_field_choices($profile_field_choices);
});
}
function set_up_external_account_field(): void {

View File

@@ -1721,6 +1721,11 @@ label.preferences-radio-choice-label {
}
}
.profile-field-choices-wrapper > .alphabetize-choices-button {
display: block;
margin: 12px 0 0;
}
.custom_user_field,
.bot_owner_user_field {
.pill-container {

View File

@@ -26,11 +26,12 @@
<input type="text" id="profile_field_hint" class="modal_text_input" name="hint" autocomplete="off" maxlength="80" />
<div class="alert" id="admin-profile-field-hint-status"></div>
</div>
<div class="input-group" id="profile_field_choices_row">
<div class="input-group profile-field-choices-wrapper" id="profile_field_choices_row">
<label for="profile_field_choices" class="modal-field-label">{{t "Field choices" }}</label>
<table class="profile_field_choices_table">
<tbody id="profile_field_choices" class="profile-field-choices"></tbody>
</table>
<button type="button" class="button white rounded alphabetize-choices-button">{{t "Alphabetize choices" }}</button>
</div>
<div class="input-group" id="custom_external_account_url_pattern">
<label for="custom_field_url_pattern" class="modal-field-label">{{t "URL pattern" }}</label>

View File

@@ -9,7 +9,7 @@
<input type="text" name="hint" id="id-custom-profile-field-hint" class="modal_text_input prop-element" value="{{ hint }}" maxlength="80" data-setting-widget-type="string" />
</div>
{{#if is_select_field }}
<div class="input-group prop-element" id="id-custom-profile-field-field-data" data-setting-widget-type="field-data-setting">
<div class="input-group prop-element profile-field-choices-wrapper" id="id-custom-profile-field-field-data" data-setting-widget-type="field-data-setting">
<label for="profile_field_choices_edit" class="modal-field-label">{{t "Field choices" }}</label>
<div class="profile-field-choices" name="profile_field_choices_edit">
<div class="edit_profile_field_choices_container">
@@ -18,6 +18,7 @@
{{/each}}
</div>
</div>
<button type="button" class="button white rounded alphabetize-choices-button">{{t "Alphabetize choices" }}</button>
</div>
{{else if is_external_account_field}}
<div class="prop-element" id="id-custom-profile-field-field-data" data-setting-widget-type="field-data-setting">