Skip to content

Commit

Permalink
Add sortable feature to ui/table component and update preview toggles
Browse files Browse the repository at this point in the history
- Integrate sorting rows functionality with the 
`sortable_controller.js`.
- Enhanced the preview section to allow enabling and disabling all table
  functionalities, including sorting, search, filters, batch actions,
  and scopes.
  • Loading branch information
rainerdema committed Nov 29, 2023
1 parent 1a33703 commit a0b8a40
Show file tree
Hide file tree
Showing 5 changed files with 129 additions and 30 deletions.
11 changes: 10 additions & 1 deletion admin/app/components/solidus_admin/ui/table/component.html.erb
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
data-controller="<%= stimulus_id %>"
data-<%= stimulus_id %>-selected-row-class="bg-gray-15"
data-<%= stimulus_id %>-mode-value="<%= initial_mode %>"
data-<%= stimulus_id %>-sortable-value="<%= @sortable.present? %>"
data-action="
<%= component("ui/table/ransack_filter").stimulus_id %>:search-><%= stimulus_id %>#search
<%= component("ui/table/ransack_filter").stimulus_id %>:showSearch-><%= stimulus_id %>#showSearch
Expand Down Expand Up @@ -122,13 +123,21 @@
</thead>
<% end %>

<tbody class="bg-white text-3.5 line-[150%] text-black">
<tbody
class="bg-white text-3.5 line-[150%] text-black"
data-<%= stimulus_id %>-target="tableBody"
<%= "data-controller=sortable" if @sortable&.url %>
<%= "data-sortable-param-value=#{@sortable.param}" if @sortable&.param %>
<%= "data-sortable-handle-value=#{@sortable.handle}" if @sortable&.handle %>
<%= "data-sortable-animation-value=#{@sortable.animation}" if @sortable&.animation %>
>
<% @data.rows.each do |row| %>
<tr
class="border-b border-gray-100 last:border-0 hover:bg-gray-50 cursor-pointer <%= 'bg-gray-15 text-gray-700' if @data.fade&.call(row) %>"
<% if @data.url %>
data-action="click-><%= stimulus_id %>#rowClicked"
data-<%= stimulus_id %>-url-param="<%= @data.url.call(row) %>"
<%= "data-sortable-url=#{@sortable.url.call(row)}" if @sortable&.url %>
<% end %>
>
<% @data.columns.each do |column| %>
Expand Down
9 changes: 9 additions & 0 deletions admin/app/components/solidus_admin/ui/table/component.js
Original file line number Diff line number Diff line change
Expand Up @@ -13,12 +13,14 @@ export default class extends Controller {
"filterToolbar",
"defaultHeader",
"batchHeader",
"tableBody",
"selectedRowsCount",
]

static classes = ["selectedRow"]
static values = {
mode: { type: String, default: "scopes" },
sortable: { type: Boolean, default: false },
}

initialize() {
Expand Down Expand Up @@ -129,6 +131,13 @@ export default class extends Controller {
checkbox.closest("tr").classList.toggle(this.selectedRowClass, checkbox.checked),
)

// Determine if sortable should be enabled
if (this.sortableValue && this.modeValue !== "batch" && this.modeValue !== "search") {
this.tableBodyTarget.setAttribute('data-controller', 'sortable');
} else {
this.tableBodyTarget.removeAttribute('data-controller');
}

// Update the selected rows count
this.selectedRowsCountTarget.textContent = `${selectedRows.length}`

Expand Down
6 changes: 4 additions & 2 deletions admin/app/components/solidus_admin/ui/table/component.rb
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,8 @@ class SolidusAdmin::UI::Table::Component < SolidusAdmin::BaseComponent
Column = Struct.new(:header, :data, :col, :wrap, keyword_init: true)
Filter = Struct.new(:presentation, :combinator, :attribute, :predicate, :options, keyword_init: true)
Scope = Struct.new(:name, :label, :default, keyword_init: true)
private_constant :BatchAction, :Column, :Filter, :Scope
Sortable = Struct.new(:url, :param, :animation, :handle, keyword_init: true)
private_constant :BatchAction, :Column, :Filter, :Scope, :Sortable

class Data < Struct.new(:rows, :class, :url, :prev, :next, :columns, :fade, :batch_actions, keyword_init: true) # rubocop:disable Lint/StructNewOverride,Style/StructInheritance
def initialize(**args)
Expand Down Expand Up @@ -49,11 +50,12 @@ def value
end
end

def initialize(id:, data:, search: nil)
def initialize(id:, data:, search: nil, sortable: nil)
@id = id
@data = Data.new(**data)
@data.columns.unshift selectable_column if @data.batch_actions.present?
@search = Search.new(**search) if search
@sortable = Sortable.new(**sortable) if sortable
end

def selectable_column
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,35 +4,114 @@
class SolidusAdmin::UI::Table::ComponentPreview < ViewComponent::Preview
include SolidusAdmin::Preview

# Render a simple table with 10 products and pagination.
# - The `:id` column is the product `#id` attribute, and uses a **symbol** for both the content and the header
# - The `:name` column is the product `#name` attribute, and uses **a block returning strings** for the content and a symbol for the header
# - The `:available_on` column is the product `#available_on` attribute, and uses **a block returning strings** for both the content and the header
# - The `:price` column shows the product `#price` attribute in a badge component, uses **blocks returning component instances** for both the content and the header
#
# All these ways to header and data cells can be mixed and matched.
def simple
# @param search_bar toggle
# @param filters toggle "Visible only with the Search Bar enabled"
# @param batch_actions toggle
# @param scopes toggle
# @param sortable select :sortable_select
def overview(search_bar: false, filters: false, batch_actions: false, scopes: false, sortable: nil)
render current_component.new(
id: 'simple-list',
data: {
class: Spree::Product,
rows: Array.new(10) { |n|
Spree::Product.new(id: n, name: "Product #{n}", price: n * 10.0, available_on: n.days.ago)
},
columns: [
{ header: :id, data: -> { _1.id.to_s } },
{ header: :name, data: :name },
{ header: -> { "Availability at #{Time.current}" }, data: -> { "#{time_ago_in_words _1.available_on} ago" } },
{ header: -> { component("ui/badge").new(name: "$$$") }, data: -> { component("ui/badge").new(name: _1.display_price, color: :green) } },
{ header: "Generated at", data: Time.current.to_s },
],
prev: nil,
next: '#2',
data: table_data(batch_actions, sortable),
search: search_bar ? search_options(filters, scopes) : nil,
sortable: sortable ? sortable_options(sortable) : nil,
)
end

private

def sortable_select
{
choices: %i[row handle],
include_blank: true
}
end

def table_data(batch_actions, sortable)
columns = [
{ header: :id, data: -> { _1.id.to_s } },
{ header: :name, data: :name },
{ header: -> { "Availability at #{Time.current}" }, data: -> { "#{time_ago_in_words _1.available_on} ago" } },
{ header: -> { component("ui/badge").new(name: "$$$") }, data: -> { component("ui/badge").new(name: _1.display_price, color: :green) } },
{ header: "Generated at", data: Time.current.to_s },
]

if sortable == "handle"
columns.unshift({
header: "",
data: ->(_) { component("ui/icon").new(name: 'draggable', class: 'w-5 h-5 cursor-pointer handle') }
})
end
{
class: Spree::Product,
rows: Array.new(10) { |n| Spree::Product.new(id: n, name: "Product #{n}", price: n * 10.0, available_on: n.days.ago) },
columns: columns,
prev: nil,
next: '#2',
}.tap do |data|
data[:batch_actions] = batch_actions_data if batch_actions
end
end

def batch_actions_data
[
{
display_name: "Delete",
action: "#",
method: :delete,
icon: 'delete-bin-7-line',
},
search: {
name: :no_key,
url: '#',
{
display_name: "Discontinue",
action: "#",
method: :put,
icon: 'pause-circle-line',
},
)
{
display_name: "Activate",
action: "#",
method: :put,
icon: 'play-circle-line',
},
]
end

def search_options(filters, scopes)
{
name: :no_key,
url: '#',
scopes: scopes ? scope_options : nil,
filters: filters ? filter_options : nil
}
end

def scope_options
[
{ name: :all, label: "All", default: true },
{ name: :deleted, label: "Deleted" }
]
end

def filter_options
[
{
presentation: "Filter",
combinator: 'or',
attribute: "attribute",
predicate: "eq",
options: [
["Yes", 1], ["No", 0]
]
}
]
end

def sortable_options(sortable)
options = {
url: ->(_) { "#" },
param: 'position'
}
options[:handle] = '.handle' if sortable == "handle"
options
end
end
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,6 @@

RSpec.describe SolidusAdmin::UI::Table::Component, type: :component do
it "renders a simple table" do
render_preview(:simple)
render_preview(:overview)
end
end

0 comments on commit a0b8a40

Please sign in to comment.