← Back to team overview

launchpad-reviewers team mailing list archive

[Merge] lp:~adeuring/launchpad/banner-for-beta-features-2 into lp:launchpad

 

Abel Deuring has proposed merging lp:~adeuring/launchpad/banner-for-beta-features-2 into lp:launchpad.

Requested reviews:
  Launchpad code reviewers (launchpad-reviewers)

For more details, see:
https://code.launchpad.net/~adeuring/launchpad/banner-for-beta-features-2/+merge/81833

This branch adds a banner pages having features in beta status.

The core idea: LP.cache has an entry "beta_features", which
contains copies of the tuples from lp.services.features.flags.flag_info
for features matching these conditions:

- the feature name appears in view.beta_features
- when a page is rendered, the value of the feature flag is not the
  default value. (see
  lp:~adeuring/launchpad/banner-for-beta-features for details)

The function beta_notification() in beta-notification.js checks if
LP.cache.beta_features is not emtpy, and if so, it adds a banner to
the page showing the title of the beta feature flags and optionally
a link to a page with more information about the feature.

The banner can be hidden by a click on the "hide" link.

The new property BugTaskSearchListingView.beta_features (which
overrides LaunchpadView.beta_features) activates the banner for bug
listings.

test: /bin/test -vvt test_beta_notification

Demo/QA:

  - enable the feature flag bugs.dynamic_bug_listings.enabled
  - visit any bug listing, for example https://bugs.launchpad.dev/firefox

lint:

= Launchpad lint =

Checking for conflicts and issues in changed files.

Linting changed files:
  lib/canonical/launchpad/icing/style-3-0.css
  lib/lp/app/javascript/beta-notification.js
  lib/lp/app/javascript/tests/test_beta_notification.html
  lib/lp/app/javascript/tests/test_beta_notification.js
  lib/lp/app/templates/base-layout-macros.pt
  lib/lp/bugs/browser/bugtask.py

./lib/canonical/launchpad/icing/style-3-0.css
      58: Unknown Property name.: -moz-border-radius
      59: Unknown Property name.: -webkit-border-radius
      60: Unknown Property name.: -khtml-border-radius
      61: Unknown Property name.: border-radius
[ca 700 lines deleted]
    2331: I003: To few newlines before selectors.
    2334: I003: To few newlines before selectors.
    2349: I003: To few newlines before selectors.
    2352: I003: To few newlines before selectors.

-- 
https://code.launchpad.net/~adeuring/launchpad/banner-for-beta-features-2/+merge/81833
Your team Launchpad code reviewers is requested to review the proposed merge of lp:~adeuring/launchpad/banner-for-beta-features-2 into lp:launchpad.
=== modified file 'lib/canonical/launchpad/icing/style-3-0.css'
--- lib/canonical/launchpad/icing/style-3-0.css	2011-11-02 21:19:13 +0000
+++ lib/canonical/launchpad/icing/style-3-0.css	2011-11-10 11:33:48 +0000
@@ -2101,6 +2101,37 @@
     padding: 0;
     }
 
+/* ===========
+   Beta banner
+*/
+.beta-banner {
+    position: fixed;
+    z-index: 9;
+    top: 0;
+    left: 0;
+    right: 0;
+    padding: 8px 20px;
+    /* Define colour for browsers that don't support transparency */
+    background-color: #606060;
+    /* Set transparent background for browsers that support it */
+    background-color: rgba(64, 64, 64,0.9);
+    color: #fff;
+    font-size: 14px;
+    line-height: 21px;
+    }
+.beta-banner .info-link {
+    color: #4884ef;
+}
+.beta-warning {
+    padding: 4px;
+    margin-right: 8px;
+    background-color: #be0000;
+    font-weight: bold;
+    }
+.beta-feature {
+    font-weight: bold;
+    }
+
 
 /*
  * YOU HAVE REACHED THE END OF THIS FILE. IF YOU SEE ANYTHING BELOW SPRITES

=== added file 'lib/lp/app/javascript/beta-notification.js'
--- lib/lp/app/javascript/beta-notification.js	1970-01-01 00:00:00 +0000
+++ lib/lp/app/javascript/beta-notification.js	2011-11-10 11:33:48 +0000
@@ -0,0 +1,132 @@
+YUI.add('lp.app.beta_features', function(Y) {
+
+var namespace = Y.namespace('lp.app.beta_features');
+
+var beta_notification_node = null;
+
+/**
+ * For unit tests - we need to reset the notification setup to run more than
+   one test.
+ */
+namespace._reset_beta_notification = function () {
+    notification_node = null;
+};
+
+/*
+ * Display beta feature notifications.
+ *
+ * This should be called after the page has loaded e.g. on 'domready'.
+ */
+function display_beta_notification() {
+    if (LP.cache.beta_features.length === 0) {
+        return;
+    }
+
+    var beta_features = LP.cache.beta_features;
+    var body = Y.one('body');
+    body.addClass('global-notification-visible');
+    var target_id = "maincontent";
+    var id_selector = "#" + target_id;
+    var main = Y.one(id_selector);
+    beta_notification_node = Y.Node.create('<div></div>')
+        .addClass('beta-banner');
+    main.appendChild(beta_notification_node);
+    var beta_warning = Y.Node.create(
+        '<span class="beta-warning">BETA</span>');
+    beta_notification_node.appendChild(beta_warning);
+    var close_box = Y.Node.create(
+        '<a href="#" class="global-notification-close">Hide' +
+        '<span class="notification-close sprite" /></a>');
+    beta_notification_node.appendChild(close_box);
+    beta_notification_node.append('Some parts of this page are in beta: ');
+    var index;
+    for (index = 0; index < beta_features.length; index++) {
+        var feature_name = beta_features[index][4];
+        var info_link = beta_features[index][5];
+        if (info_link.length > 0) {
+            info_link =
+                ' (<a href="' + info_link + '" class="info-link">' +
+                'read more</a>)';
+        }
+        beta_notification_node.appendChild(Y.Node.create(
+            '<span class="beta-feature"> ' + feature_name + info_link +
+            '</span>'));
+    }
+    close_box.on('click', function(e) {
+        e.halt();
+        var fade_out = new Y.Anim({
+            node: '.beta-banner',
+            to: {opacity: 0},
+            duration: 0.3
+        });
+        var body_space = new Y.Anim({
+            node: 'body',
+            to: {'paddingTop': 0},
+            duration: 0.2,
+            easing: Y.Easing.easeOut
+        });
+        var login_space = new Y.Anim({
+            node: '.login-logout',
+            to: {'top': '6px'},
+            duration: 0.2,
+            easing: Y.Easing.easeOut
+        });
+        fade_out.on('end', function() {
+            fade_out.get('node').addClass('hidden');
+        });
+        body_space.on('end', function() {
+            Y.one('body').removeClass('global-notification-visible');
+        });
+
+        fade_out.run();
+        body_space.run();
+        login_space.run();
+    });
+}
+namespace.display_beta_notification = display_beta_notification;
+
+/*
+ * Hide beta notifications
+ */
+function hide_beta_notification() {
+    setup_beta_notification();
+    if (!Y.one('.global-notification').hasClass('hidden')) {
+        var fade_out = new Y.Anim({
+            node: '.global-notification',
+            to: {opacity: 0},
+            duration: 0.3
+        });
+        var body_space = new Y.Anim({
+            node: 'body',
+            to: {'paddingTop': 0},
+            duration: 0.2,
+            easing: Y.Easing.easeOut
+        });
+        var login_space = new Y.Anim({
+            node: '.login-logout',
+            to: {'top': '6px'},
+            duration: 0.2,
+            easing: Y.Easing.easeOut
+        });
+        fade_out.on('end', function() {
+            fade_out.get('node').addClass('hidden');
+        });
+        body_space.on('end', function() {
+            Y.one('body').removeClass('global-notification-visible');
+        });
+
+        fade_out.run();
+        body_space.run();
+        login_space.run();
+
+    }
+}
+namespace.hide_beta_notification = hide_beta_notification;
+
+/* A helper for unit tests. */
+namespace._reset_beta_notification = function() {
+    beta_notification_node = null;
+};
+
+
+}, "0.1", {"requires": ["base", "node", "anim"]});

=== added file 'lib/lp/app/javascript/tests/test_beta_notification.html'
--- lib/lp/app/javascript/tests/test_beta_notification.html	1970-01-01 00:00:00 +0000
+++ lib/lp/app/javascript/tests/test_beta_notification.html	2011-11-10 11:33:48 +0000
@@ -0,0 +1,23 @@
+<html>
+  <head>
+  <title>Launchpad Beta Feature Notifications</title>
+
+  <!-- YUI and test setup -->
+  <script type="text/javascript"
+          src="../../../../canonical/launchpad/icing/yui/yui/yui.js">
+  </script>
+  <link rel="stylesheet" href="../../../app/javascript/testing/test.css" />
+  <script type="text/javascript"
+          src="../../../app/javascript/testing/testrunner.js"></script>
+
+    <!-- The module under test. -->
+    <script type="text/javascript" src="../beta-notification.js"></script>
+
+    <!-- The test suite. -->
+    <script type="text/javascript" src="test_beta_notification.js"></script>
+  </head>
+  <body class="yui3-skin-sam">
+    <!-- The example markup required by the script to run. -->
+  <div id="maincontent"> </div>
+  </body>
+</html>

=== added file 'lib/lp/app/javascript/tests/test_beta_notification.js'
--- lib/lp/app/javascript/tests/test_beta_notification.js	1970-01-01 00:00:00 +0000
+++ lib/lp/app/javascript/tests/test_beta_notification.js	2011-11-10 11:33:48 +0000
@@ -0,0 +1,125 @@
+/* Copyright 2011 Canonical Ltd.  This software is licensed under the
+ * GNU Affero General Public License version 3 (see the file LICENSE).
+ */
+
+// Set the "enabled" variable, normally set by base-layout-macros.
+// This must be a global variable for the code being tested to work.
+var privacy_notification_enabled = true;
+
+YUI().use('lp.testing.runner', 'test', 'console', 'node',
+          'lp.app.beta_features', 'node-event-simulate', function(Y) {
+
+    var suite = new Y.Test.Suite("lp.app.beta_notification Tests");
+
+    suite.add(new Y.Test.Case({
+        name: 'beta-notification',
+
+        _reset_container: function () {
+            Y.lp.app.beta_features._reset_beta_notification();
+            var body = Y.one(document.body);
+
+            // Replace the container.
+            var container = Y.one('#maincontent');
+            container.remove(true);
+            container = Y.Node.create('<div></div>')
+                .set('id', 'maincontent');
+            body.appendChild(container);
+            body.removeClass('global-notification-visible');
+            return container;
+        },
+
+        setUp: function () {
+            // Create the global notification html.
+            var container = this._reset_container();
+            var login_logout = Y.Node.create('<div></div>')
+                .addClass('login-logout');
+            container.appendChild(login_logout);
+            window.LP = {
+                cache: {}
+            };
+        },
+
+        test_beta_banner_one_beta_feature: function() {
+            LP.cache.beta_features = [
+                ['', '', '', '', 'A beta feature', 'http://lp.dev/LEP/one']];
+            Y.lp.app.beta_features.display_beta_notification();
+
+            var body = Y.one('body');
+            // The <body> node has the class global-notification-visible,
+            // so that the element has enough padding for the beta banner.
+            Y.Assert.isTrue(body.hasClass('global-notification-visible'));
+
+            var banner = Y.one('.beta-banner');
+            var sub_nodes = banner.get('children');
+
+            // The message about a beta feature consists of the feature
+            // title and a link to a page with more information about
+            // the feature.
+            feature_info = sub_nodes.filter('.beta-feature').item(0);
+            Y.Assert.areEqual(
+                ' A beta feature (read more)', feature_info.get('text'));
+            info_link = feature_info.get('children').item(0);
+            Y.Assert.areEqual('http://lp.dev/LEP/one', info_link.get('href'));
+        },
+
+        test_beta_banner_two_beta_features: function() {
+            LP.cache.beta_features = [
+                ['', '', '', '', 'Beta feature 1', 'http://lp.dev/LEP/one'],
+                ['', '', '', '', 'Beta feature 2', '']];
+            Y.lp.app.beta_features.display_beta_notification();
+
+            var body = Y.one('body');
+            Y.Assert.isTrue(body.hasClass('global-notification-visible'));
+
+            var banner = Y.one('.beta-banner');
+            var sub_nodes = banner.get('children');
+
+            // Notifications about several features can be displayed.
+            feature_info = sub_nodes.filter('.beta-feature').item(0);
+            Y.Assert.areEqual(
+                ' Beta feature 1 (read more)', feature_info.get('text'));
+            info_link = feature_info.get('children').item(0);
+            Y.Assert.areEqual('http://lp.dev/LEP/one', info_link.get('href'));
+
+            // If an entry in LP.cache.beta_features does not provide a
+            // "read more" link, the corrsponding node is not added.
+            feature_info = sub_nodes.filter('.beta-feature').item(1);
+            Y.Assert.areEqual(
+                ' Beta feature 2', feature_info.get('text'));
+            Y.Assert.isNull(feature_info.get('children').item(0));
+        },
+
+        test_beta_banner_no_beta_features_defined: function() {
+            LP.cache.beta_features = [];
+            Y.lp.app.beta_features.display_beta_notification();
+
+            var body = Y.one('body');
+            Y.Assert.isFalse(body.hasClass('global-notification-visible'));
+
+            Y.Assert.isNull(Y.one('.beta-banner'));
+        },
+
+        test_hide_beta_banner: function() {
+            LP.cache.beta_features = [
+                ['', '', '', '', 'A beta feature', 'http://lp.dev/LEP/one']];
+            Y.lp.app.beta_features.display_beta_notification();
+            var body = Y.one('body');
+            var banner = Y.one('.beta-banner');
+            Y.Assert.isFalse(banner.hasClass('hidden'));
+
+            close_link = Y.one('.global-notification-close');
+            close_link.simulate('click');
+            // We must wait until the fade out animation finishes.
+            this.wait(
+                function() {
+                    Y.Assert.isTrue(banner.hasClass('hidden'));
+                    Y.Assert.isFalse(
+                        body.hasClass('global-notification-visible'));
+                },
+                400);
+        }
+    }));
+
+    Y.lp.testing.Runner.run(suite);
+});
+

=== modified file 'lib/lp/app/templates/base-layout-macros.pt'
--- lib/lp/app/templates/base-layout-macros.pt	2011-10-25 23:41:57 +0000
+++ lib/lp/app/templates/base-layout-macros.pt	2011-11-10 11:33:48 +0000
@@ -107,8 +107,8 @@
   <metal:load-lavascript use-macro="context/@@+base-layout-macros/load-javascript" />
 
     <script type="text/javascript">
-      LPS.use('base', 'node', 'oop', 'event', 'lp.bugs.bugtask_index',
-                'lp.bugs.subscribers',
+      LPS.use('base', 'node', 'oop', 'event', 'lp.app.beta_features',
+              'lp.bugs.bugtask_index', 'lp.bugs.subscribers',
                 'lp.code.branchmergeproposal.diff', 'lp.comments.hide',
                 function(Y) {
           Y.on("domready", function () {
@@ -116,6 +116,7 @@
                   Y.lp.app.privacy.setup_privacy_notification();
                   Y.lp.app.privacy.display_privacy_notification();
               }
+              Y.lp.app.beta_features.display_beta_notification();
           });
       });
     </script>
@@ -266,11 +267,6 @@
       &nbsp;&bull;&nbsp;
       <a href="http://blog.launchpad.net/";
 	>Blog</a>
-      <tal:careers_link condition="not: features/baselayout.careers_link.disabled">
-	&nbsp;&bull;&nbsp;
-	<a href="http://www.canonical.com/about-canonical/careers";
-		>Careers</a>
-      </tal:careers_link>
       &nbsp;&bull;&nbsp;
       <a href="http://identi.ca/launchpadstatus";
         >System status</a>

=== modified file 'lib/lp/bugs/browser/bugtask.py'
--- lib/lp/bugs/browser/bugtask.py	2011-11-08 14:08:46 +0000
+++ lib/lp/bugs/browser/bugtask.py	2011-11-10 11:33:48 +0000
@@ -2445,6 +2445,8 @@
 
     implements(IBugTaskSearchListingMenu)
 
+    beta_features = ['bugs.dynamic_bug_listings.enabled']
+
     # Only include <link> tags for bug feeds when using this view.
     feed_types = (
         BugTargetLatestBugsFeedLink,


Follow ups