diff --git a/frontend_tests/node_tests/lib/events.js b/frontend_tests/node_tests/lib/events.js index fe523d8f22..719c975356 100644 --- a/frontend_tests/node_tests/lib/events.js +++ b/frontend_tests/node_tests/lib/events.js @@ -128,8 +128,24 @@ exports.fixtures = { custom_profile_fields: { type: "custom_profile_fields", fields: [ - {id: 1, name: "teams", type: 1, hint: "", field_data: "", order: 1}, - {id: 2, name: "hobbies", type: 1, hint: "", field_data: "", order: 2}, + { + id: 1, + name: "teams", + type: 1, + hint: "", + field_data: "", + order: 1, + display_in_profile_summary: false, + }, + { + id: 2, + name: "hobbies", + type: 1, + hint: "", + field_data: "", + order: 2, + display_in_profile_summary: false, + }, ], }, diff --git a/frontend_tests/node_tests/settings_profile_fields.js b/frontend_tests/node_tests/settings_profile_fields.js index f897959084..3a0127a3fd 100644 --- a/frontend_tests/node_tests/settings_profile_fields.js +++ b/frontend_tests/node_tests/settings_profile_fields.js @@ -13,10 +13,14 @@ const SHORT_TEXT_ID = 1; const SELECT_ID = 3; const EXTERNAL_ACCOUNT_ID = 7; +const LONG_TEXT_ID = 2; +const USER_FIELD_ID = 6; const SHORT_TEXT_NAME = "Short text"; const SELECT_NAME = "Select"; const EXTERNAL_ACCOUNT_NAME = "External account"; +const LONG_TEXT_NAME = "Long text"; +const USER_FIELD_NAME = "Person"; const custom_profile_field_types = { SHORT_TEXT: { @@ -31,6 +35,14 @@ const custom_profile_field_types = { id: EXTERNAL_ACCOUNT_ID, name: EXTERNAL_ACCOUNT_NAME, }, + LONG_TEXT: { + id: LONG_TEXT_ID, + name: LONG_TEXT_NAME, + }, + USER: { + id: USER_FIELD_ID, + name: USER_FIELD_NAME, + }, }; page_params.custom_profile_field_types = custom_profile_field_types; @@ -71,6 +83,8 @@ run_test("populate_profile_fields", ({mock_template}) => { page_params.custom_profile_fields = {}; page_params.realm_default_external_accounts = JSON.stringify({}); + $("#admin_profile_fields_table .display_in_profile_summary_false").toggleClass = () => {}; + const template_data = []; mock_template("settings/admin_profile_field_list.hbs", false, (data) => { template_data.push(data); @@ -84,6 +98,8 @@ run_test("populate_profile_fields", ({mock_template}) => { name: "favorite color", hint: "blue?", field_data: "", + display_in_profile_summary: false, + valid_to_display_in_summary: true, }, { type: SELECT_ID, @@ -100,6 +116,8 @@ run_test("populate_profile_fields", ({mock_template}) => { order: 1, }, ]), + display_in_profile_summary: false, + valid_to_display_in_summary: true, }, { type: EXTERNAL_ACCOUNT_ID, @@ -109,6 +127,8 @@ run_test("populate_profile_fields", ({mock_template}) => { field_data: JSON.stringify({ subtype: "github", }), + display_in_profile_summary: true, + valid_to_display_in_summary: true, }, { type: EXTERNAL_ACCOUNT_ID, @@ -119,6 +139,8 @@ run_test("populate_profile_fields", ({mock_template}) => { subtype: "custom", url_pattern: "https://chat.zulip.com/%(username)s", }), + display_in_profile_summary: true, + valid_to_display_in_summary: true, }, ]; const expected_template_data = [ @@ -131,6 +153,8 @@ run_test("populate_profile_fields", ({mock_template}) => { choices: [], is_select_field: false, is_external_account_field: false, + display_in_profile_summary: false, + valid_to_display_in_summary: true, }, can_modify: true, realm_default_external_accounts: page_params.realm_default_external_accounts, @@ -147,6 +171,8 @@ run_test("populate_profile_fields", ({mock_template}) => { ], is_select_field: true, is_external_account_field: false, + display_in_profile_summary: false, + valid_to_display_in_summary: true, }, can_modify: true, realm_default_external_accounts: page_params.realm_default_external_accounts, @@ -160,6 +186,8 @@ run_test("populate_profile_fields", ({mock_template}) => { choices: [], is_select_field: false, is_external_account_field: true, + display_in_profile_summary: true, + valid_to_display_in_summary: true, }, can_modify: true, realm_default_external_accounts: page_params.realm_default_external_accounts, @@ -173,6 +201,8 @@ run_test("populate_profile_fields", ({mock_template}) => { choices: [], is_select_field: false, is_external_account_field: true, + display_in_profile_summary: true, + valid_to_display_in_summary: true, }, can_modify: true, realm_default_external_accounts: page_params.realm_default_external_accounts, diff --git a/static/js/settings_profile_fields.js b/static/js/settings_profile_fields.js index b2d49db4de..1116766282 100644 --- a/static/js/settings_profile_fields.js +++ b/static/js/settings_profile_fields.js @@ -39,6 +39,7 @@ export function maybe_disable_widgets() { .prop("disabled", true); } +let display_in_profile_summary_fields_limit_reached = false; let order = []; const field_types = page_params.custom_profile_field_types; @@ -58,6 +59,14 @@ export function field_type_id_to_string(type_id) { return undefined; } +// Checking custom profile field type is valid for showing display in profile summary checkbox field. +function is_valid_to_display_in_summary(field_type) { + if (field_type === field_types.LONG_TEXT.id || field_type === field_types.USER.id) { + return false; + } + return true; +} + function update_profile_fields_table_element() { const $profile_fields_table = $("#admin_profile_fields_table").expectOne(); @@ -206,8 +215,9 @@ function set_up_create_field_form() { $("#dialog_error").hide(); const $field_elem = $("#profile_field_external_accounts"); const $field_url_pattern_elem = $("#custom_external_account_url_pattern"); + const profile_field_type = Number.parseInt($("#profile_field_type").val(), 10); - if (Number.parseInt($("#profile_field_type").val(), 10) === field_types.EXTERNAL_ACCOUNT.id) { + if (profile_field_type === field_types.EXTERNAL_ACCOUNT.id) { $field_elem.show(); const $profile_field_external_account_type = $( "#profile_field_external_accounts_type", @@ -230,6 +240,13 @@ function set_up_create_field_form() { $field_url_pattern_elem.hide(); $field_elem.hide(); } + + // Not showing "display in profile summary" option for long text/user profile field. + if (is_valid_to_display_in_summary(profile_field_type)) { + $("#profile_field_display_in_profile_summary").closest(".input-group").show(); + } else { + $("#profile_field_display_in_profile_summary").closest(".input-group").hide(); + } } function read_field_data_from_form(field_type_id, $profile_field_form, old_field_data) { @@ -261,6 +278,9 @@ function open_custom_profile_field_form_modal() { hint: $("#profile_field_hint").val(), field_type, field_data: JSON.stringify(field_data), + display_in_profile_summary: $("#profile_field_display_in_profile_summary").is( + ":checked", + ), }; const url = "/json/realm/profile_fields"; const opts = { @@ -275,6 +295,22 @@ function open_custom_profile_field_form_modal() { set_up_select_field(); set_up_external_account_field(); clear_form_data(); + + // If we already have 2 custom profile fields configured to be + // displayed in the user profile summary, disable the input to + // change it. + $("#add-new-custom-profile-field-form #profile_field_display_in_profile_summary").prop( + "disabled", + display_in_profile_summary_fields_limit_reached, + ); + $("#add-new-custom-profile-field-form .profile_field_display_label").toggleClass( + "disabled_label", + display_in_profile_summary_fields_limit_reached, + ); + $("#add-new-custom-profile-field-form .checkbox").toggleClass( + "display_in_profile_summary_tooltip", + display_in_profile_summary_fields_limit_reached, + ); } dialog_widget.launch({ @@ -409,8 +445,10 @@ function open_edit_form_modal(e) { name: field.name, hint: field.hint, choices, + display_in_profile_summary: field.display_in_profile_summary === true, is_select_field: field.type === field_types.SELECT.id, is_external_account_field: field.type === field_types.EXTERNAL_ACCOUNT.id, + valid_to_display_in_summary: is_valid_to_display_in_summary(field.type), }, realm_default_external_accounts: page_params.realm_default_external_accounts, }); @@ -418,6 +456,17 @@ function open_edit_form_modal(e) { function set_initial_values_of_profile_field() { const $profile_field_form = $("#edit-custom-profile-field-form-" + field_id); + // If its passes or equals the max limit we are disabling option for display custom profile field + // in user profile summary and adding tooptip except already checked field. + if (display_in_profile_summary_fields_limit_reached && !field.display_in_profile_summary) { + $profile_field_form + .find("input[name=display_in_profile_summary]") + .prop("disabled", true); + $profile_field_form + .find(".checkbox") + .addClass("display_in_profile_summary_tooltip disabled_label"); + } + if (Number.parseInt(field.type, 10) === field_types.SELECT.id) { set_up_select_field_edit_form($profile_field_form, field_data); } @@ -450,6 +499,9 @@ function open_edit_form_modal(e) { data.name = $profile_field_form.find("input[name=name]").val(); data.hint = $profile_field_form.find("input[name=hint]").val(); + data.display_in_profile_summary = $profile_field_form + .find("input[name=display_in_profile_summary]") + .is(":checked"); const new_field_data = read_field_data_from_form( Number.parseInt(field.type, 10), @@ -493,12 +545,52 @@ function open_edit_form_modal(e) { form_id: edit_custom_profile_field_form_id, html_heading: $t_html({defaultMessage: "Edit custom profile field"}), html_body, + id: "edit-custom-profile-field-form-modal", on_click: submit_form, post_render: set_initial_values_of_profile_field, loading_spinner: true, }); } +// If passes or equals the max limit, we are disabling option for +// display custom profile field in user profile summary and adding tooltip. +function update_profile_fields_checkboxes() { + // Disabling only uncheck checkboxes in table, so user should able uncheck checked checkboxes. + $("#admin_profile_fields_table .display_in_profile_summary_checkbox_false").prop( + "disabled", + display_in_profile_summary_fields_limit_reached, + ); + $("#admin_profile_fields_table .display_in_profile_summary_false").toggleClass( + "display_in_profile_summary_tooltip", + display_in_profile_summary_fields_limit_reached, + ); +} + +function toggle_display_in_profile_summary_profile_field(e) { + const field_id = Number.parseInt($(e.currentTarget).attr("data-profile-field-id"), 10); + const field = get_profile_field(field_id); + + let field_data; + if (field.field_data) { + field_data = field.field_data; + } + + const data = { + name: field.name, + hint: field.hint, + field_data, + display_in_profile_summary: !field.display_in_profile_summary, + }; + const $profile_field_status = $("#admin-profile-field-status").expectOne(); + + settings_ui.do_settings_change( + channel.patch, + "/json/realm/profile_fields/" + field_id, + data, + $profile_field_status, + ); +} + export function reset() { meta.loaded = false; } @@ -533,6 +625,7 @@ export function do_populate_profile_fields(profile_fields_data) { $profile_fields_table.find("tr.profile-field-form").remove(); // Clear all rows. order = []; + let display_in_profile_summary_fields_count = 0; for (const profile_field of profile_fields_data) { order.push(profile_field.id); let field_data = {}; @@ -544,6 +637,7 @@ export function do_populate_profile_fields(profile_fields_data) { choices = parse_field_choices_from_field_data(field_data); } + const display_in_profile_summary = profile_field.display_in_profile_summary === true; $profile_fields_table.append( render_admin_profile_field_list({ profile_field: { @@ -555,13 +649,23 @@ export function do_populate_profile_fields(profile_fields_data) { is_select_field: profile_field.type === field_types.SELECT.id, is_external_account_field: profile_field.type === field_types.EXTERNAL_ACCOUNT.id, + display_in_profile_summary, + valid_to_display_in_summary: is_valid_to_display_in_summary(profile_field.type), }, can_modify: page_params.is_admin, realm_default_external_accounts: page_params.realm_default_external_accounts, }), ); + + // Keeping counts of all display_in_profile_summary profile fields, to keep track. + if (display_in_profile_summary) { + display_in_profile_summary_fields_count += 1; + } } + // Update whether we're at the limit for display_in_profile_summary. + display_in_profile_summary_fields_limit_reached = display_in_profile_summary_fields_count >= 2; + if (page_params.is_admin) { const field_list = $("#admin_profile_fields_table")[0]; Sortable.create(field_list, { @@ -571,6 +675,7 @@ export function do_populate_profile_fields(profile_fields_data) { }); } + update_profile_fields_checkboxes(); update_profile_fields_table_element(); loading.destroy_indicator($("#admin_page_profile_fields_loading_indicator")); } @@ -647,4 +752,9 @@ export function build_page() { $("#admin_profile_fields_table").on("click", ".delete", delete_profile_field); $("#add-custom-profile-field-btn").on("click", open_custom_profile_field_form_modal); $("#admin_profile_fields_table").on("click", ".open-edit-form-modal", open_edit_form_modal); + $("#admin_profile_fields_table").on( + "click", + ".display_in_profile_summary", + toggle_display_in_profile_summary_profile_field, + ); } diff --git a/static/js/tippyjs.js b/static/js/tippyjs.js index 03fb828db8..8457abda97 100644 --- a/static/js/tippyjs.js +++ b/static/js/tippyjs.js @@ -367,4 +367,26 @@ export function initialize() { // Avoid inheriting `position: relative` CSS on the stream sorter widget. appendTo: () => document.body, }); + + delegate("body", { + // This tooltip appears on the "Summary" checkboxes in + // settings > custom profile fields, when at the limit of 2 + // fields with display_in_profile_summary enabled. + target: [ + "#profile-field-settings .display_in_profile_summary_tooltip", + "#edit-custom-profile-field-form-modal .display_in_profile_summary_tooltip", + "#add-new-custom-profile-field-form .display_in_profile_summary_tooltip", + ], + content: $t({ + defaultMessage: "Only 2 custom profile fields can be displayed in the profile summary.", + }), + appendTo: () => document.body, + onTrigger(instance) { + // Sometimes just removing class is not enough to destroy/remove tooltip, especially in + // "Add a new custom profile field" form, so here we are manually calling `destroy()`. + if (!instance.reference.classList.contains("display_in_profile_summary_tooltip")) { + instance.destroy(); + } + }, + }); } diff --git a/static/styles/settings.css b/static/styles/settings.css index b351b21a90..eea54f351d 100644 --- a/static/styles/settings.css +++ b/static/styles/settings.css @@ -213,8 +213,9 @@ h3, width: 20%; } - .admin-table-wrapper table.admin_profile_fields_table tr td { - width: 28%; + /* Limit the actions column to not using excessive width */ + .admin-table-wrapper table.admin_profile_fields_table tr .actions { + width: 11%; } } @@ -640,6 +641,10 @@ input[type="checkbox"] { margin-left: calc(10em + 20px) !important; } margin-bottom: 15px; + + .checkbox { + margin-top: 5px; + } } .grey-box .wrapper { @@ -1870,3 +1875,11 @@ $option_title_width: 180px; margin-top: 12px; } } + +#add-new-custom-profile-field-form, +#edit-custom-profile-field-form-modal { + .disabled_label { + cursor: default; + opacity: 0.7; + } +} diff --git a/static/templates/settings/add_new_custom_profile_field_form.hbs b/static/templates/settings/add_new_custom_profile_field_form.hbs index 918e907a45..9b2442f8bc 100644 --- a/static/templates/settings/add_new_custom_profile_field_form.hbs +++ b/static/templates/settings/add_new_custom_profile_field_form.hbs @@ -36,5 +36,12 @@ +
+ +
diff --git a/static/templates/settings/admin_profile_field_list.hbs b/static/templates/settings/admin_profile_field_list.hbs index bb2a65a57c..4647c2086a 100644 --- a/static/templates/settings/admin_profile_field_list.hbs +++ b/static/templates/settings/admin_profile_field_list.hbs @@ -13,6 +13,16 @@ {{type}} + + {{#if valid_to_display_in_summary}} + + + + {{/if}} + {{#if ../can_modify}}