From c240f04d6d86100540e7e32db10549f7f286576e Mon Sep 17 00:00:00 2001 From: Elia Schito Date: Mon, 18 Sep 2023 10:33:07 +0200 Subject: [PATCH] Add a low-level ui/input component This will represent all input shaped elements like `input`, `textarea` and `select`. --- .../arrow_down_s_fill_gray_700.svg | 3 + .../arrow_down_s_fill_red_400.svg | 3 + .../solidus_admin/ui/forms/input/component.js | 16 ++++ .../solidus_admin/ui/forms/input/component.rb | 96 +++++++++++++++++++ .../solidus_admin/tailwind.config.js.erb | 2 + .../ui/forms/input/component_preview.rb | 60 ++++++++++++ .../input/component_preview/overview.html.erb | 28 ++++++ .../ui/forms/input/component_spec.rb | 12 +++ 8 files changed, 220 insertions(+) create mode 100644 admin/app/assets/images/solidus_admin/arrow_down_s_fill_gray_700.svg create mode 100644 admin/app/assets/images/solidus_admin/arrow_down_s_fill_red_400.svg create mode 100644 admin/app/components/solidus_admin/ui/forms/input/component.js create mode 100644 admin/app/components/solidus_admin/ui/forms/input/component.rb create mode 100644 admin/spec/components/previews/solidus_admin/ui/forms/input/component_preview.rb create mode 100644 admin/spec/components/previews/solidus_admin/ui/forms/input/component_preview/overview.html.erb create mode 100644 admin/spec/components/solidus_admin/ui/forms/input/component_spec.rb diff --git a/admin/app/assets/images/solidus_admin/arrow_down_s_fill_gray_700.svg b/admin/app/assets/images/solidus_admin/arrow_down_s_fill_gray_700.svg new file mode 100644 index 00000000000..78070f47c71 --- /dev/null +++ b/admin/app/assets/images/solidus_admin/arrow_down_s_fill_gray_700.svg @@ -0,0 +1,3 @@ + + + diff --git a/admin/app/assets/images/solidus_admin/arrow_down_s_fill_red_400.svg b/admin/app/assets/images/solidus_admin/arrow_down_s_fill_red_400.svg new file mode 100644 index 00000000000..de7bff77392 --- /dev/null +++ b/admin/app/assets/images/solidus_admin/arrow_down_s_fill_red_400.svg @@ -0,0 +1,3 @@ + + + diff --git a/admin/app/components/solidus_admin/ui/forms/input/component.js b/admin/app/components/solidus_admin/ui/forms/input/component.js new file mode 100644 index 00000000000..44ff79b03b0 --- /dev/null +++ b/admin/app/components/solidus_admin/ui/forms/input/component.js @@ -0,0 +1,16 @@ +import { Controller } from '@hotwired/stimulus' + +export default class extends Controller { + static values = { + customValidity: String, + } + + connect() { + if (this.customValidityValue) + this.element.setCustomValidity(this.customValidityValue) + } + + clearCustomValidity() { + this.element.setCustomValidity('') + } +} diff --git a/admin/app/components/solidus_admin/ui/forms/input/component.rb b/admin/app/components/solidus_admin/ui/forms/input/component.rb new file mode 100644 index 00000000000..b0644a5e364 --- /dev/null +++ b/admin/app/components/solidus_admin/ui/forms/input/component.rb @@ -0,0 +1,96 @@ +# frozen_string_literal: true + +class SolidusAdmin::UI::Forms::Input::Component < SolidusAdmin::BaseComponent + SIZES = { + s: "form-control-sm px-3 py-1.5 body-small", + m: "form-control-md px-3 py-1.5 body-small", + l: "form-control-lg px-3 py-1.5 body-text" + }.freeze + + HEIGHTS = { + s: "h-7", + m: "h-9", + l: "h-12" + }.freeze + + MULTILINE_HEIGHTS = { + s: %w[min-h-[84px]], + m: %w[min-h-[108px]], + l: %w[min-h-[144px]], + }.freeze + + TYPES = Set.new(%i[ + text + password + number + email + tel + url + search + color + date + datetime-local + month + week + time + ]).freeze + + def initialize(tag: :input, size: :m, error: nil, **attributes) + @tag = tag + @attributes = attributes + @size = size + + specialized_classes = [] + + case @tag + when :input + specialized_classes << "form-input" + specialized_classes << HEIGHTS[size] + if @attributes[:type] && !TYPES.include?(@attributes[:type]) + raise ArgumentError, "unsupported type attribute: attributes[:type]}" + end + when :textarea + specialized_classes << "form-textarea" + specialized_classes << MULTILINE_HEIGHTS[size] + when :select + if @attributes[:multiple] + specialized_classes << "form-multiselect" + specialized_classes << MULTILINE_HEIGHTS[size] + else + specialized_classes << "form-select" + specialized_classes << "bg-arrow-down-s-fill-gray-700 invalid:bg-arrow-down-s-fill-red-400 aria-invalid:bg-arrow-down-s-fill-red-400" + specialized_classes << HEIGHTS[size] + end + end + + @attributes[:class] = [ + %w[ + w-full + text-black bg-white border border-gray-300 rounded-sm placeholder:text-gray-400 + hover:border-gray-500 + focus:ring focus:ring-gray-300 focus:ring-0.5 focus:bg-white focus:ring-offset-0 [&:focus-visible]:outline-none + disabled:bg-gray-50 disabled:text-gray-500 disabled:placeholder:text-gray-300 disabled:cursor-not-allowed + invalid:border-red-400 invalid:hover:border-red-400 invalid:text-red-400 + aria-invalid:border-red-400 aria-invalid:hover:border-red-400 aria-invalid:text-red-400 + ], + SIZES[size], + specialized_classes, + @attributes[:class], + ].compact.join(" ") + + @error = error + + raise ArgumentError, "unsupported tag: #{tag}" unless %i[input textarea select].include?(@tag) + end + + def call + tag.public_send( + @tag, + content, + "data-controller": stimulus_id, + "data-#{stimulus_id}-custom-validity-value": @error.presence, + "data-action": "#{stimulus_id}#clearCustomValidity", + **@attributes + ) + end +end diff --git a/admin/config/solidus_admin/tailwind.config.js.erb b/admin/config/solidus_admin/tailwind.config.js.erb index 8846b6f8ca7..5cf74942e64 100644 --- a/admin/config/solidus_admin/tailwind.config.js.erb +++ b/admin/config/solidus_admin/tailwind.config.js.erb @@ -74,6 +74,8 @@ module.exports = { }, backgroundImage: { 'arrow-right-up-line': "url('solidus_admin/arrow_right_up_line.svg')", + 'arrow-down-s-fill-gray-700': "url('solidus_admin/arrow_down_s_fill_gray_700.svg')", + 'arrow-down-s-fill-red-400': "url('solidus_admin/arrow_down_s_fill_red_400.svg')", }, boxShadow: { sm: '0px 1px 2px 0px rgba(0, 0, 0, 0.04)', diff --git a/admin/spec/components/previews/solidus_admin/ui/forms/input/component_preview.rb b/admin/spec/components/previews/solidus_admin/ui/forms/input/component_preview.rb new file mode 100644 index 00000000000..156e1345442 --- /dev/null +++ b/admin/spec/components/previews/solidus_admin/ui/forms/input/component_preview.rb @@ -0,0 +1,60 @@ +# frozen_string_literal: true + +# @component "ui/forms/input" +class SolidusAdmin::UI::Forms::Input::ComponentPreview < ViewComponent::Preview + include SolidusAdmin::Preview + + def overview + render_with_template + end + + # @param error toggle + # @param size select { choices: [s, m, l] } + # @param value text + # @param type select :input_types + def input_playground(error: false, size: "m", value: "value", type: "text") + render component("ui/forms/input").new( + tag: :input, + type: type.to_sym, + error: error ? "There is an error" : nil, + size: size.to_sym, + value: value, + ) + end + + # @param error toggle + # @param size select { choices: [s, m, l] } + # @param multiple toggle + # @param rows number + # @param options number + # @param include_blank toggle + def select_playground(error: false, inlcude_blank: true, options: 3, rows: 1, size: "m") + options = (1..options).map { |i| ["Option #{i}", i] } + options.unshift(["", ""]) if inlcude_blank + options.map! { tag.option(_1, value: _2) } + + render component("ui/forms/input").new( + tag: :select, + "size" => rows > 1 ? rows : nil, + error: error ? "There is an error" : nil, + size: size.to_sym, + ).with_content(options.reduce(:+)) + end + + # @param error toggle + # @param size select { choices: [s, m, l] } + # @param content textarea + def textarea_playground(error: false, size: "m", content: "value") + render component("ui/forms/input").new( + tag: :textarea, + size: size.to_sym, + error: error ? "There is an error" : nil, + ).with_content(content) + end + + private + + def input_types + current_component::TYPES.to_a + end +end diff --git a/admin/spec/components/previews/solidus_admin/ui/forms/input/component_preview/overview.html.erb b/admin/spec/components/previews/solidus_admin/ui/forms/input/component_preview/overview.html.erb new file mode 100644 index 00000000000..33a55354b1d --- /dev/null +++ b/admin/spec/components/previews/solidus_admin/ui/forms/input/component_preview/overview.html.erb @@ -0,0 +1,28 @@ +
+

Input

+ +
+
Default
+ <%= render current_component.new(placeholder: "Placeholder") %> +
+ +
+
Filled
+ <%= render current_component.new(value: "My value") %> +
+ +
+
Error
+ <%= render current_component.new(value: "Bad value", error: "The value is wrong!") %> +
+ +
+
Disabled
+ <%= render current_component.new(placeholder: "Placeholder", disabled: true) %> +
+ +
+
Disabled filled
+ <%= render current_component.new(value: "My value", disabled: true) %> +
+
diff --git a/admin/spec/components/solidus_admin/ui/forms/input/component_spec.rb b/admin/spec/components/solidus_admin/ui/forms/input/component_spec.rb new file mode 100644 index 00000000000..0fac56f5d82 --- /dev/null +++ b/admin/spec/components/solidus_admin/ui/forms/input/component_spec.rb @@ -0,0 +1,12 @@ +# frozen_string_literal: true + +require "spec_helper" + +RSpec.describe SolidusAdmin::UI::Forms::Input::Component, type: :component do + it "renders the overview preview" do + render_preview(:overview) + render_preview(:input_playground) + render_preview(:select_playground) + render_preview(:textarea_playground) + end +end