migrate edit update and destroy to jQuery and refactor it

test for changing state is wip, forgot to mark it with @wip
This commit is contained in:
Reinier Balt 2010-10-07 23:24:50 +02:00
parent ee4ef4ad42
commit 35453acd57
12 changed files with 198 additions and 105 deletions

View file

@ -125,9 +125,7 @@ class ProjectsController < ApplicationController
@project_not_done_counts[@project.id] = @project.reload().not_done_todos_including_hidden.count @project_not_done_counts[@project.id] = @project.reload().not_done_todos_including_hidden.count
end end
@contexts = current_user.contexts @contexts = current_user.contexts
@active_projects_count = current_user.projects.active.count update_state_counts
@hidden_projects_count = current_user.projects.hidden.count
@completed_projects_count = current_user.projects.completed.count
init_data_for_sidebar init_data_for_sidebar
render :template => 'projects/update.js.erb' render :template => 'projects/update.js.erb'
return return
@ -166,11 +164,12 @@ class ProjectsController < ApplicationController
def destroy def destroy
@project.recurring_todos.each {|rt| rt.remove_from_project!} @project.recurring_todos.each {|rt| rt.remove_from_project!}
@project.destroy @project.destroy
@active_projects_count = current_user.projects.active.count
@hidden_projects_count = current_user.projects.hidden.count
@completed_projects_count = current_user.projects.completed.count
respond_to do |format| respond_to do |format|
format.js { @down_count = current_user.projects.size } format.js {
@down_count = current_user.projects.size
update_state_counts
}
format.xml { render :text => "Deleted project #{@project.name}" } format.xml { render :text => "Deleted project #{@project.name}" }
end end
end end
@ -201,7 +200,16 @@ class ProjectsController < ApplicationController
end end
protected protected
def update_state_counts
@active_projects_count = current_user.projects.active.count
@hidden_projects_count = current_user.projects.hidden.count
@completed_projects_count = current_user.projects.completed.count
@show_active_projects = @active_projects_count > 0
@show_hidden_projects = @hidden_projects_count > 0
@show_completed_projects = @completed_projects_count > 0
end
def render_projects_html def render_projects_html
lambda do lambda do
@page_title = t('projects.list_projects') @page_title = t('projects.list_projects')

View file

@ -110,6 +110,12 @@ module ApplicationHelper
def link_to_project(project, descriptor = sanitize(project.name)) def link_to_project(project, descriptor = sanitize(project.name))
link_to( descriptor, project_path(project), :title => "View project: #{project.name}" ) link_to( descriptor, project_path(project), :title => "View project: #{project.name}" )
end end
def link_to_edit_project (project, descriptor = sanitize(project.name))
link_to(descriptor,
url_for({:controller => 'projects', :action => 'edit', :id => project.id}),
{:id => "link_edit_#{dom_id(project)}", :class => "project_edit_settings"})
end
def link_to_project_mobile(project, accesskey, descriptor = sanitize(project.name)) def link_to_project_mobile(project, accesskey, descriptor = sanitize(project.name))
link_to( descriptor, project_path(project, :format => 'm'), {:title => "View project: #{project.name}", :accesskey => accesskey} ) link_to( descriptor, project_path(project, :format => 'm'), {:title => "View project: #{project.name}", :accesskey => accesskey} )

View file

@ -13,24 +13,15 @@ suppress_edit_button ||= false
<%= link_to_project( project ) %><%= " (" + count_undone_todos_and_notes_phrase(project,"actions") + ")" %> <%= link_to_project( project ) %><%= " (" + count_undone_todos_and_notes_phrase(project,"actions") + ")" %>
</div> </div>
<div class="buttons"> <div class="buttons">
<span class="grey"><%= t('states.' + project.current_state.to_s).upcase %></span> <span class="grey"><%= project.current_state.to_s.upcase %></span>
<a class="delete_project_button"
href="<%= project_path(project, :format => 'js') %>" <a class="delete_project_button" id="delete_project_<%= project.id%> " href="<%= project_path(project, :format => 'js') %>"
title="<%= t('projects.delete_project_title') %> '<%= project.name %>'"><%= image_tag( "blank.png", title="<%= t('projects.delete_project_title') %> '<%= project.name %>'">
:title => t('projects.delete_project'), <%= image_tag( "blank.png", :title => t('projects.delete_project'), :class=>"delete_item") %>
:class=>"delete_item") %></a> </a>
<% unless suppress_edit_button -%> <% unless suppress_edit_button -%>
<%= link_to_remote( <%= link_to_edit_project(project, image_tag( "blank.png", :title => t('projects.edit_project_title'), :class=>"edit_item")) %>
image_tag( "blank.png", :title => t('projects.edit_project_title'), :class=>"edit_item"),
:url => {:controller => 'projects', :action => 'edit', :id => project.id},
:method => 'get',
:with => "'_source_view=#{@source_view}'",
:before => "$('#{dom_id(project)}').block({message:null});",
:complete => "$('#{dom_id(project)}').unblock();enable_rich_interaction();",
:html => {:id => "link_edit_#{dom_id(project)}"}
) %>
<% end -%> <% end -%>
</div> </div>
</div> </div>

View file

@ -14,16 +14,7 @@
<% else -%> <% else -%>
'<%= project.default_tags -%>' as the default tags. '<%= project.default_tags -%>' as the default tags.
<% end -%> <% end -%>
<a class ="project_edit_settings" id="link_edit_<%=dom_id(project)-%>" href="<%=url_for(:controller => 'projects', :action => 'edit', :id => project.id)%>">Edit Project Settings</a> <%= link_to_edit_project(project, "Edit Project Settings") %>
<%#= link_to_remote(
"",
:url => {:controller => 'projects', :action => 'edit', :id => project.id},
:method => 'get',
:with => "'_source_view=#{@source_view}'",
:before => "$('#{dom_id(project)}').block({message: null});",
:complete => "$('#{dom_id(project)}').unblock();enable_rich_interaction();",
:html => {:id => "link_edit_#{dom_id(project)}"}
) %>
</div> </div>
<% unless project.description.blank? -%> <% unless project.description.blank? -%>
<div class="project_description"><%= format_note(project.description) %></div> <div class="project_description"><%= format_note(project.description) %></div>

View file

@ -0,0 +1,12 @@
remove_deleted_project();
ProjectListPage.update_state_count(<%=@active_projects_count%>, <%=@hidden_projects_count%>, <%=@completed_projects_count%>)
ProjectListPage.show_or_hide_state_container(<%= @show_active_projects %>, <%= @show_hidden_projects %>, <%= @show_completed_projects %>);
page_notify('notice', "Deleted project '#{@project.name}'", 5);
set_page_badge(<%=@down_count%>);
function remove_deleted_project() {
$('div#<%=dom_id(@project, "container")%>').slideUp(1000,
function() {
$('div#<%=dom_id(@project, "container")%>').remove()
});
}

View file

@ -1,13 +0,0 @@
page.visual_effect :fade, dom_id(@project, "container"), :duration => 0.5
page.delay(0.5) do
page[dom_id(@project, "container")].remove
page.replace_html "active-projects-count", @active_projects_count
page.replace_html "hidden-projects-count", @hidden_projects_count
page.replace_html "completed-projects-count", @completed_projects_count
page.set_element_visible("list-hidden-projects-container", @hidden_projects_count > 0)
page.set_element_visible("list-active-projects-container", @active_projects_count > 0)
page.set_element_visible("list-completed-projects-container", @completed_projects_count > 0)
end
page.notify :notice, "Deleted project '#{@project.name}'", 5.0
page['badge_count'].replace_html @down_count
page.hide "busy"

View file

@ -1,5 +1,5 @@
newHtml = "<%= escape_javascript(render(:partial => 'project_form', :locals => { :project_form => @project })) %>" newHtml = "<%= escape_javascript(render(:partial => 'project_form', :locals => { :project_form => @project })) %>"
$('div#<%=dom_id(@project, 'edit')%>').html(newHtml); $('div#<%=dom_id(@project, 'edit')%>').html(newHtml);
$('div#<%=dom_id(@project)%>').hide(); $('div#<%=dom_id(@project)%>').hide();
$('div#<%=dom_id(@project, 'edit')%>').show(); $('div#<%=dom_id(@project, 'edit')%>').show(500);
$('input.project-name').focus(); $('input.project-name').focus();

View file

@ -1,69 +1,101 @@
<% if @saved -%> <% if @saved -%>
pageNotify('notice', '<%=t('projects.project_saved_status')%>', 5); pageNotify('notice', '<%=t('projects.project_saved_status')%>', 5);
<% if source_view_is :project_list -%> <% if source_view_is :project_list -%>
<% if @state_changed -%> <% if @state_changed -%>
<% else # TODO!!!-%> remove_project();
add_project();
<% else -%>
replace_project();
<% end -%> <% end -%>
<% else # source_view must be :project %>
$('#<%=dom_id(@project, 'edit')%>').hide(); update_sortable();
$('#<%=dom_id(@project, 'container')%>').html("<%= escape_javascript(render(:partial => 'project_settings', :locals => { :project => @project })) %> "); ProjectListPage.update_state_count(<%=@active_projects_count%>, <%=@hidden_projects_count%>, <%=@completed_projects_count%>)
$('#<%=dom_id(@project)%>').show(); ProjectListPage.show_or_hide_state_container(<%= @show_active_projects %>, <%= @show_hidden_projects %>, <%= @show_completed_projects %>);
$('input#todo_project_name').val("<%= escape_javascript(@project.name)%>");
<% else # assume source_view :project %>
remove_project_edit_form();
update_and_show_project_settings();
TracksForm.set_project_name("<%= escape_javascript(@project.name)%>");
<% if @project.default_context %> <% if @project.default_context %>
$('input#todo_context_name').val("<%= escape_javascript(@project.default_context.name)%>"); TracksForm.set_context_name_and_default_context_name("<%= escape_javascript(@project.default_context.name)%>");
$('input[name=default_context_name]').val('<%= escape_javascript(@project.default_context.name)%>');
<% end %> <% end %>
<% if @project.default_tags %> <% if @project.default_tags %>
$('input#tag_list').val("<%= escape_javascript(@project.default_tags)%>"); TracksForm.set_tag_list("<%= escape_javascript(@project.default_tags)%>");
<% end %> <% end %>
update_sidebar();
<% end %> <% end %>
$('#default_project_name_id').val('<%= escape_javascript(@project.name)%>');
$('input#todo_project_name').val("<%= escape_javascript(@project.name)%>"); TracksForm.set_project_name_and_default_project_name("<%= escape_javascript(@project.name)%>");
$('#project_name').html("<%= escape_javascript(@project.name)%>");
$('#sidebar').html("<%= escape_javascript(render(:file => 'sidebar/sidebar.html.erb')) %>");
<% else -%> <% else -%>
$('div#error_status').html("<%= escape_javascript(error_messages_for('project')) %>"); show_errors();
$('div#error_status').show();
<% end %> <% end %>
enable_rich_interaction(); enable_rich_interaction();
<%# *page << "console.log(\"hello\");"%> function show_errors() {
<%# *if @saved%> $('div#error_status').html(html_for_error_messages());
<%# *status_message = 'Project saved'%> $('div#error_status').show();
<%# *page.notify :notice, status_message, 5.0%> }
<%# *if source_view_is :project_list%>
<%# *if @state_changed%>
<%#*page[dom_id(@project, 'container')].remove%>
<%#*page.insert_html :bottom, "list-#{@project.state}-projects", :partial => 'project_listing', :object => @project%>
<%# *else%>
<%#*page.replace_html dom_id(@project, 'container'), :partial => 'project_listing', :object => @project%>
<%# *end%>
<%#*page.sortable "list-#{@project.state}-projects", get_listing_sortable_options("list-#{@project.state}-projects")%>
<%#*page.replace_html "active-projects-count", @active_projects_count%>
<%#*page.replace_html "hidden-projects-count", @hidden_projects_count%>
<%#*page.replace_html "completed-projects-count", @completed_projects_count%>
<%#*page.set_element_visible("list-hidden-projects-container", @hidden_projects_count > 0)%> function remove_project_edit_form() {
<%#*page.set_element_visible("list-active-projects-container", @active_projects_count > 0)%> $('#<%=dom_id(@project, 'edit')%>').hide(500);
<%#*page.set_element_visible("list-completed-projects-container", @completed_projects_count > 0)%> }
<%# *else%>
<%# *page[dom_id(@project, 'edit')].hide%>
<%# *page.replace_html dom_id(@project, 'container'), :partial => 'project_settings', :locals => { :project => @project }%>
<%# *page[dom_id(@project)].show%>
<%# *page['todo_context_name'].value = @project.default_context.name if @project.default_context%> function update_and_show_project_settings() {
<%# *page['#todo_project_name'].value = @project.name%> $('#<%=dom_id(@project, 'container')%>').html(html_for_project_settings());
<%# *page['tag_list'].value = @project.default_tags if @project.default_tags%> $('#<%=dom_id(@project)%>').show();
<%# *page << "$('input[name=default_context_name]').val('#{@project.default_context.name}');" if @project.default_context%> }
<%# *end%>
<%# *page['default_project_name_id'].value = @project.name%> function update_sidebar() {
<%# *page['todo_project_name'].value = @project.name%> $('#sidebar').html();
<%# *page.replace_html "project_name", @project.name%> }
<%# *page.replace_html "sidebar", :file => 'sidebar/sidebar.html.erb'%> function update_sortable() {
<%# *else%> <%#* page.sortable "list-#{@project.state}-projects", get_listing_sortable_options("list-#{@project.state}-projects")%>
<%# *page.show 'error_status'%> console.log("Pending: update_sortable() on update project");
<%# *page.replace_html 'error_status', "#{error_messages_for('project')}"%> }
<%# *end%>
<%# *page << "enable_rich_interaction();"%> function replace_project() {
$('#<%=dom_id(@project, 'container')%>').fadeOut(500, function() {
$('#<%=dom_id(@project, 'container')%>').html(html_for_project_listing());
$('#<%=dom_id(@project, 'container')%>').fadeIn(500);
});
}
function remove_project() {
$('#<%=dom_id(@project, 'container')%>').slideUp(500, function() {
$('#<%=dom_id(@project, 'container')%>').remove();});
}
function add_project() {
$('#list-<%=@project.state%>-projects').append(html_for_project_listing);
}
<%
# the following functions return empty string if rendering the partial is not
# necessary, for example the sidebar is not on the project list page, so do not
# render it into the function.
-%>
function html_for_project_listing() {
return "<%= source_view_is(:project_list) ? escape_javascript(render(:partial => 'project_listing', :locals => { :project_listing => @project })) : "" %>";
}
function html_for_sidebar() {
return "<%= source_view_is(:project) ? escape_javascript(render(:file => 'sidebar/sidebar.html.erb')) : "" %>";
}
function html_for_project_settings() {
return "<%= source_view_is(:project) ? escape_javascript(render(:partial => 'project_settings', :locals => { :project => @project })) : "" %>";
}
function html_for_error_messages() {
return "<%= escape_javascript(error_messages_for('project')) %>";
}

View file

@ -17,7 +17,7 @@ Feature: Manage the list of projects
When I go to the projects page When I go to the projects page
Then I should see "manage me" Then I should see "manage me"
And I should see "upgrade jquery" And I should see "upgrade jquery"
And the badge should show 2 And the badge should show 3
Scenario: Clicking on a project takes me to the project page Scenario: Clicking on a project takes me to the project page
When I go to the projects page When I go to the projects page
@ -27,12 +27,26 @@ Feature: Manage the list of projects
@selenium @selenium
Scenario: Editing a project name will update the list Scenario: Editing a project name will update the list
When I go to the projects page When I go to the projects page
And I edit the project name for "manage me" to "manage him" And I edit the project name of "manage me" to "manage him"
Then I should see "manage him" Then I should see "manage him"
Scenario: Dragging a project to change list order of projects @selenium
Scenario: Deleting a project will remove it from the list Scenario: Deleting a project will remove it from the list
When I go to the projects page
And I delete project "manage me"
Then I should not see "manage me"
And the badge should show 2
And the project list badge for "active" projects should show 2
@selenium
Scenario: Changing project state will move project to other state list Scenario: Changing project state will move project to other state list
When I go to the projects page
Then the project "manage me" should be in state list "active"
When I edit the project state of "manage me" to "hidden"
Then the project "manage me" should not be in state list "active"
And the project "manage me" should be in state list "hidden"
Scenario: Dragging a project to change list order of projects
Scenario: Adding a new project Scenario: Adding a new project
Scenario: Adding a new project and take me to the project page Scenario: Adding a new project and take me to the project page
Scenario: Hiding and unhiding the new project form Scenario: Hiding and unhiding the new project form

View file

@ -0,0 +1,14 @@
When /^I delete project "([^"]*)"$/ do |project_name|
# from the project list page
project = @current_user.projects.find_by_name(project_name)
project.should_not be_nil
click_link "delete_project_#{project.id}"
selenium.get_confirmation.should == "Are you sure that you want to delete the project '#{project_name}'?"
wait_for do
!selenium.is_element_present("delete_project_#{project.id}")
end
end
Then /^the project list badge for "([^"]*)" projects should show (\d+)$/ do |state_name, count|
selenium.get_text("css=span##{state_name}-projects-count").should == count
end

View file

@ -42,12 +42,14 @@ end
When /^I edit the project name to "([^\"]*)"$/ do |new_title| When /^I edit the project name to "([^\"]*)"$/ do |new_title|
click_link "link_edit_project_#{@project.id}" click_link "link_edit_project_#{@project.id}"
# no need to wait for the form because the AJAX loading should not be async!
fill_in "project[name]", :with => new_title fill_in "project[name]", :with => new_title
# changed to make sure selenium waits until the saving has a result either # changed to make sure selenium waits until the saving has a result either
# positive or negative. Was: :element=>"flash", :text=>"Project saved" # positive or negative. Was: :element=>"flash", :text=>"Project saved"
# we may need to change it back if you really need a positive outcome, i.e. # we may need to change it back if you really need a positive outcome, i.e.
# this step needs to fail if the project was not saved succesfully # this step needs to fail if the project was not saved successfully
selenium.click "submit_project_#{@project.id}", selenium.click "submit_project_#{@project.id}",
:wait_for => :text, :wait_for => :text,
:text => /(Project saved|1 error prohibited this project from being saved)/ :text => /(Project saved|1 error prohibited this project from being saved)/

View file

@ -26,6 +26,37 @@ var TracksForm = {
toggle_overlay: function () { toggle_overlay: function () {
el = document.getElementById("overlay"); el = document.getElementById("overlay");
el.style.visibility = (el.style.visibility == "visible") ? "hidden" : "visible"; el.style.visibility = (el.style.visibility == "visible") ? "hidden" : "visible";
},
set_project_name: function (name) {
$('input#todo_project_name').val(name);
},
set_context_name_and_default_context_name: function (name) {
$('input#todo_context_name').val(name);
$('input[name=default_context_name]').val(name);
},
set_project_name_and_default_project_name: function (name) {
$('#default_project_name_id').val(name);
$('input#todo_project_name').val();
$('#project_name').html(name);
},
set_tag_list: function (name) {
$('input#tag_list').val(name);
}
}
var ProjectListPage = {
update_state_count: function (active, hidden, completed) {
$('#active-projects-count').html(active);
$('#hidden-projects-count').html(hidden);
$('#completed-projects-count').html(completed);
},
show_or_hide_state_container: function (show_active, show_hidden, show_completed) {
active = $('#list-active-projects-container');
hidden = $('#list-hidden-projects-container');
completed = $('#list-completed-projects-container');
if (show_active) { active.show(); } else { active.hide(); }
if (show_hidden) { hidden.show(); } else { hidden.hide(); }
if (show_completed) { completed.show(); } else { completed.hide(); }
} }
} }
@ -282,10 +313,11 @@ function enable_rich_interaction(){
$('h2#project_name').editable(save_project_name, {style: 'padding:0px', submit: "OK"}); $('h2#project_name').editable(save_project_name, {style: 'padding:0px', submit: "OK"});
/* set behavior for edit project settings link */ /* set behavior for edit project settings link */
$("a.project_edit_settings").live('click', function (ev) { $("a.project_edit_settings").live('click', function (ev) {
$.ajax({ $.ajax({
url: this.href, url: this.href,
async: true,
project_dom_id: 'project_'+this.id, project_dom_id: 'project_'+this.id,
dataType: 'script', dataType: 'script',
beforeSend: function() {$(this.project_dom_id).block({message: null});}, beforeSend: function() {$(this.project_dom_id).block({message: null});},
@ -314,7 +346,7 @@ function setup_auto_refresh(interval){
}); });
} }
function pageNotify(type, message, fade_duration_in_sec) { function page_notify(type, message, fade_duration_in_sec) {
flash = $('h4#flash'); flash = $('h4#flash');
flash.html("<h4 id=\'flash\' class=\'alert "+type+"\'>"+message+"</h4>"); flash.html("<h4 id=\'flash\' class=\'alert "+type+"\'>"+message+"</h4>");
flash = $('h4#flash'); flash = $('h4#flash');
@ -322,6 +354,10 @@ function pageNotify(type, message, fade_duration_in_sec) {
flash.fadeOut(fade_duration_in_sec*1000); flash.fadeOut(fade_duration_in_sec*1000);
} }
function set_page_badge(count) {
$('#badge_count').html(count);
}
function setup_periodic_check(url_for_check, interval_in_sec, method) { function setup_periodic_check(url_for_check, interval_in_sec, method) {
ajaxMethod = "GET" ajaxMethod = "GET"
if (method) { ajaxMethod = method; } if (method) { ajaxMethod = method; }