Merge lp:~sinzui/launchpad/progressive-enhancement-ftw into lp:launchpad

Proposed by Curtis Hovey
Status: Merged
Approved by: j.c.sackett
Approved revision: no longer in the source branch.
Merged at revision: 15180
Proposed branch: lp:~sinzui/launchpad/progressive-enhancement-ftw
Merge into: lp:launchpad
Diff against target: 646 lines (+245/-134)
17 files modified
lib/lp/app/doc/lazr-js-widgets.txt (+13/-16)
lib/lp/app/javascript/autocomplete/autocomplete.js (+0/-7)
lib/lp/app/javascript/picker/tests/test_personpicker.html (+4/-4)
lib/lp/app/javascript/picker/tests/test_picker_patcher.html (+4/-4)
lib/lp/app/javascript/tests/test_multicheckboxwidget.html (+3/-4)
lib/lp/app/templates/inline-multicheckbox-widget.pt (+10/-9)
lib/lp/app/templates/inline-picker.pt (+15/-19)
lib/lp/bugs/javascript/bugtask_index.js (+1/-1)
lib/lp/bugs/javascript/official_bug_tags.js (+11/-8)
lib/lp/bugs/javascript/tests/test_official_bug_tags.html (+133/-0)
lib/lp/bugs/javascript/tests/test_official_bug_tags.js (+49/-0)
lib/lp/bugs/templates/bugtarget-macros-filebug.pt (+1/-3)
lib/lp/bugs/templates/official-bug-target-manage-tags.pt (+1/-3)
lib/lp/bugs/tests/testfiles/sourceforge-project-fronobulator.html (+0/-45)
lib/lp/bugs/tests/testfiles/sourceforge-tracker-5570.html (+0/-3)
lib/lp/registry/stories/project/xx-project-edit.txt (+0/-3)
lib/lp/registry/templates/product-index.pt (+0/-5)
To merge this branch: bzr merge lp:~sinzui/launchpad/progressive-enhancement-ftw
Reviewer Review Type Date Requested Status
j.c.sackett (community) Approve
Richard Harding (community) code* Approve
Review via email: mp+103733@code.launchpad.net

Commit message

Avoid noscript because it contradicts progressive enhancement assumptions.

Description of the change

Several features fail for less common browsers because noscript is used
for the default markup. When progressive enhancement fails, there is no
default markup to fallback to. YUI assumes progressive enhancement works.

Over the last 3 years, we have seen IE, Opera, Konquorer and Firefox 3
break because progressive enhancement failed without fallback.

This branch fixes half of the issues which prevent users from completing
tasks.

--------------------------------------------------------------------

RULES

    Pre-implementation: wgrant
    * Remove all uses of noscript that have a YUI counterpart.
    * Remove noscript that setups up visibility state.

QA

    These acceptance tests require firefox, chromium, ie8 and knoquorer.
    Also, one browser should have js disabled to verify a plain html link
    is always available:

    Person Picker
    * Visit https://qastaging.launchpad.net/launchpad
    * Verify there is an action change the maintainer and the driver.
      The driver action also has a help link.
    * Verify that the action shows the overlay or html page.

    Person Picker and Picker Patcher
    * Visit https://blueprints.qastaging.launchpad.net/gdp/+spec/gdplaunchpad
    * Verify there the drafter and implementations status can be changed

    Multicheckbox Widget
    * Visit https://code.qastaging.launchpad.net/~sinzui/+recipe/pocket-lint-daily
    * Verify there the distribution series can be changed.

    Official bug tags
    * Visit https://bugs.qastaging.launchpad.net/launchpad/+manage-official-tags
    * Verify the plain html form is shown to non-js browsers.
    * Verify that the js form is shown to js-enabled-browsers

    Project group choose product
    * Visit https://bugs.qastaging.launchpad.net/launchpad-project/+filebug
    * Choose Launchpad, enter a summary, then choose Next.
    * In a js-enabled browser verify the URL says that you are at
      launchpad/+filebug
    * In a non-js browser verify the URL is still at launchpad-project
      and you can see Launchpad selected in the project field

    Configuration Progress
    * Visit https://qastaging.launchpad.net/launchpad/
    * In a js-enabled browser, verify the Configuration Progress is shown,
      /but/ the applications are collapsed.
    * In a non-js browser, verify the Configuration Progress is shown,
      /and/ the applications are shown too.

    File Bug Extra Options
    * Visit https://bugs.qastaging.launchpad.net/launchpad/+filebug
    * Enter 'athena' in the summary summary and choose Next.
    * In a js-enabled browser, verify extra options is collapsed.
    * In a non-js browser, verify the extra options are visible.

LINT

    lib/lp/app/doc/lazr-js-widgets.txt
    lib/lp/app/javascript/picker/tests/test_personpicker.html
    lib/lp/app/javascript/picker/tests/test_picker_patcher.html
    lib/lp/app/javascript/tests/test_multicheckboxwidget.html
    lib/lp/app/templates/inline-multicheckbox-widget.pt
    lib/lp/app/templates/inline-picker.pt
    lib/lp/bugs/javascript/official_bug_tags.js
    lib/lp/bugs/javascript/tests/test_official_bug_tags.html
    lib/lp/bugs/javascript/tests/test_official_bug_tags.js
    lib/lp/bugs/templates/bugtarget-macros-filebug.pt
    lib/lp/bugs/templates/official-bug-target-manage-tags.pt
    lib/lp/registry/templates/product-index.pt

TEST

    ./bin/test -vv -t lazr-js-widgets lp.app.tests.test_doc
    ./bin/test -vv --layer=YUItest lp.app
    ./bin/test -vv --layer=YUItest lp.bugs

IMPLEMENTATION

The picker, the patcher and multicheckbox widget all used a button for
js browsers, and an anchor for non-js browsers. All three cases were
fixed by replacing the button with the anchor and adding "lazr-btn
yui3-activator-act" to the class. Note that the inline edit picker
doctest was wrong...it never tested that the widget rendered in edit
mode with all the proper css! Two of the widget templates needed a root
element so that I could lint them.
    lib/lp/app/doc/lazr-js-widgets.txt
    lib/lp/app/javascript/picker/tests/test_personpicker.html
    lib/lp/app/javascript/picker/tests/test_picker_patcher.html
    lib/lp/app/javascript/tests/test_multicheckboxwidget.html
    lib/lp/app/templates/inline-multicheckbox-widget.pt
    lib/lp/app/templates/inline-picker.pt

I removed the noscript around the plain official bug tags html form and
added a line to the script to hide the html form when setup completes.
Alas there was no YUI test for the script, and the script had lint. I
did the minimum to hush lint and test my line of code.
    lib/lp/bugs/javascript/official_bug_tags.js
    lib/lp/bugs/javascript/tests/test_official_bug_tags.html
    lib/lp/bugs/javascript/tests/test_official_bug_tags.js
    lib/lp/bugs/templates/official-bug-target-manage-tags.pt

I removed the noscript from the product widget because it is only shown
on the project group page. It might have been needed in the past, but
users with js browsers cannot see it anyway because when you make your
selection, your browser changes the context to the real product, so Lp
was hiding the widget on a page that js users never load. See below for
the explanation of why 'unseen' is not needed.
    lib/lp/bugs/templates/bugtarget-macros-filebug.pt

The lp.app.expander module is our friend. It handles the setup and
fallback rules for blocks with the 'collapsible' class. Pages and
scripts do not need to set the initial state of visibility or add rules
to ensure non-js browsers can see the block. I removed the 'unseen'
class from +filebug extra options and the css redefines for collapsible
because the markup is visible by default. These rules are cruft that
could have been removed when we enabled lp.app.expander lp.app.expander.
    lib/lp/registry/templates/product-index.pt

To post a comment you must log in.
Revision history for this message
Richard Harding (rharding) wrote :

Thanks for this. I really love to see this noscript stuff die in a fire. A few comments and info to look into. I'm going to mark as needs fixing mainly based on our irc conversation and the alt text note below.

- wgrant brought up that a div in span tag (line #24) isn't valid. This appears to 'run deep' though in the layout and I'm worried that the activator message box bits will break if it's not a div. Can you investigate? Can we just make it another span and css that up to be a display: block to make it quick/easy to update? This will require checking the css and the js for relying on the div bit in selectors and such for both classes yui3-activator-message-box and yui3-activator-hidden

- Should we be adding alt text to the <a links? Not sure if screen readers will do title if alt not there, etc. In doing a quick google, it seems many by default will not read title, but alt. Title is handy for hover in browsers though. I see comments that adding both with the same informationis bad though. The idea is that alt says what something *is* while title says where it'll go if clicked? So for the edit links, it might be an alt says 'edit button' while the title text would be 'edit object xxx'.

- Discussed in irc the implications of having the <a tags all invisible-link, in particular for non-js users and non-graphical users.

- #239 the link goes out to egg?
- #407 if the unseen css class isn't needed is the div needed at all?

review: Needs Fixing (code*)
Revision history for this message
Curtis Hovey (sinzui) wrote :

We, well myself and former members of the registry team, tried to solve the div span issue. It is tricky work. The root issues is that HTML and Lp require all widgets to work as inline elements because they can be placed in markup that only permits inline elements, such as a paragraph. This work is out of scope for this branch. I believe we have a better chance of fixing this issue now that Lp intends to be HTML5. I think we want to switch everything to span and use inline-blocks when needed.

I agree that we want titles on the links.

Yes, we have code that declares that it uses a module that is in an egg.

The container element is needed so that lp.app.expander can find the second chunk and add the lazr-closed class.

Revision history for this message
Curtis Hovey (sinzui) wrote :

Hi Rick.

I think I already answered your first question.

I reviewed the changes I made...Lp is already providing a title for all the links. The title attribute was introduced in HTML 3.0 and enforced in HTML 3.2 in 1997. All screen readers support title because it is a valid attribute for all HTML elements. The alt attribute is historical and only used by img, applet, area, and input. We do not want to add an invalid attribute.

Yes we have two modules that import widget-position-ext, autocomplete and official_bug_tags. The calls are commented out in autocomplete, and official_bug_tag now uses WidgetPosition.CC. Maybe the code was updated to use the standard lib when .CC was moved into it. We do not need widget-position-ext. I removed the remaining calls to it. I added a test (because I already took the painful step of adding a test module) to demonstrate that the bug tag error message is shown.

As for the container element in a collapsible, it is needed so that lp.app.expander can find the second chunk and add the lazr-closed class.

Revision history for this message
Richard Harding (rharding) wrote :

Thanks Curtis, appreciate the changes.

review: Approve (code*)
Revision history for this message
j.c.sackett (jcsackett) wrote :

I have nothing to add. Noscript is terrible, and I'm happy to see it go away.

review: Approve

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1=== modified file 'lib/lp/app/doc/lazr-js-widgets.txt'
2--- lib/lp/app/doc/lazr-js-widgets.txt 2012-04-10 14:01:17 +0000
3+++ lib/lp/app/doc/lazr-js-widgets.txt 2012-04-28 03:06:27 +0000
4@@ -249,18 +249,20 @@
5 huge, the different items are shown in the normal paginated way for the user
6 to select.
7
8- >>> owner = IArchive['owner']
9- >>> widget = InlineEditPickerWidget(archive, owner, default_text)
10+ >>> ignore = login_person(product.owner)
11+ >>> owner = IProduct['owner']
12+ >>> widget = InlineEditPickerWidget(product, owner, default_text)
13 >>> print widget()
14 <span id="edit-owner">
15 <span class="yui3-activator-data-box">
16- <a...>Eric</a>
17+ <a href="/~eric" class="sprite person">Eric</a>
18 </span>
19- <button class="lazr-btn yui3-activator-act yui3-activator-hidden">
20- Edit
21- </button>
22- <div class="yui3-activator-message-box yui3-activator-hidden"></div>
23- </span>
24+ <span>
25+ <a class="sprite edit lazr-btn yui3-activator-act"
26+ href="http://launchpad.dev/widget/+edit"
27+ title="">&nbsp;<span class="invisible-link">Edit</span></a>
28+ <div class="yui3-activator-message-box yui3-activator-hidden"></div>
29+ </span> ...
30
31
32 Picker headings
33@@ -462,15 +464,10 @@
34 <span id="edit-distroseries">
35 <dt>
36 Recipe distro series
37- <button class="lazr-btn yui3-activator-act yui3-activator-hidden"
38- id="edit-distroseries-btn">
39- Edit
40- </button>
41- <noscript>
42- <a class="sprite edit"
43+ <a class="sprite edit lazr-btn yui3-activator-act"
44 href="http://code.launchpad.dev/~eric/+recipe/cake_recipe/+edit"
45- title=""></a>
46- </noscript>
47+ id="edit-distroseries-btn"
48+ title="">&nbsp;<span class="invisible-link">Edit</span></a>
49 </dt>
50 <span class="yui3-activator-data-box">
51 <dl id='edit-distroseries-items'>
52
53=== modified file 'lib/lp/app/javascript/autocomplete/autocomplete.js'
54--- lib/lp/app/javascript/autocomplete/autocomplete.js 2012-03-14 12:51:18 +0000
55+++ lib/lp/app/javascript/autocomplete/autocomplete.js 2012-04-28 03:06:27 +0000
56@@ -661,13 +661,6 @@
57
58 // We need to calculate the input area's caret position.
59 Y.augment(node, Y.lazr.NodeCaretPos);
60-
61- // Align our position to the input element.
62- //~ this.set('align', {
63- //~ node: node,
64- //~ points: [Y.WidgetPositionExt.TL, Y.WidgetPositionExt.BL]
65- //~ });
66-
67 return node;
68 },
69
70
71=== modified file 'lib/lp/app/javascript/picker/tests/test_personpicker.html'
72--- lib/lp/app/javascript/picker/tests/test_personpicker.html 2012-03-14 04:41:36 +0000
73+++ lib/lp/app/javascript/picker/tests/test_personpicker.html 2012-04-28 03:06:27 +0000
74@@ -70,10 +70,10 @@
75 <span id="pickertest">
76 <span>
77 A picker widget test
78- <button id="edit-pickertest-btn"
79- class="lazr-btn yui3-activator-act yui3-activator-hidden">
80- Edit
81- </button>
82+ <a id="edit-pickertest-btn"
83+ class="sprite edit lazr-btn yui3-activator-act"
84+ href="/fnord/+edit-people"
85+ >&nbsp;Edit</a>
86 </span>
87 <span class="yui3-activator-data-box">
88 </span>
89
90=== modified file 'lib/lp/app/javascript/picker/tests/test_picker_patcher.html'
91--- lib/lp/app/javascript/picker/tests/test_picker_patcher.html 2012-03-14 04:41:36 +0000
92+++ lib/lp/app/javascript/picker/tests/test_picker_patcher.html 2012-04-28 03:06:27 +0000
93@@ -67,10 +67,10 @@
94 <span id="pickertest">
95 <span>
96 A picker widget test
97- <button id="edit-pickertest-btn"
98- class="lazr-btn yui3-activator-act yui3-activator-hidden">
99- Edit
100- </button>
101+ <a id="edit-pickertest-btn"
102+ class="sprite edit lazr-btn yui3-activator-act"
103+ href="/fnord/+edit-people"
104+ >&nbsp;Edit</a>
105 </span>
106 <span class="yui3-activator-data-box">
107 </span>
108
109=== modified file 'lib/lp/app/javascript/tests/test_multicheckboxwidget.html'
110--- lib/lp/app/javascript/tests/test_multicheckboxwidget.html 2012-03-14 04:41:36 +0000
111+++ lib/lp/app/javascript/tests/test_multicheckboxwidget.html 2012-04-28 03:06:27 +0000
112@@ -50,10 +50,9 @@
113 <span id="multicheckboxtest">
114 <span>
115 A multicheckbox widget test
116- <button id="edit-multicheckboxtest-btn"
117- class="lazr-btn yui3-activator-act yui3-activator-hidden">
118- Edit
119- </button>
120+ <a id="edit-multicheckboxtest-btn"
121+ class="sprite edit lazr-btn yui3-activator-act"
122+ >Edit</a>
123 </span>
124 <span class="yui3-activator-data-box">
125 <span id="edit-test-items"></span>
126
127=== modified file 'lib/lp/app/templates/inline-multicheckbox-widget.pt'
128--- lib/lp/app/templates/inline-multicheckbox-widget.pt 2012-02-01 15:31:32 +0000
129+++ lib/lp/app/templates/inline-multicheckbox-widget.pt 2012-04-28 03:06:27 +0000
130@@ -1,18 +1,18 @@
131+<tal:root
132+ xmlns:tal="http://xml.zope.org/namespaces/tal"
133+ xmlns:i18n="http://xml.zope.org/namespaces/i18n"
134+ omit-tag="">
135 <span tal:define="items view/items|nothing"
136 tal:attributes="id view/content_box_id">
137 <tal:label-open-tag replace="structure view/label_open_tag"/>
138 <span tal:replace="structure view/label"/>
139 <tal:has_choices condition="python:view.has_choices and view.can_write">
140- <button tal:attributes="id string:${view/content_box_id}-btn"
141- class="lazr-btn yui3-activator-act yui3-activator-hidden">
142- Edit
143- </button>
144+ <a tal:attributes="id string:${view/content_box_id}-btn;
145+ href view/edit_url;
146+ title view/edit_title"
147+ class="sprite edit lazr-btn yui3-activator-act"
148+ >&nbsp;<span class="invisible-link">Edit</span></a>
149 </tal:has_choices>
150- <noscript tal:condition="view/can_write">
151- <a tal:attributes="href view/edit_url;
152- title view/edit_title"
153- class="sprite edit"></a>
154- </noscript>
155 <tal:label-close-tag replace="structure view/label_close_tag"/>
156 <span class="yui3-activator-data-box">
157 <tal:items-open-tag replace="structure view/items_open_tag"/>
158@@ -48,3 +48,4 @@
159 }, window);
160 });
161 "/>
162+</tal:root>
163
164=== modified file 'lib/lp/app/templates/inline-picker.pt'
165--- lib/lp/app/templates/inline-picker.pt 2012-03-21 01:26:45 +0000
166+++ lib/lp/app/templates/inline-picker.pt 2012-04-28 03:06:27 +0000
167@@ -1,33 +1,28 @@
168-<span tal:attributes="id view/content_box_id">
169+<tal:root
170+ xmlns:tal="http://xml.zope.org/namespaces/tal"
171+ xmlns:i18n="http://xml.zope.org/namespaces/i18n"
172+ omit-tag="">
173+ <span tal:attributes="id view/content_box_id">
174 <span class="yui3-activator-data-box">
175 <tal:attribute replace="structure view/default_html"/>
176 </span>
177- <button class="lazr-btn yui3-activator-act yui3-activator-hidden">
178- Edit
179- </button>
180-
181- <a tal:condition="view/help_link"
182- tal:attributes="href view/help_link"
183- target="help"
184- class="sprite maybe">&nbsp;
185- <span class="invisible-link">Driver help</span>
186- </a>
187-
188- <noscript tal:condition="view/can_write">
189+ <span tal:condition="view/can_write">
190 <a tal:attributes="href view/edit_url;
191 title view/edit_title"
192- class="sprite edit"></a>
193+ class="sprite edit lazr-btn yui3-activator-act"
194+ >&nbsp;<span class="invisible-link">Edit</span></a>
195+
196 <a tal:condition="view/help_link"
197 tal:attributes="href view/help_link"
198 target="help"
199 class="sprite maybe">&nbsp;
200 <span class="invisible-link">Driver help</span>
201 </a>
202- </noscript>
203- <div class="yui3-activator-message-box yui3-activator-hidden"></div>
204-</span>
205+ <div class="yui3-activator-message-box yui3-activator-hidden"></div>
206+ </span>
207+ </span>
208
209-<script tal:condition="view/can_write"
210+ <script tal:condition="view/can_write"
211 tal:content="structure string:
212 LPJS.use('lp.app.picker', 'lp.client', function(Y) {
213 var picker_config = ${view/json_config}
214@@ -41,4 +36,5 @@
215 picker_config);
216 }, window);
217 });
218-"/>
219+ "/>
220+</tal:root>
221
222=== modified file 'lib/lp/bugs/javascript/bugtask_index.js'
223--- lib/lp/bugs/javascript/bugtask_index.js 2012-04-23 23:36:37 +0000
224+++ lib/lp/bugs/javascript/bugtask_index.js 2012-04-28 03:06:27 +0000
225@@ -1520,7 +1520,7 @@
226
227
228 }, "0.1", {"requires": ["base", "oop", "node", "event", "io-base",
229- "json-parse", "substitute", "widget-position-ext",
230+ "json-parse", "substitute",
231 "lazr.formoverlay", "lp.anim", "lazr.base",
232 "lazr.overlay", "lazr.choiceedit", "lp.app.picker",
233 "lp.bugs.bugtask_index.portlets.subscription",
234
235=== modified file 'lib/lp/bugs/javascript/official_bug_tags.js'
236--- lib/lp/bugs/javascript/official_bug_tags.js 2012-03-15 05:40:30 +0000
237+++ lib/lp/bugs/javascript/official_bug_tags.js 2012-04-28 03:06:27 +0000
238@@ -54,7 +54,7 @@
239 */
240 var sort_tags = function(tags) {
241 tags.sort(function(x, y) {
242- if (x.tag == y.tag) {
243+ if (x.tag === y.tag) {
244 return 0;
245 } else if (x.tag > y.tag) {
246 return 1;
247@@ -89,7 +89,7 @@
248 /**
249 * Get an array of unofficial bug tags and their use count.
250 *
251- * Uses an array of objects representing used tags
252+ * Uses an array of objects representing used tags
253 * embedded in the page.
254 *
255 * @method get_other_bug_tags
256@@ -298,21 +298,21 @@
257 var new_tag_already_official = false;
258 Y.each(official_tags, function(item) {
259 new_tag_already_official = (
260- new_tag_already_official || (item.tag == new_tag));
261+ new_tag_already_official || (item.tag === new_tag));
262 });
263 var new_tag_already_used = false;
264 Y.each(other_tags, function(item) {
265 new_tag_already_used = (
266- new_tag_already_used || (item.tag == new_tag));
267+ new_tag_already_used || (item.tag === new_tag));
268 });
269 if (new_tag_already_used) {
270 Y.each(other_tags, function(item) {
271- if (item.tag == new_tag) {
272+ if (item.tag === new_tag) {
273 official_tags.push(item);
274 }
275 });
276 other_tags = filter_array(other_tags, function(item) {
277- return item.tag != new_tag;
278+ return item.tag !== new_tag;
279 });
280 }
281 if (!new_tag_already_official && !new_tag_already_used) {
282@@ -418,7 +418,7 @@
283 Y.one('#new-tag-text').on('keypress', function(e) {
284 var new_value = Y.Lang.trim(Y.one('#new-tag-text').get('value'));
285 Y.one('#new-tag-add').set('disabled', new_value === '');
286- if (e.keyCode == 13) { // Enter == 13
287+ if (e.keyCode === 13) { // Enter == 13
288 on_new_tag_add();
289 }
290 });
291@@ -427,9 +427,12 @@
292 e.halt();
293 save_tags();
294 });
295+
296+ // All went well, hide the plain html form.
297+ Y.one('form[name=launchpadform]').setStyle('display', 'none');
298 };
299 }, "0.1", {
300 "requires": [
301- "array-extras", "node", "substitute", "base", "widget-position-ext",
302+ "array-extras", "node", "substitute", "base",
303 "collection", "lazr.base", "lazr.overlay"]
304 });
305
306=== added file 'lib/lp/bugs/javascript/tests/test_official_bug_tags.html'
307--- lib/lp/bugs/javascript/tests/test_official_bug_tags.html 1970-01-01 00:00:00 +0000
308+++ lib/lp/bugs/javascript/tests/test_official_bug_tags.html 2012-04-28 03:06:27 +0000
309@@ -0,0 +1,133 @@
310+<!DOCTYPE html>
311+<!--
312+Copyright 2012 Canonical Ltd. This software is licensed under the
313+GNU Affero General Public License version 3 (see the file LICENSE).
314+-->
315+
316+<html>
317+ <head>
318+ <title>Test official_bug_tags</title>
319+
320+ <!-- YUI and test setup -->
321+ <script type="text/javascript"
322+ src="../../../../../build/js/yui/yui/yui.js">
323+ </script>
324+ <link rel="stylesheet"
325+ href="../../../../../build/js/yui/console/assets/console-core.css" />
326+ <link rel="stylesheet"
327+ href="../../../../../build/js/yui/console/assets/skins/sam/console.css" />
328+ <link rel="stylesheet"
329+ href="../../../../../build/js/yui/test/assets/skins/sam/test.css" />
330+
331+ <script type="text/javascript"
332+ src="../../../../../build/js/lp/app/testing/testrunner.js"></script>
333+ <link rel="stylesheet" href="../../../app/javascript/testing/test.css" />
334+
335+ <!-- Dependencies -->
336+ <script type="text/javascript" src="../../../../../build/js/yui/base/base.js"></script>
337+ <script type="text/javascript" src="../../../../../build/js/yui/node/node.js"></script>
338+ <script type="text/javascript" src="../../../../../build/js/yui/collection/collection.js"></script>
339+ <script type="text/javascript" src="../../../../../build/js/yui/collection/array-extras.js"></script>
340+ <script type="text/javascript" src="../../../../../build/js/yui/substitute/substitute.js"></script>
341+ <script type="text/javascript" src="../../../../../build/js/lp/app/lazr/lazr.js"></script>
342+ <script type="text/javascript" src="../../../../../build/js/lp/app/overlay/overlay.js"></script>
343+
344+ <!-- The module under test. -->
345+ <script type="text/javascript" src="../official_bug_tags.js"></script>
346+
347+ <!-- The test suite. -->
348+ <script type="text/javascript" src="test_official_bug_tags.js"></script>
349+ </head>
350+ <body class="yui3-skin-sam">
351+ <ul id="suites">
352+ <li>lp.bugs.official_bug_tags.test</li>
353+ </ul>
354+
355+ <div id="fixture"></div>
356+
357+ <script type="text/javascript">
358+ // The page must provide these three global variables.
359+ var used_bug_tags = {"trivial": 1, "easy": 1};
360+ var official_bug_tags = ["exception", "feature", "ui"];
361+ var valid_name_pattern = "^[a-z0-9][a-z0-9\\+\\.\\-]+$";
362+ </script>
363+
364+ <script type="text/x-template" id="form-template">
365+ <div>
366+ <form name="launchpadform">
367+ <input type="text" name="tags" />
368+ <input type="submit" name="Save" />
369+ </form>
370+ </div>
371+
372+ <table id="layout-table" class="official-tags-layout-table hidden">
373+ <tr>
374+ <th>Official tags</th>
375+ <th></th>
376+ <th>Other tags</th>
377+ </tr>
378+ <tr>
379+ <td id="left-column" class="left-column">
380+ <div class="list-scrollable-area">
381+ <ul id="official-tags-list">
382+ </ul>
383+ </div>
384+ </td>
385+ <td id="middle-column" class="middle-column">
386+ <input type="submit" value="&larr;"
387+ id="add-official-tags" class="arrow-button"
388+ disabled="true" />
389+ <br />
390+ <input type="submit" value="&rarr;;"
391+ id="remove-official-tags" class="arrow-button"
392+ disabled="true" />
393+ <br />
394+ <form action="+manage-official-tags" method="post" id="save-form">
395+ <input type="hidden"
396+ id="field-official_bug_tags"
397+ name="field.official_bug_tags" />
398+ <input type="hidden" name="field.actions.save" value="Save" />
399+ </form>
400+ </td>
401+ <td id="right-column" class="right-column">
402+ <div class="list-scrollable-area">
403+ <ul id="other-tags-list">
404+ </ul>
405+ </div>
406+ </td>
407+ </tr>
408+ <tr>
409+ <td class="left-column">
410+ <br />
411+ <table class="input-field-layout-table">
412+ <tr>
413+ <td colspan="2">
414+ Add a new official tag:
415+ </td>
416+ </tr>
417+ <tr>
418+ <td style="width: 100%">
419+ <input type="text" id="new-tag-text" style="width: 100%" />
420+ </td>
421+ <td>
422+ <input type="submit" value="Add" id="new-tag-add"
423+ disabled="true" />
424+ </td>
425+ </tr>
426+ </table>
427+ </td>
428+ <td class="middle-column">
429+ </td>
430+ <td class="right-column">
431+ </td>
432+ </tr>
433+ <tr>
434+ <td class="actions" colspan="3">
435+ <input type="submit" value="Save" id="save-button"
436+ disabled="true" />
437+ </td>
438+ </tr>
439+ </table>
440+ </script>
441+ </body>
442+</html>
443
444=== added file 'lib/lp/bugs/javascript/tests/test_official_bug_tags.js'
445--- lib/lp/bugs/javascript/tests/test_official_bug_tags.js 1970-01-01 00:00:00 +0000
446+++ lib/lp/bugs/javascript/tests/test_official_bug_tags.js 2012-04-28 03:06:27 +0000
447@@ -0,0 +1,49 @@
448+YUI().use('lp.testing.runner', 'base', 'test', 'console', 'node',
449+ 'node-event-simulate', 'lp.bugs.official_bug_tags', function(Y) {
450+
451+var suite = new Y.Test.Suite("Official Bug Tags Tests");
452+var module = Y.lp.bugs.official_bug_tags;
453+
454+
455+suite.add(new Y.Test.Case({
456+ name: 'Official Bug Tags',
457+
458+ setUp: function() {
459+ this.fixture = Y.one('#fixture');
460+ var tags_form = Y.Node.create(
461+ Y.one('#form-template').getContent());
462+ this.fixture.appendChild(tags_form);
463+ },
464+
465+ tearDown: function() {
466+ if (this.fixture !== null) {
467+ this.fixture.empty();
468+ }
469+ delete this.fixture;
470+ },
471+
472+ test_setup_bug_tags_table: function() {
473+ // The bug tags table is visible and the html form is not.
474+ module.setup_official_bug_tag_management();
475+ html_form = Y.one('[name=launchpadform]');
476+ tags_table = Y.one('#layout-table');
477+ Y.Assert.areEqual('none', html_form.getStyle('display'));
478+ Y.Assert.areEqual('block', tags_table.getStyle('display'));
479+ },
480+
481+ test_on_new_tag_add_display_error: function() {
482+ // Adding a tag with an invalid name displays a message.
483+ module.setup_official_bug_tag_management();
484+ Y.one('#new-tag-text').set('value', 'me!');
485+ var new_tag_button = Y.one('#new-tag-add');
486+ new_tag_button.set('disabled', false);
487+ new_tag_button.simulate('click');
488+ var message_overlay = Y.one('.official-tag-error-message');
489+ Y.Assert.isNotNull(message_overlay);
490+ var tag = message_overlay.one(".official-tag-error-message-value");
491+ Y.Assert.isTrue(message_overlay.get('text').indexOf('me!') !== -1);
492+ }
493+}));
494+
495+Y.lp.testing.Runner.run(suite);
496+});
497
498=== modified file 'lib/lp/bugs/templates/bugtarget-macros-filebug.pt'
499--- lib/lp/bugs/templates/bugtarget-macros-filebug.pt 2012-04-20 01:12:05 +0000
500+++ lib/lp/bugs/templates/bugtarget-macros-filebug.pt 2012-04-28 03:06:27 +0000
501@@ -44,12 +44,10 @@
502 </td>
503 </tr>
504
505- <noscript>
506 <tal:product tal:define="widget nocall:view/widgets/product|nothing"
507 tal:condition="widget">
508 <metal:widget use-macro="context/@@launchpad_form/widget_row" />
509 </tal:product>
510- </noscript>
511
512 <metal:summary tal:define="widget nocall:view/widgets/title">
513 <metal:row use-macro="context/@@launchpad_form/widget_row" />
514@@ -66,7 +64,7 @@
515 <td colspan="2">
516 <fieldset id="filebug-extra-options" class="collapsible">
517 <legend>Extra options</legend>
518- <div class="unseen">
519+ <div>
520 <table>
521 <tal:status
522 define="widget nocall:view/widgets/status|nothing"
523
524=== modified file 'lib/lp/bugs/templates/official-bug-target-manage-tags.pt'
525--- lib/lp/bugs/templates/official-bug-target-manage-tags.pt 2012-02-01 15:31:32 +0000
526+++ lib/lp/bugs/templates/official-bug-target-manage-tags.pt 2012-04-28 03:06:27 +0000
527@@ -17,9 +17,7 @@
528 <div metal:fill-slot="main">
529 <div class="yui-g">
530 <div class="yui-u first">
531- <noscript>
532- <div metal:use-macro="context/@@launchpad_form/form" />
533- </noscript>
534+ <div metal:use-macro="context/@@launchpad_form/form" />
535
536 <script tal:replace="structure view/tags_js_data" />
537 <script type="text/javascript">
538
539=== modified file 'lib/lp/bugs/tests/testfiles/sourceforge-project-fronobulator.html'
540--- lib/lp/bugs/tests/testfiles/sourceforge-project-fronobulator.html 2011-12-19 14:54:37 +0000
541+++ lib/lp/bugs/tests/testfiles/sourceforge-project-fronobulator.html 2012-04-28 03:06:27 +0000
542@@ -23,48 +23,6 @@
543
544 <link rel="stylesheet" type="text/css" href="http://static.sourceforge.net/css/phoneix/keepsake.css?secure=0&amp;20080417-1657" media="screen" />
545 <link rel="stylesheet" type="text/css" href="http://static.sourceforge.net/css/phoneix/print.css?secure=0&amp;20080417-1657" media="print" />
546-<!-- <script>
547-function utmx_section(){}function utmx(){}
548-(function(){var k='1291903540',d=document,l=d.location,c=d.cookie;function f(n){
549-if(c){var i=c.indexOf(n+'=');if(i>-1){var j=c.indexOf(';',i);return c.substring(i+n.
550-length+1,j<0?c.length:j)}}}var x=f('__utmx'),xx=f('__utmxx'),h=l.hash;
551-d.write('<sc'+'ript src="'+
552-'http'+(l.protocol=='https:'?'s://ssl':'://www')+'.google-analytics.com'
553-+'/siteopt.js?v=1&utmxkey='+k+'&utmx='+(x?x:'')+'&utmxx='+(xx?xx:'')+'&utmxtime='
554-+new Date().valueOf()+(h?'&utmxhash='+escape(h.substr(1)):'')+
555-'" type="text/javascript" charset="utf-8"></sc'+'ript>')})();
556-</script>
557-<script>
558- var originalUrl = document.location.href;
559- var urlParamsTokenPos = originalUrl.indexOf('?');
560- var hashTokenPos = originalUrl.indexOf('#');
561- var hashString = '';
562-
563- if(hashTokenPos > -1){
564- baseUrl = originalUrl.substring(0, hashTokenPos);
565- hashString = originalUrl.substring(hashTokenPos)
566- } else {
567- baseUrl = originalUrl;
568- }
569-
570-test_url = baseUrl;
571-if(test_url.match("/$") == null && test_url.match(".php$") == null){
572- test_url += "/";
573-}
574-if(urlParamsTokenPos > -1){
575- test_url += "&";
576-} else {
577- test_url += "?";
578-}
579-test_url += "abmode=1";
580-test_url += hashString;
581-
582-</script>
583-<script>utmx_section("Redirect Control")</script>
584-<script>
585- //document.location = test_url;
586-</script>
587-</noscript> -->
588 <script type="text/javascript" src="http://static.sourceforge.net/include/jquery/jquery-1.2.6.min.js"></script>
589
590
591@@ -628,9 +586,6 @@
592 </script>
593
594 <script type="text/javascript" src="//secure.quantserve.com/quant.js"></script>
595-<noscript>
596-<img src="//secure.quantserve.com/pixel/p-93nPV3-Eb4x22.gif" style="display: none;" border="0" height="1" width="1" alt="Quantcast"/>
597-</noscript>
598 <!-- End Quantcast tag -->
599
600
601
602=== modified file 'lib/lp/bugs/tests/testfiles/sourceforge-tracker-5570.html'
603--- lib/lp/bugs/tests/testfiles/sourceforge-tracker-5570.html 2011-12-19 14:54:37 +0000
604+++ lib/lp/bugs/tests/testfiles/sourceforge-tracker-5570.html 2012-04-28 03:06:27 +0000
605@@ -348,9 +348,6 @@
606 </script>
607
608 <script type="text/javascript" src="//secure.quantserve.com/quant.js"></script>
609-<noscript>
610-<img src="//secure.quantserve.com/pixel/p-93nPV3-Eb4x22.gif" style="display: none;" border="0" height="1" width="1" alt="Quantcast"/>
611-</noscript>
612 <!-- End Quantcast tag -->
613
614
615
616=== modified file 'lib/lp/registry/stories/project/xx-project-edit.txt'
617--- lib/lp/registry/stories/project/xx-project-edit.txt 2012-01-15 13:32:27 +0000
618+++ lib/lp/registry/stories/project/xx-project-edit.txt 2012-04-28 03:06:27 +0000
619@@ -61,11 +61,8 @@
620 Project group information
621 Maintainer:
622 Sample Person
623- Edit
624 Driver:
625 Not yet selected
626- Edit
627- Driver help
628 Bug tracker:
629 The Mozilla.org Bug Tracker
630 Download RDF metadata
631
632=== modified file 'lib/lp/registry/templates/product-index.pt'
633--- lib/lp/registry/templates/product-index.pt 2012-03-20 03:03:07 +0000
634+++ lib/lp/registry/templates/product-index.pt 2012-04-28 03:06:27 +0000
635@@ -14,11 +14,6 @@
636
637 <head>
638 <tal:head-epilogue metal:fill-slot="head_epilogue">
639- <noscript>
640- <style type="text/css">
641- div.collapsible, div.collapsed {display: block;}
642- </style>
643- </noscript>
644 <tal:uses_launchpad_bugtracker
645 condition="context/bug_tracking_usage/enumvalue:LAUNCHPAD">
646 <script type="text/javascript">