From 7e3a4e94138ff58b77f846013b0d6455b696cb28 Mon Sep 17 00:00:00 2001 From: Jakob Skjerning Date: Thu, 21 Aug 2025 16:47:44 +0200 Subject: [PATCH 1/4] Add the most basic card component For now this is basically just a frame. Going forward I expect us to add more details and functionality in order to implement all of the options of https://flowbite.com/docs/components/card/ - but for now the most basic version is a good start. --- CHANGELOG.md | 1 + app/components/flowbite/card.rb | 29 +++++++++++++++++++ demo/test/components/previews/card_preview.rb | 8 +++++ test/components/flowbite/card_test.rb | 11 +++++++ 4 files changed, 49 insertions(+) create mode 100644 app/components/flowbite/card.rb create mode 100644 demo/test/components/previews/card_preview.rb create mode 100644 test/components/flowbite/card_test.rb diff --git a/CHANGELOG.md b/CHANGELOG.md index 7ffdfba..4b02706 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -10,6 +10,7 @@ This project adheres to [Semantic Versioning](http://semver.org/). * Button component (first component, wee!) * Input components * InputField components +* Basic Card component ### Changes diff --git a/app/components/flowbite/card.rb b/app/components/flowbite/card.rb new file mode 100644 index 0000000..21eb58d --- /dev/null +++ b/app/components/flowbite/card.rb @@ -0,0 +1,29 @@ +module Flowbite + # Renders a card element. + # + # See https://flowbite.com/docs/components/cards/ + class Card < ViewComponent::Base + class << self + def classes(state: :default, style: :default) + style = styles.fetch(style) + style.fetch(state) + end + + # rubocop:disable Layout/LineLength + def styles + { + default: Flowbite::Style.new( + default: ["max-w-sm", "p-6", "bg-white", "border", "border-gray-200", "rounded-lg", "shadow-sm", "dark:bg-gray-800", "dark:border-gray-700"] + ) + }.freeze + end + # rubocop:enable Layout/LineLength + end + + def call + content_tag(:div, class: self.class.classes) do + concat(content_tag(:div, content, class: "font-normal text-gray-700 dark:text-gray-400")) + end + end + end +end diff --git a/demo/test/components/previews/card_preview.rb b/demo/test/components/previews/card_preview.rb new file mode 100644 index 0000000..f2b44f0 --- /dev/null +++ b/demo/test/components/previews/card_preview.rb @@ -0,0 +1,8 @@ +# frozen_string_literal: true + +class CardPreview < Lookbook::Preview + # Use the following simple card component with a title and description. + def default + render(Flowbite::Card.new) { "Use the following simple card component with a title and description." } + end +end diff --git a/test/components/flowbite/card_test.rb b/test/components/flowbite/card_test.rb new file mode 100644 index 0000000..4fa41a4 --- /dev/null +++ b/test/components/flowbite/card_test.rb @@ -0,0 +1,11 @@ +require "test_helper" + +class Flowbite::CardTest < Minitest::Test + include ViewComponent::TestHelpers + + def test_renders_a_default_card + render_inline(Flowbite::Card.new) { "Card Content" } + + assert_selector("div.p-6.bg-white.border.border-gray-200.rounded-lg.shadow-sm") + end +end From b6310eca88f0bb967f3d63ef0180c0078d8a2a06 Mon Sep 17 00:00:00 2001 From: Jakob Skjerning Date: Fri, 22 Aug 2025 11:53:57 +0200 Subject: [PATCH 2/4] Add option to pass attributes to the card --- app/components/flowbite/card.rb | 9 ++++++++- test/components/flowbite/card_test.rb | 6 ++++++ 2 files changed, 14 insertions(+), 1 deletion(-) diff --git a/app/components/flowbite/card.rb b/app/components/flowbite/card.rb index 21eb58d..21a12a4 100644 --- a/app/components/flowbite/card.rb +++ b/app/components/flowbite/card.rb @@ -21,9 +21,16 @@ def styles end def call - content_tag(:div, class: self.class.classes) do + content_tag(:div, {class: self.class.classes}.merge(@options)) do concat(content_tag(:div, content, class: "font-normal text-gray-700 dark:text-gray-400")) end end + + # @param options [Hash] Additional HTML options for the card container + # (e.g., custom classes, data attributes). These options are merged into + # the card's root element. + def initialize(options: {}) + @options = options || {} + end end end diff --git a/test/components/flowbite/card_test.rb b/test/components/flowbite/card_test.rb index 4fa41a4..a3ccfda 100644 --- a/test/components/flowbite/card_test.rb +++ b/test/components/flowbite/card_test.rb @@ -8,4 +8,10 @@ def test_renders_a_default_card assert_selector("div.p-6.bg-white.border.border-gray-200.rounded-lg.shadow-sm") end + + def test_passes_options_to_the_card_as_attributes + render_inline(Flowbite::Card.new(options: {id: "card-1"})) { "Card Content" } + + assert_selector("div#card-1") + end end From 8486ac897d640e00898f53919c749bfcd278802c Mon Sep 17 00:00:00 2001 From: Jakob Skjerning Date: Fri, 22 Aug 2025 12:38:03 +0200 Subject: [PATCH 3/4] Make it possible to add custom classes to cards --- README.md | 27 +++++++++++++++++++++++++++ app/components/flowbite/card.rb | 11 +++++++++-- test/components/flowbite/card_test.rb | 13 +++++++++++++ 3 files changed, 49 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index a357dbc..de63077 100644 --- a/README.md +++ b/README.md @@ -147,6 +147,33 @@ Add Flowbite to your Tailwind CSS configuration. In your `app/assets/tailwind/ap ) %> ``` +## How to customize components + +### Add specific CSS classes + +A common use case for customizing a component is to add more CSS classes when +rendering it, fx to change the size or spacing. flowbite-components is optimized +for this case and all you need to do is specify the extra classes: + +```erb +<%= render(Flowbite::Card.new(class: "w-full my-8")) { "Content" } %> +``` +renders +```html +
+``` + +If you want to fully replace the existing classes, you can pass an entirely new +`class` attribute via options: + +```erb +<%= render(Flowbite::Card.new(options: {class: "w-full my-8"})) { "Content" } %> +``` +renders +```html +
+``` + ## Available Components ### Form Components diff --git a/app/components/flowbite/card.rb b/app/components/flowbite/card.rb index 21a12a4..1035a5a 100644 --- a/app/components/flowbite/card.rb +++ b/app/components/flowbite/card.rb @@ -21,15 +21,22 @@ def styles end def call - content_tag(:div, {class: self.class.classes}.merge(@options)) do + card_options = {} + card_options[:class] = self.class.classes + @class + + content_tag(:div, card_options.merge(@options)) do concat(content_tag(:div, content, class: "font-normal text-gray-700 dark:text-gray-400")) end end + # @param class [Array] Additional CSS classes for the card + # container. + # # @param options [Hash] Additional HTML options for the card container # (e.g., custom classes, data attributes). These options are merged into # the card's root element. - def initialize(options: {}) + def initialize(class: [], options: {}) + @class = Array(binding.local_variable_get(:class)) || [] @options = options || {} end end diff --git a/test/components/flowbite/card_test.rb b/test/components/flowbite/card_test.rb index a3ccfda..e334dcd 100644 --- a/test/components/flowbite/card_test.rb +++ b/test/components/flowbite/card_test.rb @@ -14,4 +14,17 @@ def test_passes_options_to_the_card_as_attributes assert_selector("div#card-1") end + + def test_adds_the_classes_to_the_default_classes + render_inline(Flowbite::Card.new(class: "custom-class another")) { "Card Content" } + + assert_selector("div.p-6.bg-white.border.border-gray-200.rounded-lg.shadow-sm.custom-class.another") + end + + def test_overrides_the_default_classes + render_inline(Flowbite::Card.new(options: {class: "custom-class another"})) { "Card Content" } + + assert_no_selector("div.p-6.bg-white.border.border-gray-200.rounded-lg.shadow-sm") + assert_selector("div.custom-class.another") + end end From 33c79934f8155d83c3c9a410b216a582800f9bba Mon Sep 17 00:00:00 2001 From: Jakob Skjerning Date: Fri, 22 Aug 2025 11:08:39 +0200 Subject: [PATCH 4/4] Use the card Now that we have a basic Card component we might as well use it. --- demo/app/views/pages/index.html.erb | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/demo/app/views/pages/index.html.erb b/demo/app/views/pages/index.html.erb index 9c77fc3..a52be2e 100644 --- a/demo/app/views/pages/index.html.erb +++ b/demo/app/views/pages/index.html.erb @@ -17,7 +17,7 @@
-
+ <%= render(Flowbite::Card.new(:class => "sm:flex-1")) do %> @@ -30,9 +30,9 @@ <% end %> -
+ <% end %> -
+ <%= render(Flowbite::Card.new(:class => "sm:flex-1")) do %> @@ -46,9 +46,9 @@ <% end %> -
+ <% end %> -
+ <%= render(Flowbite::Card.new(:class => "sm:flex-1")) do %> @@ -64,5 +64,5 @@ <% end %> -
+ <% end %>