← Back to team overview

launchpad-reviewers team mailing list archive

[Merge] lp:~danilo/launchpad/refactor-overlay-setup into lp:launchpad

 

Данило Шеган has proposed merging lp:~danilo/launchpad/refactor-overlay-setup into lp:launchpad.

Requested reviews:
  Launchpad code reviewers (launchpad-reviewers)

For more details, see:
https://code.launchpad.net/~danilo/launchpad/refactor-overlay-setup/+merge/56146

= Refactor overlay setup =

At the moment, overlay setup is too ingrained in what the overlays do and you can't easily construct overlays with different purposes on the same page (eg. one for editing a subscription and another for adding a new subscription).

== Proposed fix ==

Decouple setup from page loading: mostly moves code around and does away with one very nasty hack for passing stuff around.

Will fix lint after the review (unless I introduced the problems).

== Tests ==

lib/lp/registry/javascript/tests/test_structural_subscription.html

== Demo and Q/A ==

Nothing breaks on:
  https://launchpad.dev/firefox/+subscriptions
  https://launchpad.dev/firefox

= Launchpad lint =

Checking for conflicts and issues in changed files.

Linting changed files:
  lib/lp/registry/javascript/structural-subscription.js

./lib/lp/registry/javascript/structural-subscription.js
     655: Line exceeds 78 characters.
     693: Line exceeds 78 characters.
    1067: Line exceeds 78 characters.
-- 
https://code.launchpad.net/~danilo/launchpad/refactor-overlay-setup/+merge/56146
Your team Launchpad code reviewers is requested to review the proposed merge of lp:~danilo/launchpad/refactor-overlay-setup into lp:launchpad.
=== modified file 'lib/lp/registry/javascript/structural-subscription.js'
--- lib/lp/registry/javascript/structural-subscription.js	2011-04-01 17:55:51 +0000
+++ lib/lp/registry/javascript/structural-subscription.js	2011-04-04 12:28:56 +0000
@@ -311,42 +311,15 @@
     add_subscription_overlay.on('cancel', clean_up);
 }
 
-/*
- * Modify the DOM to insert a link or two into the global actions portlet.
- * If structural subscriptions already exist then a 'modify' link is
- * added.  Otherwise, just the 'add' link is put into the portlet.
- *
- * @method setup_subscription_links
- * @param {String} overlay_id Id of the overlay element.
- * @param {String} content_box_id Id of the element on the page where
- *     the overlay is anchored.
- */
-function setup_subscription_links(overlay_id, content_box_id) {
-    // Modify the menu-link-subscribe-to-bug-mail link to be visible.
-    var link = Y.one('.menu-link-subscribe_to_bug_mail');
-    if (!Y.Lang.isValue(link)) {
-        Y.fail('"Subscribe to bug mail" link not found.');
-    }
-    link.removeClass('invisible-link');
-    link.addClass('visible-link');
-    link.on('click', function(e) {
-        // Only proceed if the form content is already available.
-        if (add_subscription_overlay) {
-            e.halt();
-            // We always set up the overlay as a blank canvas, in case it was
-            // used before.
-            clear_overlay(Y.one(content_box_id));
-            add_subscription_overlay.show();
-        }
-    });
-    link.addClass('js-action');
-}                               // setup_subscription_links
-
 /**
  * Reset the overlay form to initial values.
  */
-function clear_overlay(content_node) {
-    set_recipient(content_node, false, undefined);
+function clear_overlay(content_node, no_recipient_picker) {
+    if (no_recipient_picker) {
+        set_recipient_label(content_node, undefined);
+    } else {
+        set_recipient(content_node, false, undefined);
+    }
     content_node.one('[name="name"]').set('value', '');
     set_checkboxes(
         content_node, LP.cache.statuses, LP.cache.statuses);
@@ -733,9 +706,9 @@
         .appendChild(container)
             .one('#structural-subscription-context-title')
                 .set('text', LP.cache.context.title);
+    add_recipient_picker(content_node, hide_recipient_picker);
 
     var accordion = create_accordion('#accordion-overlay', content_node);
-    add_recipient_picker(container, hide_recipient_picker);
 
     // Set up click handlers for the events radio buttons.
     var radio_group = Y.all('#events input');
@@ -787,35 +760,11 @@
  *         the overlay will be anchored.
  */
 namespace.setup_bug_subscriptions = function(config) {
-    validate_config(config);
-    Y.on('domready', function() {
-        if (Y.Lang.isValue(config.lp_client)) {
-            // Tests can specify an lp_client if they want to.
-            namespace.lp_client = config.lp_client;
-        } else {
-            // Setup the Launchpad client.
-            setup_client();
-        }
+    // Return if pre-setup fails.
+    if (!pre_setup(config))
+        return;
 
-        var overlay_id = setup_overlay(config.content_box, true);
-        var submit_button = Y.Node.create(
-            '<button type="submit" name="field.actions.create" ' +
-            'value="Save Changes" class="lazr-pos lazr-btn" ' +
-            '>OK</button>');
-        // This is a bit of an odd approach, but it lets us retrofit code
-        // without a large refactoring.  When edit_subscription_handler is
-        // called, context.filter_info will have the information about the
-        // filter that is being edited.
-        var context = {};
-        create_overlay(config.content_box, overlay_id, submit_button,
-            function (form_data) {
-                return edit_subscription_handler(context, form_data);});
-        fill_in_bug_subscriptions(config, context);
-        // We need to initialize the help links.  They may have already been
-        // initialized except for the ones we added, so setupHelpTrigger
-        // is idempotent.  Notice that this is old MochiKit code.
-        forEach(findHelpLinks(), setupHelpTrigger);
-    }, window);
+    fill_in_bug_subscriptions(config);
 };
 
 /*
@@ -923,95 +872,149 @@
     }
 }
 
+/* Sets the recipient label according to the filter on the overlay.
+ * Overlay must not have a recipient picker, but a simple recipient label.
+ */
+function set_recipient_label(content_node, filter_info) {
+    var recipient_label = content_node.one('input[name="recipient"] + span'),
+        teams = LP.cache.administratedTeams;
+    if (filter_info !== undefined && filter_info.subscriber_is_team) {
+        var i;
+        for (i=0; i<teams.length; i++) {
+            if (teams[i].link === filter_info.subscriber_link){
+                recipient_label.set('text', teams[i].title);
+                break;
+            }
+        }
+    } else {
+        recipient_label.set('text', 'Yourself');
+    }
+};
+
+/* Sets filter statuses and importances on the overlay based on the filter
+ * data.
+ */
+function set_filter_statuses_and_importances(content_node, filter) {
+    var is_lifecycle = filter.bug_notification_level==='Lifecycle',
+        statuses = filter.statuses,
+        importances = filter.importances;
+    if (is_lifecycle) {
+        statuses = LP.cache.statuses;
+        importances = LP.cache.importances;
+    } else {
+        // An absence of values is equivalent to all values.
+        if (statuses.length === 0) {
+            statuses = LP.cache.statuses;
+        }
+        if (importances.length === 0) {
+            importances = LP.cache.importances;
+        }
+    }
+    set_checkboxes(content_node, LP.cache.statuses, statuses);
+    set_checkboxes(
+        content_node, LP.cache.importances, importances);
+};
+
+/* Sets filter tags and tag matching options in the overlay based on the
+ * filter data.
+ */
+function set_filter_tags(content_node, filter) {
+    var is_lifecycle = filter.bug_notification_level==='Lifecycle';
+    content_node.one('[name="tags"]').set(
+        'value', is_lifecycle ? '' : filter.tags.join(' '));
+    set_radio_buttons(
+        content_node, [MATCH_ALL, MATCH_ANY],
+        filter.find_all_tags ? MATCH_ALL : MATCH_ANY);
+};
+
+/* Sets filter notification level radio/check boxes in the overlay
+ * according to the filter data.
+ */
+function set_filter_notification_options(content_node, filter) {
+    var is_lifecycle = filter.bug_notification_level==='Lifecycle';
+        has_advanced_filters = !is_lifecycle && (
+            filter.statuses.length ||
+                filter.importances.length ||
+                filter.tags.length) > 0,
+        filters = has_advanced_filters ? [ADVANCED_FILTER] : [],
+        event = ADDED_OR_CHANGED;
+    // Chattiness: Lifecycle < Details < Discussion.
+    switch (filter.bug_notification_level) {
+    case 'Lifecycle':
+        event = ADDED_OR_CLOSED;
+        filters = [];
+        break;
+    case 'Details':
+        filters.push(FILTER_COMMENTS);
+        break;
+    // case 'Discussion': This is already handled/the default.
+    // default: If we get here then it is a programmer error.
+    }
+    set_radio_buttons(
+        content_node, [ADDED_OR_CLOSED, ADDED_OR_CHANGED], event);
+    set_checkboxes(
+        content_node, [FILTER_COMMENTS, ADVANCED_FILTER], filters);
+    handle_change(ADDED_OR_CHANGED, FILTER_WRAPPER, {duration: 0});
+    handle_change(ADVANCED_FILTER, ACCORDION_WRAPPER, {duration: 0});
+};
+
+/* Loads all data from the filter into the overlay for editing. */
+function load_overlay_with_filter_data(content_node, filter_info) {
+    var filter = filter_info.filter;
+    set_recipient_label(content_node, filter_info);
+    content_node.one('[name="name"]').set('value',filter.description);
+    set_filter_statuses_and_importances(content_node, filter);
+    set_filter_tags(content_node, filter);
+    set_filter_notification_options(content_node, filter);
+}
+
+/* Show an overlay for editing a subscription. */
+function show_edit_overlay(config, subscription, filter_info, filter_id) {
+    Y.one(config.content_box).empty();
+    var content_node = Y.one(config.content_box),
+        overlay_id = setup_overlay(config.content_box, true),
+        submit_button = Y.Node.create(
+            '<button type="submit" name="field.actions.create" ' +
+                'value="Save Changes" class="lazr-pos lazr-btn" ' +
+                '>OK</button>');
+
+    clear_overlay(content_node, true);
+
+    var context = {
+        filter_info: filter_info,
+        filter_id: filter_id
+    };
+    create_overlay(
+        config.content_box, overlay_id, submit_button,
+        function (form_data) {
+            return edit_subscription_handler(context, form_data);});
+
+    load_overlay_with_filter_data(content_node, filter_info);
+    var title = subscription.target_title;
+    Y.one('#structural-subscription-context-title')
+        .set('text', title);
+    Y.one('#subscription-overlay-title')
+        .set('text', 'Edit subscription for '+title+' bugs');
+
+    // We need to initialize the help links.  They may have already been
+    // initialized except for the ones we added, so setupHelpTrigger
+    // is idempotent.  Notice that this is old MochiKit code.
+    forEach(findHelpLinks(), setupHelpTrigger);
+    add_subscription_overlay.show();
+};
+
 /**
  * Return an edit handler for the specified filter.
  */
-function make_edit_handler(subscription, filter_info, filter_id,
-                           config, context) {
+function make_edit_handler(subscription, filter_info, filter_id, config) {
     // subscription is the filter's subscription.
     // filter_info is the filter's information (from subscription.filters).
     // filter_id is the numerical id for the filter, unique on the page.
     // config is the configuration object used for the entire assembly of the
     // page.
-    // context is a way to communicate to the shared edit handler what filter
-    // should be updated.
     return function(e) {
-        // Only proceed if the form content is already available.
-        if (add_subscription_overlay) {
-            e.halt();
-            var content_node = Y.one(config.content_box),
-                teams = LP.cache.administratedTeams,
-                filter = filter_info.filter,
-                is_lifecycle = filter.bug_notification_level==='Lifecycle',
-                recipient_label = content_node.one(
-                    'input[name="recipient"] + span'),
-                statuses = filter.statuses,
-                importances = filter.importances;
-            if (filter_info.subscriber_is_team) {
-                var i;
-                for (i=0; i<teams.length; i++) {
-                    if (teams[i].link === filter_info.subscriber_link){
-                        recipient_label.set('text', teams[i].title);
-                        break;
-                    }
-                }
-            } else {
-                recipient_label.set('text', 'Yourself');
-            }
-            content_node.one('[name="name"]').set('value',filter.description);
-            if (is_lifecycle) {
-                statuses = LP.cache.statuses;
-                importances = LP.cache.importances;
-            } else {
-                // An absence of values is equivalent to all values.
-                if (statuses.length === 0) {
-                    statuses = LP.cache.statuses;
-                }
-                if (importances.length === 0) {
-                    importances = LP.cache.importances;
-                }
-            }
-            set_checkboxes(content_node, LP.cache.statuses, statuses);
-            set_checkboxes(
-                content_node, LP.cache.importances, importances);
-            content_node.one('[name="tags"]').set(
-                'value', is_lifecycle ? '' : filter.tags.join(' '));
-            set_radio_buttons(
-                content_node, [MATCH_ALL, MATCH_ANY],
-                filter.find_all_tags ? MATCH_ALL : MATCH_ANY);
-            var has_advanced_filters = !is_lifecycle && (
-                    filter.statuses.length ||
-                    filter.importances.length ||
-                    filter.tags.length) > 0,
-                filters = has_advanced_filters ? [ADVANCED_FILTER] : [],
-                event = ADDED_OR_CHANGED;
-            // Chattiness: Lifecycle < Details < Discussion.
-            switch (filter.bug_notification_level) {
-                case 'Lifecycle':
-                    event = ADDED_OR_CLOSED;
-                    filters = [];
-                    break;
-                case 'Details':
-                    filters.push(FILTER_COMMENTS);
-                    break;
-                // case 'Discussion': This is already handled/the default.
-                // default: If we get here then it is a programmer error.
-            }
-            set_radio_buttons(
-                content_node, [ADDED_OR_CLOSED, ADDED_OR_CHANGED], event);
-            set_checkboxes(
-                content_node, [FILTER_COMMENTS, ADVANCED_FILTER], filters);
-            handle_change(ADDED_OR_CHANGED, FILTER_WRAPPER, {duration: 0});
-            handle_change(ADVANCED_FILTER, ACCORDION_WRAPPER, {duration: 0});
-            context.filter_info = filter_info;
-            context.filter_id = filter_id;
-            var title = subscription.target_title;
-            Y.one('#structural-subscription-context-title')
-                .set('text', title);
-            Y.one('#subscription-overlay-title')
-                .set('text', 'Edit subscription for '+title+' bugs');
-            add_subscription_overlay.show();
-        }
+        e.halt();
+        show_edit_overlay(config, subscription, filter_info, filter_id);
     };
 }
 
@@ -1049,7 +1052,7 @@
 /**
  * Attach activation (click) handlers to all of the edit links on the page.
  */
-function wire_up_edit_links(config, context) {
+function wire_up_edit_links(config) {
     var listing = Y.one('#subscription-listing');
     var subscription_info = LP.cache.subscription_info;
     var filter_id = 0;
@@ -1064,7 +1067,7 @@
                 var node = Y.one('#subscription-filter-'+filter_id.toString());
                 var edit_link = node.one('a.edit-subscription');
                 var edit_handler = make_edit_handler(
-                    sub, filter_info, filter_id, config, context);
+                    sub, filter_info, filter_id, config);
                 edit_link.on('click', edit_handler);
                 var delete_link = node.one('a.delete-subscription');
                 var delete_handler = make_delete_handler(
@@ -1079,7 +1082,7 @@
 /**
  * Populate the subscription list DOM element with subscription descriptions.
  */
-function fill_in_bug_subscriptions(config, context) {
+function fill_in_bug_subscriptions(config) {
     validate_config(config);
 
     var listing = Y.one('#subscription-listing');
@@ -1159,7 +1162,7 @@
     }
     listing.appendChild(top_node);
 
-    wire_up_edit_links(config, context);
+    wire_up_edit_links(config);
 }
 
 /**
@@ -1267,20 +1270,16 @@
 // Expose in the namespace for testing purposes.
 namespace._validate_config = validate_config;
 
-/*
- * External entry point for configuring the structual subscription.
- * @method setup
- * @param {Object} config Object literal of config name/value pairs.
- *     config.content_box is the name of an element on the page where
- *         the overlay will be anchored.
+/* Do pre-setup checks and initalizations.
+ * Sets up the LP client and ensures the user is logged-in.
  */
-namespace.setup = function(config) {
+function pre_setup(config) {
     validate_config(config);
 
     // If the user is not logged in, then we need to defer to the
     // default behaviour.
     if (LP.links.me === undefined) {
-        return;
+        return false;
     }
     if (Y.Lang.isValue(config.lp_client)) {
         // Tests can specify an lp_client if they want to.
@@ -1289,22 +1288,68 @@
         // Setup the Launchpad client.
         setup_client();
     }
-    // Create the overlay.
+    return true;
+};
+
+/* Show the overlay for creating a new subscription.
+ */
+function show_add_overlay(config) {
+    Y.one(config.content_box).empty();
     var overlay_id = setup_overlay(config.content_box);
-    // Create the subscription links on the page.
-    setup_subscription_links(overlay_id, config.content_box);
+    clear_overlay(Y.one(config.content_box), false);
 
     var submit_button = Y.Node.create(
         '<button type="submit" name="field.actions.create" ' +
         'value="Create subscription" class="lazr-pos lazr-btn" '+
         '>OK</button>');
+
     create_overlay(config.content_box, overlay_id, submit_button,
         save_subscription);
     // We need to initialize the help links.  They may have already been
     // initialized except for the ones we added, so setupHelpTrigger
     // is idempotent.  Notice that this is old MochiKit code.
     forEach(findHelpLinks(), setupHelpTrigger);
-};               // setup
+    add_subscription_overlay.show()
+    return overlay_id;
+};
+
+/*
+ * Modify a link to pop up a subscription overlay.
+ *
+ * @method setup_subscription_link
+ * @param {String} link_id Id of the link element.
+ * @param {String} overlay_id Id of the overlay element.
+ */
+function setup_subscription_link(config, link_id) {
+    // Modify the menu-link-subscribe-to-bug-mail link to be visible.
+    var link = Y.one(link_id);
+    if (!Y.Lang.isValue(link)) {
+        Y.fail('Link to set as the pop-up link not found.');
+    }
+    link.removeClass('invisible-link');
+    link.addClass('visible-link');
+    link.on('click', function(e) {
+        e.halt();
+        show_add_overlay(config);
+    });
+    link.addClass('js-action');
+}                               // setup_subscription_links
+
+/*
+ * External entry point for configuring the structural subscription.
+ * @method setup
+ * @param {Object} config Object literal of config name/value pairs.
+ *     config.content_box is the name of an element on the page where
+ *         the overlay will be anchored.
+ */
+namespace.setup = function(config) {
+    // Return if pre-setup fails.
+    if (!pre_setup(config))
+        return;
+
+    // Create the subscription links on the page.
+    setup_subscription_link(config, '.menu-link-subscribe_to_bug_mail');
+}; // setup
 
 }, '0.1', {requires: [
         'dom', 'node', 'lazr.anim', 'lazr.formoverlay', 'lazr.overlay',


Follow ups