Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 9 additions & 3 deletions engine/app/assets/stylesheets/coplan/application.css
Original file line number Diff line number Diff line change
Expand Up @@ -555,6 +555,10 @@ img, svg {
letter-spacing: 0.025em;
}

/* Status badge that opens the status menu (decorated by status_menu_controller). */
.badge--menu { cursor: pointer; }
.badge--menu::after { content: " ▾"; opacity: 0.7; }

.badge--brainstorm { background: var(--color-status-brainstorm-bg); color: var(--color-status-brainstorm); }
.badge--considering { background: var(--color-status-considering-bg); color: var(--color-status-considering); }
.badge--developing { background: var(--color-status-developing-bg); color: var(--color-status-developing); }
Expand Down Expand Up @@ -1480,18 +1484,20 @@ img.avatar {
right: 0;
top: 100%;
margin-top: var(--space-xs);
padding: var(--space-xs) 0;
background: var(--color-surface);
border: 1px solid var(--color-border);
border-radius: var(--radius);
box-shadow: var(--shadow-lg);
min-width: 200px;
min-width: 220px;
z-index: 100;
}

.dropdown__item {
display: block;
width: 100%;
padding: var(--space-sm) var(--space-md);
padding: 0.625rem var(--space-md);
line-height: 1.45;
text-align: left;
background: none;
border: none;
Expand Down Expand Up @@ -3083,7 +3089,7 @@ body:not(:has(.comment-toolbar)) .web-push-banner {
display: block;
font-size: var(--text-xs);
color: var(--color-text-muted);
margin-top: 2px;
margin-top: var(--space-xs);
}

/* Markdown editor (edit_content page) */
Expand Down
25 changes: 0 additions & 25 deletions engine/app/javascript/controllers/coplan/dropdown_controller.js

This file was deleted.

Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
import { Controller } from "@hotwired/stimulus"

// Opens the status menu from the status badge in the plan header. The badge
// lives inside the broadcast-replaced #plan-header (rendered without a
// current_user), so the policy-gated menu renders outside it and this
// controller spans both: the badge only becomes interactive when a menu is
// present for this viewer.
export default class extends Controller {
static targets = ["badge", "menu"]

badgeTargetConnected(badge) {
if (!this.hasMenuTarget) return
badge.setAttribute("role", "button")
badge.setAttribute("tabindex", "0")
badge.setAttribute("title", "Change status")
badge.classList.add("badge--menu")
}

toggle(event) {
if (!this.hasMenuTarget) return
const menu = this.menuTarget
if (menu.style.display === "none") {
const rect = event.currentTarget.getBoundingClientRect()
menu.style.position = "fixed"
menu.style.top = `${rect.bottom + 4}px`
menu.style.left = `${rect.left}px`
menu.style.right = "auto"
menu.style.display = ""
} else {
menu.style.display = "none"
}
}

close(event) {
if (!this.hasMenuTarget) return
const insideBadge = this.hasBadgeTarget && this.badgeTarget.contains(event.target)
if (!this.menuTarget.contains(event.target) && !insideBadge) {
this.menuTarget.style.display = "none"
}
}

connect() {
this._closeHandler = this.close.bind(this)
document.addEventListener("click", this._closeHandler)
}

disconnect() {
document.removeEventListener("click", this._closeHandler)
}
}
4 changes: 3 additions & 1 deletion engine/app/views/coplan/plans/_header.html.erb
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,9 @@
<div>
<h1><%= plan.title %></h1>
<div class="text-sm text-muted mt-md">
<%= user_avatar(plan.created_by_user) %> <%= plan.created_by_user.name %> · v<%= plan.current_revision %> · <span class="badge badge--<%= plan.status %>"><%= plan.status %></span>
<%= user_avatar(plan.created_by_user) %> <%= plan.created_by_user.name %> · v<%= plan.current_revision %> · <span class="badge badge--<%= plan.status %> badge--status"
data-coplan--status-menu-target="badge"
data-action="click->coplan--status-menu#toggle keydown.enter->coplan--status-menu#toggle"><%= plan.status %></span>
<% if plan.plan_type %>
· <span class="badge badge--type"><%= plan.plan_type.name %></span>
<% end %>
Expand Down
58 changes: 27 additions & 31 deletions engine/app/views/coplan/plans/show.html.erb
Original file line number Diff line number Diff line change
Expand Up @@ -11,40 +11,36 @@

<%= turbo_stream_from @plan %>

<div data-controller="coplan--presence" data-coplan--presence-plan-id-value="<%= @plan.id %>">
<%= render partial: "coplan/plans/header", locals: { plan: @plan } %>
</div>
<div data-controller="coplan--status-menu">
<div data-controller="coplan--presence" data-coplan--presence-plan-id-value="<%= @plan.id %>">
<%= render partial: "coplan/plans/header", locals: { plan: @plan } %>
</div>

<%# Owner affordances live outside the broadcast-replaced #plan-header so
they can be policy-gated server-side (broadcast renders have no
current_user). %>
<% if allowed_to?(@plan, :edit_content?) || allowed_to?(@plan, :update_status?) %>
<div class="plan-actions">
<% if allowed_to?(@plan, :edit_content?) %>
<%# Owner affordances live outside the broadcast-replaced #plan-header so
they can be policy-gated server-side (broadcast renders have no
current_user). The status menu opens from the header's status badge —
see status_menu_controller.js. %>
<% if allowed_to?(@plan, :update_status?) %>
<div class="dropdown__menu" data-coplan--status-menu-target="menu" style="display: none;">
<% CoPlan::Plan::STATUSES.each do |status| %>
<% next if status == @plan.status %>
<%= button_to update_status_plan_path(@plan, status: status),
method: :patch,
class: "dropdown__item",
form: (status_publishes?(@plan, status) ? { data: { turbo_confirm: "Moving to “#{status}” publishes this plan to everyone in the org. Continue?" } } : {}) do %>
<span class="badge badge--<%= status %>"><%= status %></span>
<span class="plan-actions__hint"><%= plan_status_hint(status) %></span>
<% end %>
<% end %>
</div>
<% end %>
<% if allowed_to?(@plan, :edit_content?) %>
<div class="plan-actions">
<%= link_to edit_content_plan_path(@plan), class: "btn btn--secondary btn--sm" do %>✏️ Edit content<% end %>
<%= link_to edit_plan_path(@plan), class: "btn btn--secondary btn--sm" do %>Title &amp; tags<% end %>
<% end %>
<% if allowed_to?(@plan, :update_status?) %>
<div class="dropdown" data-controller="coplan--dropdown">
<button type="button" class="btn btn--secondary btn--sm" data-action="coplan--dropdown#toggle">
Move to… ▾
</button>
<div class="dropdown__menu" data-coplan--dropdown-target="menu" style="display: none;">
<% CoPlan::Plan::STATUSES.each do |status| %>
<% next if status == @plan.status %>
<%= button_to update_status_plan_path(@plan, status: status),
method: :patch,
class: "dropdown__item",
form: (status_publishes?(@plan, status) ? { data: { turbo_confirm: "Moving to “#{status}” publishes this plan to everyone in the org. Continue?" } } : {}) do %>
<span class="badge badge--<%= status %>"><%= status %></span>
<span class="plan-actions__hint"><%= plan_status_hint(status) %></span>
<% end %>
<% end %>
</div>
</div>
<% end %>
</div>
<% end %>
</div>
<% end %>
</div>

<% active_tab = %w[references attachments history].include?(params[:tab]) ? params[:tab] : "content" %>
<div class="plan-tabs" data-controller="coplan--tabs">
Expand Down
8 changes: 5 additions & 3 deletions spec/system/human_editing_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -54,10 +54,10 @@ def sign_in(user)
expect(page).to have_field("content", with: /Preview me/)
end

it "changes status from the dropdown" do
it "changes status by clicking the status badge" do
visit plan_path(plan)

click_button "Move to…"
find("#plan-header .badge--status[role='button']").click
within(".dropdown__menu") { click_button "developing" }

expect(page).to have_content("Status updated to developing.")
Expand All @@ -72,6 +72,8 @@ def sign_in(user)
visit plan_path(plan)
expect(page).to have_content("Editable Plan")
expect(page).not_to have_link("Edit content")
expect(page).not_to have_button("Move to…")
# The status badge stays a plain label — no menu, no button affordance.
expect(page).not_to have_css(".dropdown__menu")
expect(page).not_to have_css(".badge--menu")
end
end
Loading