Skip to content
37 changes: 37 additions & 0 deletions app/javascript/controllers/better_together/uploads_controller.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
import { Controller } from "@hotwired/stimulus"

export default class extends Controller {
static targets = ["search", "sort", "list", "item"]

filter() {
const query = this.searchTarget.value.toLowerCase()
this.itemTargets.forEach((item) => {
const name = item.dataset.name.toLowerCase()
item.classList.toggle("d-none", !name.includes(query))
})
}

sort() {
const key = this.sortTarget.value
const items = this.itemTargets.slice().sort((a, b) => {
if (key === "name") {
return a.dataset.name.localeCompare(b.dataset.name)
}
if (key === "created_at") {
return new Date(b.dataset.createdAt) - new Date(a.dataset.createdAt)
}
return 0
})
items.forEach((item) => this.listTarget.appendChild(item))
}

copy(event) {
const url = event.currentTarget.dataset.url
navigator.clipboard?.writeText(url)
}

insert(event) {
const signedId = event.currentTarget.dataset.signedId
this.dispatch("insert", { detail: { signedId } })
}
}
43 changes: 41 additions & 2 deletions app/views/better_together/uploads/index.html.erb
Original file line number Diff line number Diff line change
@@ -1,2 +1,41 @@
<h1>Files#index</h1>
<p>Find me in app/views/better_together/files/index.html.erb</p>
<% content_for :page_title do %>
<%= BetterTogether::Upload.model_name.human.pluralize %>
<% end %>

<div class="container mt-4" data-controller="better_together--uploads">
<div class="row mb-3">
<div class="col-sm">
<input type="search" class="form-control" placeholder="Search uploads" data-better_together--uploads-target="search" data-action="input->better_together--uploads#filter">
</div>
<div class="col-sm-auto">
<select class="form-select" data-better_together--uploads-target="sort" data-action="change->better_together--uploads#sort">
<option value="created_at">Newest</option>
<option value="name">Name</option>
</select>
</div>
</div>

<div class="row g-3" data-better_together--uploads-target="list">
<% @uploads.each do |upload| %>
<div class="col-md-3" data-better_together--uploads-target="item" data-name="<%= upload.name.to_s %>" data-created-at="<%= upload.created_at.iso8601 %>">
<div class="card h-100">
<% if upload.file.attached? %>
<%= image_tag upload.file.variant(resize_to_limit: [200, 200]), class: "card-img-top", alt: upload.name %>
<% end %>
<div class="card-body d-flex flex-column">
<h6 class="card-title text-truncate"><%= upload.name %></h6>
<ul class="list-unstyled small mb-3">
<li><strong>Filename:</strong> <%= upload.filename %></li>
<li><strong>Size:</strong> <%= number_to_human_size(upload.byte_size) %></li>
<li><strong>Type:</strong> <%= upload.content_type %></li>
</ul>
<div class="mt-auto btn-group">
<button class="btn btn-sm btn-outline-secondary" data-action="better_together--uploads#copy" data-url="<%= url_for(upload.file) %>">Copy URL</button>
<button class="btn btn-sm btn-primary" data-action="better_together--uploads#insert" data-signed-id="<%= upload.file.blob.signed_id %>">Insert</button>
</div>
</div>
</div>
</div>
<% end %>
</div>
</div>
47 changes: 47 additions & 0 deletions spec/features/uploads/gallery_spec.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
# frozen_string_literal: true

require 'rails_helper'

RSpec.describe 'uploads gallery', type: :feature, js: true do # rubocop:disable Metrics/BlockLength
include BetterTogether::DeviseSessionHelpers

before do
configure_host_platform
login_as_platform_manager
@creator = BetterTogether::User.find_by(email: '[email protected]').person
end

def create_upload(name, creator:, created_at: Time.current)
create(:better_together_upload, name:, creator:, created_at:).tap do |upload|
upload.file.attach(io: StringIO.new('stub'), filename: "#{name}.txt", content_type: 'text/plain')
end
end

scenario 'searches, sorts, and copies upload urls' do
create_upload('Alpha', creator: @creator, created_at: 2.days.ago)
create_upload('Beta', creator: @creator, created_at: 1.day.ago)

visit file_index_path(locale: I18n.default_locale)

expect(page.all('[data-better_together--uploads-target="item"] .card-title').map(&:text))
.to eq(%w[Beta Alpha])

fill_in placeholder: 'Search uploads', with: 'Al'
expect(page).to have_selector('[data-name="Alpha"]', visible: true)
expect(page).to have_selector('[data-name="Beta"].d-none', visible: :all)

find('select[data-better_together--uploads-target="sort"]').select('Name')
expect(page.all('[data-better_together--uploads-target="item"] .card-title').map(&:text))
.to eq(%w[Alpha Beta])

copy_button = find('button', text: 'Copy URL', match: :first)
page.execute_script <<~JS
window.copiedText = null;
Object.defineProperty(navigator, "clipboard", {
value: { writeText: function(t){ window.copiedText = t } }
});
JS
copy_button.click
expect(page.evaluate_script('window.copiedText')).to eq(copy_button['data-url'])
end
end
5 changes: 4 additions & 1 deletion spec/support/capybara.rb
Original file line number Diff line number Diff line change
Expand Up @@ -20,11 +20,14 @@
)
# Generate a unique temporary directory for each session to avoid conflicts
options.add_argument("--user-data-dir=#{Dir.mktmpdir}")
options.binary = '/usr/bin/chromium-browser'
service = Selenium::WebDriver::Service.chrome(path: '/usr/bin/chromedriver')

Capybara::Selenium::Driver.new(
app,
browser: :chrome,
options: options
options: options,
service: service
)
end

Expand Down
Loading