diff --git a/lib/phlexy_ui.rb b/lib/phlexy_ui.rb index fd5a01c..376d5bd 100644 --- a/lib/phlexy_ui.rb +++ b/lib/phlexy_ui.rb @@ -15,6 +15,7 @@ module PhlexyUI autoload :Button, "phlexy_ui/button" autoload :Card, "phlexy_ui/card" autoload :Tabs, "phlexy_ui/tabs" + autoload :Drawer, "phlexy_ui/drawer" end loader.eager_load diff --git a/lib/phlexy_ui/base.rb b/lib/phlexy_ui/base.rb index 64cde53..fbea120 100644 --- a/lib/phlexy_ui/base.rb +++ b/lib/phlexy_ui/base.rb @@ -11,7 +11,7 @@ def initialize(*base_modifiers, **options) private - attr_reader :base_modifiers, :options, :data, :as + attr_reader :base_modifiers, :options, :data, :as, :id def generate_classes!( component_html_class:, diff --git a/lib/phlexy_ui/drawer.rb b/lib/phlexy_ui/drawer.rb new file mode 100644 index 0000000..28fe0c6 --- /dev/null +++ b/lib/phlexy_ui/drawer.rb @@ -0,0 +1,69 @@ +# frozen_string_literal: true + +module PhlexyUI + class Drawer < Base + def initialize(*, id:, as: :section, **) + super(*, **) + @as = as + @id = id + end + + def view_template(&) + generate_classes!( + component_html_class: :drawer, + modifiers_map: DRAWER_MODIFIERS_MAP, + base_modifiers:, + options: + ).then do |classes| + public_send(as, class: classes, **options, &) + end + end + + def toggle(**options, &) + generate_classes!( + component_html_class: :"drawer-toggle", + options: + ).then do |classes| + input(id:, type: :checkbox, class: classes, **options, &) + end + end + + def content(as: :div, **options, &) + generate_classes!( + component_html_class: :"drawer-content", + options: + ).then do |classes| + public_send(as, class: classes, **options, &) + end + end + + def side(as: :div, **options, &) + generate_classes!( + component_html_class: :"drawer-side", + options: + ).then do |classes| + public_send(as, class: classes, **options, &) + end + end + + def overlay(**options, &) + generate_classes!( + component_html_class: :"drawer-overlay", + options: + ).then do |classes| + label(for: id, class: classes, **options, &) + end + end + + def button(*, **, &) + render Button.new(*, as: :label, for: id, **, &) + end + + private + + DRAWER_MODIFIERS_MAP = { + end: "drawer-end", + open: "drawer-open" + }.freeze + end +end diff --git a/spec/lib/phlexy_ui/drawer_spec.rb b/spec/lib/phlexy_ui/drawer_spec.rb new file mode 100644 index 0000000..2906382 --- /dev/null +++ b/spec/lib/phlexy_ui/drawer_spec.rb @@ -0,0 +1,244 @@ +require "spec_helper" + +describe PhlexyUI::Drawer do + subject(:output) { render described_class.new } + + describe "conditions" do + { + end: "drawer-end", + open: "drawer-open" + }.each do |condition, css| + context "when given :#{condition} condition" do + subject(:output) { render described_class.new(condition, id: :my_drawer) } + + it "renders it apart from the main class" do + expected_html = html <<~HTML +
+ HTML + + expect(output).to eq(expected_html) + end + end + end + + context "when condition doesn't exist" do + it "raises an error" do + expect { render described_class.new(:foo, id: :my_drawer) } + .to raise_error( + ArgumentError, + "Condition `foo` is not defined for PhlexyUI::Drawer" + ) + end + end + + context "when given multiple conditions" do + subject(:output) { render described_class.new(:end, :open, id: :my_drawer) } + + it "renders them separately" do + expected_html = html <<~HTML +
+ HTML + + expect(output).to eq(expected_html) + end + end + end + + describe "data" do + subject(:output) do + render described_class.new(:open, id: :my_drawer, data: {foo: "bar"}) + end + + it "renders it correctly" do + expected_html = html <<~HTML +
+ HTML + + expect(output).to eq(expected_html) + end + end + + describe "prefix" do + around do |example| + original_prefix = PhlexyUI.configuration.prefix + + PhlexyUI.configure do |config| + config.prefix = "foo-" + end + + example.run + + PhlexyUI.configure do |config| + config.prefix = original_prefix + end + end + + subject(:output) do + render described_class.new(:open, id: :my_drawer) + end + + it "renders it correctly" do + expected_html = html <<~HTML +
+ HTML + + expect(output).to eq(expected_html) + end + end + + describe "responsiveness" do + %i[sm md lg].each do |viewport| + context "when given an :#{viewport} responsive option as a single argument" do + subject(:output) do + render described_class.new(:open, viewport => :end, :id => :my_drawer) + end + + it "renders it separately with a responsive prefix" do + expected_html = html <<~HTML +
+
+ HTML + + expect(output).to eq(expected_html) + end + end + + context "when given multiple responsive options as an array" do + subject(:output) do + render described_class.new(:open, viewport => [:open, :end], :id => :my_drawer) + end + + it "renders it separately with a responsive prefix" do + expected_html = html <<~HTML +
+
+ HTML + + expect(output).to eq(expected_html) + end + end + + context "when it's prefixed" do + around do |example| + original_prefix = PhlexyUI.configuration.prefix + + PhlexyUI.configure do |config| + config.prefix = "foo-" + end + + example.run + + PhlexyUI.configure do |config| + config.prefix = original_prefix + end + end + + subject(:output) do + render described_class.new(:open, viewport => [:open, :end], :id => :my_drawer) + end + + it "renders it separately with a responsive prefix" do + expected_html = html <<~HTML +
+
+ HTML + + expect(output).to eq(expected_html) + end + end + end + end + + describe "rendering via Kit" do + subject(:output) do + Drawer(:end, id: :my_drawer) + end + + it "renders it correctly" do + expected_html = html <<~HTML +
+ HTML + + expect(output).to eq(expected_html) + end + end + + describe "passing :as option" do + subject(:output) { render described_class.new(as: :div, id: :my_drawer) } + + it "renders the card as the given tag" do + expected_html = html <<~HTML +
+ HTML + + expect(output).to eq(expected_html) + end + end + + describe "rendering a full drawer" do + let(:component) do + Class.new(Phlex::HTML) do + def view_template(&) + render PhlexyUI::Drawer.new(:end, id: :my_drawer) do |drawer| + drawer.toggle(class: "my-toggle", data: {my: "toggles"}) + drawer.content(class: "my-content", data: {my: "contents"}) do + drawer.button(:primary, class: "my-button", data: {my: "buttons"}) do + "Open Drawer" + end + + div do + "Content" + end + end + drawer.side(class: "my-side", data: {my: "sides"}) do |side| + side.overlay + + ul do + li do + a do + "Sidebar Item 1" + end + end + end + end + end + end + end + end + + subject(:output) do + render component.new + end + + it "is expected to match the formatted HTML" do + expected_html = html <<~HTML +
+ +
+ +
Content
+
+
+ + + +
+
+ HTML + + is_expected.to eq(expected_html) + end + end +end