Skip to content
Closed
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
427 changes: 427 additions & 0 deletions Gemfile.lock

Large diffs are not rendered by default.

36 changes: 36 additions & 0 deletions app/components/flowbite/timeline.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
# frozen_string_literal: true

module Flowbite
# Renders a vertical timeline component for displaying chronological events.
#
# @example Basic usage
# <%= render(Flowbite::Timeline.new) do %>
# <%= render(Flowbite::Timeline::Item.new(datetime: "February 2022", title: "Event title")) do %>
# Event description goes here.
# <% end %>
# <% end %>
#
# @see https://flowbite.com/docs/components/timeline/
# @lookbook_embed TimelinePreview
class Timeline < ViewComponent::Base
CLASSES = ["border-s", "border-default", "relative"].freeze

attr_reader :options

# @param class [String, Array<String>] Additional CSS classes.
def initialize(class: nil, **options)
@class = Array.wrap(binding.local_variable_get(:class))
@options = options
end

private

def classes
CLASSES + @class
end

def tag_options
{class: classes}.merge(options)
end
end
end
69 changes: 69 additions & 0 deletions app/components/flowbite/timeline/item.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
# frozen_string_literal: true

module Flowbite
class Timeline
# Renders a single item within a Timeline component.
#
# @example Basic usage
# <%= render(Flowbite::Timeline::Item.new(datetime: "February 2022", title: "Event title")) do %>
# Event description goes here.
# <% end %>
class Item < ViewComponent::Base
CLASSES = ["mb-10", "ms-4"].freeze

DOT_CLASSES = [
"-start-1.5",
"absolute",
"bg-neutral-quaternary",
"border",
"border-buffer",
"h-3",
"mt-1.5",
"rounded-full",
"w-3"
].freeze

DATETIME_CLASSES = [
"font-normal",
"leading-none",
"text-body",
"text-sm"
].freeze

TITLE_CLASSES = [
"font-semibold",
"my-2",
"text-heading",
"text-lg"
].freeze

BODY_CLASSES = [
"font-normal",
"text-base",
"text-body"
].freeze

attr_reader :options

# @param class [String, Array<String>] Additional CSS classes.
# @param datetime [String] The time label displayed for the item.
# @param title [String] The heading text for the item.
def initialize(datetime:, title:, class: nil, **options)
@class = Array.wrap(binding.local_variable_get(:class))
@datetime = datetime
@title = title
@options = options
end

private

def classes
CLASSES + @class
end

def tag_options
{class: classes}.merge(options)
end
end
end
end
6 changes: 6 additions & 0 deletions app/components/flowbite/timeline/item/item.html.erb
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
<%= content_tag(:li, **tag_options) do %>
<div class="<%= DOT_CLASSES.join(" ") %>"></div>
<time class="<%= DATETIME_CLASSES.join(" ") %>"><%= @datetime %></time>
<h3 class="<%= TITLE_CLASSES.join(" ") %>"><%= @title %></h3>
<p class="<%= BODY_CLASSES.join(" ") %>"><%= content %></p>
<% end %>
3 changes: 3 additions & 0 deletions app/components/flowbite/timeline/timeline.html.erb
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
<%= content_tag(:ol, **tag_options) do %>
<%= content %>
<% end %>
7 changes: 7 additions & 0 deletions claude.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
# Claude

- Keep lists of arguments and @params sorted alphabetically.
- Make sure that all tests pass using `rake test`
- Make sure that all files can be linted succesfully using `rake standard`
- When running tests, make sure to run the entire test suite, not just the files that has been updated or created.
- When generating components, generate a preview for the component in demo/test/components/previews following the patterns from the other previews there.
19 changes: 19 additions & 0 deletions demo/test/components/previews/timeline_preview.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
# frozen_string_literal: true

class TimelinePreview < Lookbook::Preview
def example
render(Flowbite::Timeline.new) do
safe_join([
render(Flowbite::Timeline::Item.new(datetime: "February 2022", title: "Application UI code in Tailwind CSS")) {
"Get access to over 20+ pages including a dashboard layout, charts, kanban board, calendar, and pre-order E-commerce & Marketing pages."
},
render(Flowbite::Timeline::Item.new(datetime: "March 2022", title: "Marketing UI design in Figma")) {
"All of the pages and components are first designed in Figma and we keep a parity between the two versions even as we update the project."
},
render(Flowbite::Timeline::Item.new(datetime: "April 2022", title: "E-Commerce UI code in Tailwind CSS")) {
"Get started with dozens of web components and interactive elements built on top of Tailwind CSS."
}
])
end
end
end
98 changes: 98 additions & 0 deletions test/components/flowbite/timeline_test.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,98 @@
require "test_helper"

class Flowbite::TimelineTest < Minitest::Test
include ViewComponent::TestHelpers

def test_render_component
render_inline(Flowbite::Timeline.new) { "Content" }

assert_component_rendered
assert_selector("ol", text: "Content")
end

def test_renders_with_default_classes
render_inline(Flowbite::Timeline.new) { "Content" }

assert_selector("ol.relative.border-s.border-default")
end

# Custom classes

def test_adds_classes_to_the_default_ones
render_inline(Flowbite::Timeline.new(class: "custom-class")) { "Content" }

assert_selector("ol.relative.border-default.custom-class")
end

# Custom options

def test_passes_additional_options_as_attributes
render_inline(Flowbite::Timeline.new(id: "my-timeline", data: {controller: "timeline"})) { "Content" }

assert_selector("ol[id='my-timeline'][data-controller='timeline']")
end
end

class Flowbite::Timeline::ItemTest < Minitest::Test
include ViewComponent::TestHelpers

def test_render_component
render_inline(Flowbite::Timeline::Item.new(datetime: "February 2022", title: "Event title")) { "Description" }

assert_component_rendered
end

def test_renders_with_default_classes
render_inline(Flowbite::Timeline::Item.new(datetime: "February 2022", title: "Event title")) { "Description" }

assert_selector("li.mb-10.ms-4")
end

# Dot

def test_renders_dot_marker
render_inline(Flowbite::Timeline::Item.new(datetime: "February 2022", title: "Event title")) { "Description" }

assert_selector("li div.absolute.rounded-full.bg-neutral-quaternary.border.border-buffer.h-3.w-3")
end

# Datetime

def test_renders_datetime
render_inline(Flowbite::Timeline::Item.new(datetime: "February 2022", title: "Event title")) { "Description" }

assert_selector("time.text-sm.font-normal.text-body", text: "February 2022")
end

# Title

def test_renders_title
render_inline(Flowbite::Timeline::Item.new(datetime: "February 2022", title: "Event title")) { "Description" }

assert_selector("h3.text-lg.font-semibold.text-heading", text: "Event title")
end

# Body

def test_renders_body_content
render_inline(Flowbite::Timeline::Item.new(datetime: "February 2022", title: "Event title")) { "Description text" }

assert_selector("p.text-base.font-normal.text-body", text: "Description text")
end

# Custom classes

def test_adds_classes_to_the_default_ones
render_inline(Flowbite::Timeline::Item.new(class: "custom-class", datetime: "February 2022", title: "Event title")) { "Description" }

assert_selector("li.mb-10.ms-4.custom-class")
end

# Custom options

def test_passes_additional_options_as_attributes
render_inline(Flowbite::Timeline::Item.new(datetime: "February 2022", id: "my-item", title: "Event title")) { "Description" }

assert_selector("li[id='my-item']")
end
end