Skip to content

Instantly share code, notes, and snippets.

@john-hamnavoe
Last active December 6, 2022 12:10
Show Gist options
  • Save john-hamnavoe/9cb45c585bccf9f3700afc6a1e6631de to your computer and use it in GitHub Desktop.
Save john-hamnavoe/9cb45c585bccf9f3700afc6a1e6631de to your computer and use it in GitHub Desktop.
My approach to table view components

Table View Component

These components used to generate a table in Rails 7 app.

Generate components

rails g component Tables::Table
rails g component Tables::Row
rails g component Tables::Header
rails g component Tables::Cell
rails g component Tables::CheckboxCell

After running generator delete the following files (don't need them).
rm app/components/tables/cell_component.html.erb
rm app/components/tables/header_component.html.erb

Table component

table_component.rb

# frozen_string_literal: true

class Tables::TableComponent < ApplicationComponent
  renders_many :headers, Tables::HeaderComponent
  renders_many :rows, Tables::RowComponent
end

table_component.html.erb

<div class="mt-4 flex flex-col">
  <div class="-my-2 -mx-4 overflow-x-auto sm:-mx-6 lg:-mx-8">
    <div class="inline-block min-w-full py-2 align-middle md:px-6 lg:px-8">
      <div class="overflow-hidden shadow ring-1 ring-black ring-opacity-5 md:rounded-lg">
        <table class="min-w-full divide-y divide-gray-200">
          <thead class="bg-gray-50">
            <tr>
              <% headers.each do |header| %>
                <%= header %>
              <% end %>
            </tr>
          </thead>

          <tbody class="divide-y divide-gray-200 bg-white">
            <% rows.each do |row| %>
              <%= row %>
            <% end %>
          </tbody>
        </table>
      </div>
    </div>
  </div>
</div>

Header Component

header_component.rb

# frozen_string_literal: true

class Tables::HeaderComponent < ApplicationComponent
  include Tables::CellClasses

  attr_reader :title, :align, :small_visible

  def initialize(title = nil, align: :left, small_visible: true)
    @title = title
    @align = align
    @small_visible = small_visible
  end

  def call
    tag.th title || content, scope: "col",
      class: class_names("px-6 py-3 text-xs font-medium text-gray-500 uppercase tracking-wider", align_class, small_visible_class)
  end
end

Row Component

row_component.rb

# frozen_string_literal: true

class Tables::RowComponent < ApplicationComponent
  renders_many :cells, Tables::CellComponent
end

row_component.html.erb

<tr>
  <% cells.each do |cell| %>
    <%= cell %>
  <% end %>
</tr>

Cell Component

cell_component.rb

# frozen_string_literal: true

class Tables::CellComponent < ApplicationComponent
  include Tables::CellClasses

  private attr_reader :primary, :align, :small_visible

  def initialize(primary: false, align: :left, small_visible: true)
    @primary = primary
    @align = align
    @small_visible = small_visible
  end

  def call
    tag.td content, class: class_names("px-6 py-2 whitespace-nowrap text-sm", align_class, small_visible_class, {
      "font-medium text-gray-900": primary,
      "text-gray-500": !primary
    })
  end
end

Checkbox Cell Component

checkbox_cell_component.rb

# frozen_string_literal: true

class Tables::CheckboxCellComponent < ApplicationComponent

  private attr_reader :checked

  def initialize(checked)
    @checked = checked
  end

  def check_state 
    checked ? "checked" : "unchecked"
  end
end

checkbox_cell_component.html.erb

<div class="flex h-5">
  <input disabled <%= check_state %> type="checkbox" class="h-4 w-4 rounded border-gray-300 text-indigo-600">
</div>

Helpers

Put in app/components/tables

cell_classes.rb

# frozen_string_literal: true

module Tables::CellClasses
  def small_visible_class
    small_visible == true ? "" : "hidden md:table-cell"
  end
  
  def align_class
    align == :right ? "text-right" : "text-left"
  end  
end
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment