diff --git a/.circleci/config.yml b/.circleci/config.yml index baec602..b6ed5e5 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -5,7 +5,7 @@ version: 2.1 rails_build: &rails_build resource_class: small docker: - - image: cimg/ruby:3.4-browsers + - image: cimg/ruby:3.4.10-browsers environment: BUNDLE_JOBS: '3' BUNDLE_RETRY: '3' @@ -21,7 +21,7 @@ jobs: lint: resource_class: medium docker: - - image: cimg/ruby:3.3 + - image: cimg/ruby:3.4.10 steps: - checkout - ruby/install-deps @@ -32,7 +32,7 @@ jobs: coverage: resource_class: small docker: - - image: cimg/ruby:3.3 + - image: cimg/ruby:3.4.10 environment: RAILS_ENV: test steps: @@ -43,7 +43,7 @@ jobs: - run: name: Merge and check coverage command: | - bundle exec rake coverage:report + bundle exec rake app:coverage:report - store_artifacts: path: coverage @@ -81,6 +81,9 @@ jobs: - store_artifacts: path: log/ + - store_artifacts: + path: tmp/capybara/ + - run: name: Stash Coverage Results command: | @@ -100,8 +103,8 @@ workflows: - test: context: - "Practical Framework" - - coverage: - context: - - "Practical Framework" - requires: - - test + # - coverage: + # context: + # - "Practical Framework" + # requires: + # - test diff --git a/.ruby-version b/.ruby-version index 6cb9d3d..84d6c67 100644 --- a/.ruby-version +++ b/.ruby-version @@ -1 +1 @@ -3.4.3 +3.4.10 diff --git a/Gemfile b/Gemfile index 92417f4..074c0a7 100644 --- a/Gemfile +++ b/Gemfile @@ -1,6 +1,6 @@ # frozen_string_literal: true -source "https://rubygems.org" +source "https://gem.coop" # Specify your gem's dependencies in practical-framework.gemspec. gemspec @@ -27,6 +27,7 @@ gem "flipper-active_record" gem "action_policy" gem "honeybadger" gem "pagy" +gem "rubocop" group :test do gem "capybara" @@ -46,4 +47,4 @@ group :development, :test do gem 'rubocop-rails', require: false gem "rubocop-rails-omakase", require: false gem 'rubocop-rubycw', require: false -end \ No newline at end of file +end diff --git a/Gemfile.lock b/Gemfile.lock index cafba01..2612e5d 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -1,46 +1,48 @@ PATH remote: . specs: - practical (0.1.0) + practical (3.0.0.pre.alpha4) action_policy devise-passkeys honeybadger loaf oaken - pagy + pagy (>= 43.0) rails (>= 8.0.2) semantic_logger shrine view_component GEM - remote: https://rubygems.org/ + remote: https://gem.coop/ specs: action_policy (0.7.5) ruby-next-core (>= 1.0) - actioncable (8.0.2) - actionpack (= 8.0.2) - activesupport (= 8.0.2) + action_text-trix (2.1.19) + railties + actioncable (8.1.3) + actionpack (= 8.1.3) + activesupport (= 8.1.3) nio4r (~> 2.0) websocket-driver (>= 0.6.1) zeitwerk (~> 2.6) - actionmailbox (8.0.2) - actionpack (= 8.0.2) - activejob (= 8.0.2) - activerecord (= 8.0.2) - activestorage (= 8.0.2) - activesupport (= 8.0.2) + actionmailbox (8.1.3) + actionpack (= 8.1.3) + activejob (= 8.1.3) + activerecord (= 8.1.3) + activestorage (= 8.1.3) + activesupport (= 8.1.3) mail (>= 2.8.0) - actionmailer (8.0.2) - actionpack (= 8.0.2) - actionview (= 8.0.2) - activejob (= 8.0.2) - activesupport (= 8.0.2) + actionmailer (8.1.3) + actionpack (= 8.1.3) + actionview (= 8.1.3) + activejob (= 8.1.3) + activesupport (= 8.1.3) mail (>= 2.8.0) rails-dom-testing (~> 2.2) - actionpack (8.0.2) - actionview (= 8.0.2) - activesupport (= 8.0.2) + actionpack (8.1.3) + actionview (= 8.1.3) + activesupport (= 8.1.3) nokogiri (>= 1.8.5) rack (>= 2.2.4) rack-session (>= 1.0.1) @@ -48,42 +50,43 @@ GEM rails-dom-testing (~> 2.2) rails-html-sanitizer (~> 1.6) useragent (~> 0.16) - actiontext (8.0.2) - actionpack (= 8.0.2) - activerecord (= 8.0.2) - activestorage (= 8.0.2) - activesupport (= 8.0.2) + actiontext (8.1.3) + action_text-trix (~> 2.1.15) + actionpack (= 8.1.3) + activerecord (= 8.1.3) + activestorage (= 8.1.3) + activesupport (= 8.1.3) globalid (>= 0.6.0) nokogiri (>= 1.8.5) - actionview (8.0.2) - activesupport (= 8.0.2) + actionview (8.1.3) + activesupport (= 8.1.3) builder (~> 3.1) erubi (~> 1.11) rails-dom-testing (~> 2.2) rails-html-sanitizer (~> 1.6) - activejob (8.0.2) - activesupport (= 8.0.2) + activejob (8.1.3) + activesupport (= 8.1.3) globalid (>= 0.3.6) - activemodel (8.0.2) - activesupport (= 8.0.2) - activerecord (8.0.2) - activemodel (= 8.0.2) - activesupport (= 8.0.2) + activemodel (8.1.3) + activesupport (= 8.1.3) + activerecord (8.1.3) + activemodel (= 8.1.3) + activesupport (= 8.1.3) timeout (>= 0.4.0) - activestorage (8.0.2) - actionpack (= 8.0.2) - activejob (= 8.0.2) - activerecord (= 8.0.2) - activesupport (= 8.0.2) + activestorage (8.1.3) + actionpack (= 8.1.3) + activejob (= 8.1.3) + activerecord (= 8.1.3) + activesupport (= 8.1.3) marcel (~> 1.0) - activesupport (8.0.2) + activesupport (8.1.3) base64 - benchmark (>= 0.3) bigdecimal concurrent-ruby (~> 1.0, >= 1.3.1) connection_pool (>= 2.2.5) drb i18n (>= 1.6, < 2) + json logger (>= 1.4.2) minitest (>= 5.1) securerandom (>= 0.3) @@ -94,9 +97,8 @@ GEM android_key_attestation (0.3.0) ast (2.4.3) base64 (0.3.0) - bcrypt (3.1.20) - benchmark (0.4.1) - bigdecimal (3.2.1) + bcrypt (3.1.22) + bigdecimal (4.1.2) bindata (2.5.1) builder (3.3.0) capybara (3.40.0) @@ -116,21 +118,21 @@ GEM capybara-screenshot (1.0.26) capybara (>= 1.0, < 4) launchy - cbor (0.5.9.8) + cbor (0.5.9.9) childprocess (5.1.0) logger (~> 1.5) - concurrent-ruby (1.3.5) - connection_pool (2.5.3) + concurrent-ruby (1.3.8) + connection_pool (3.0.2) content_disposition (1.0.0) cose (1.3.1) cbor (~> 0.5.9) openssl-signature_algorithm (~> 1.0) - crass (1.0.6) + crass (1.0.7) dartsass-rails (0.5.1) railties (>= 6.0.0) sass-embedded (~> 1.63) - date (3.4.1) - debug (1.10.0) + date (3.5.1) + debug (1.11.0) irb (~> 1.10) reline (>= 0.3.8) devise (4.9.4) @@ -150,16 +152,16 @@ GEM down (5.4.2) addressable (~> 2.8) drb (2.2.3) - erb (5.0.1) + erb (6.0.6) erubi (1.13.1) - faker (3.5.1) + faker (3.5.2) i18n (>= 1.8.11, < 2) - flipper (1.3.4) + flipper (1.3.6) concurrent-ruby (< 2) - flipper-active_record (1.3.4) + flipper-active_record (1.3.6) activerecord (>= 4.2, < 9) - flipper (~> 1.3.4) - globalid (1.2.1) + flipper (~> 1.3.6) + globalid (1.4.0) activesupport (>= 6.1) google-protobuf (4.31.1) bigdecimal @@ -170,22 +172,23 @@ GEM google-protobuf (4.31.1-x86_64-linux-gnu) bigdecimal rake (>= 13) - honeybadger (5.28.0) + honeybadger (6.0.6) logger ostruct - i18n (1.14.7) + i18n (1.15.2) concurrent-ruby (~> 1.0) - io-console (0.8.0) - irb (1.15.2) + io-console (0.8.2) + irb (1.18.0) pp (>= 0.6.0) + prism (>= 1.3.0) rdoc (>= 4.0.0) reline (>= 0.4.2) jsbundling-rails (1.3.1) railties (>= 6.0.0) - json (2.12.2) - jwt (2.10.1) + json (2.21.1) + jwt (3.2.0) base64 - language_server-protocol (3.17.0.5) + language_server-protocol (3.17.0.6) launchy (3.1.1) addressable (~> 2.8) childprocess (~> 5.0) @@ -193,23 +196,25 @@ GEM lint_roller (1.1.0) loaf (0.10.0) railties (>= 3.2) - localhost (1.5.0) + localhost (1.6.0) logger (1.7.0) - loofah (2.24.1) + loofah (2.25.2) crass (~> 1.0.2) nokogiri (>= 1.12.0) - mail (2.8.1) + mail (2.9.1) + logger mini_mime (>= 0.1.1) net-imap net-pop net-smtp - marcel (1.0.4) - matrix (0.4.2) - method_source (1.1.0) + marcel (1.2.1) + matrix (0.4.3) mini_mime (1.1.5) mini_portile2 (2.8.9) - minitest (5.25.5) - net-imap (0.5.8) + minitest (6.0.6) + drb (~> 2.0) + prism (~> 1.5) + net-imap (0.6.6) date net-protocol net-pop (0.1.2) @@ -218,116 +223,121 @@ GEM timeout net-smtp (0.5.1) net-protocol - nio4r (2.7.4) - nokogiri (1.18.8) + nio4r (2.7.5) + nokogiri (1.19.4) mini_portile2 (~> 2.8.2) racc (~> 1.4) - nokogiri (1.18.8-arm64-darwin) + nokogiri (1.19.4-arm64-darwin) racc (~> 1.4) - nokogiri (1.18.8-x86_64-linux-gnu) + nokogiri (1.19.4-x86_64-linux-gnu) racc (~> 1.4) oaken (0.9.1) openssl (3.3.0) openssl-signature_algorithm (1.3.0) openssl (> 2.0) orm_adapter (0.5.0) - ostruct (0.6.1) - pagy (9.3.4) - parallel (1.27.0) - parser (3.3.8.0) + ostruct (0.6.3) + pagy (43.2.3) + json + yaml + parallel (2.1.0) + parser (3.3.12.0) ast (~> 2.4.1) racc - pp (0.6.2) + pp (0.6.4) prettyprint prettyprint (0.2.0) - prism (1.4.0) - propshaft (1.1.0) + prism (1.9.0) + propshaft (1.2.1) actionpack (>= 7.0.0) activesupport (>= 7.0.0) rack - railties (>= 7.0.0) - psych (5.2.6) - date - stringio public_suffix (6.0.2) - puma (6.6.0) + puma (8.0.2) nio4r (~> 2.0) racc (1.8.1) - rack (3.1.15) - rack-session (2.1.1) + rack (3.2.6) + rack-session (2.1.2) base64 (>= 0.1.0) rack (>= 3.0.0) rack-test (2.2.0) rack (>= 1.3) - rackup (2.2.1) + rackup (2.3.1) rack (>= 3) - rails (8.0.2) - actioncable (= 8.0.2) - actionmailbox (= 8.0.2) - actionmailer (= 8.0.2) - actionpack (= 8.0.2) - actiontext (= 8.0.2) - actionview (= 8.0.2) - activejob (= 8.0.2) - activemodel (= 8.0.2) - activerecord (= 8.0.2) - activestorage (= 8.0.2) - activesupport (= 8.0.2) + rails (8.1.3) + actioncable (= 8.1.3) + actionmailbox (= 8.1.3) + actionmailer (= 8.1.3) + actionpack (= 8.1.3) + actiontext (= 8.1.3) + actionview (= 8.1.3) + activejob (= 8.1.3) + activemodel (= 8.1.3) + activerecord (= 8.1.3) + activestorage (= 8.1.3) + activesupport (= 8.1.3) bundler (>= 1.15.0) - railties (= 8.0.2) + railties (= 8.1.3) rails-dom-testing (2.3.0) activesupport (>= 5.0.0) minitest nokogiri (>= 1.6) - rails-html-sanitizer (1.6.2) - loofah (~> 2.21) + rails-html-sanitizer (1.7.1) + loofah (~> 2.25, >= 2.25.2) nokogiri (>= 1.15.7, != 1.16.7, != 1.16.6, != 1.16.5, != 1.16.4, != 1.16.3, != 1.16.2, != 1.16.1, != 1.16.0.rc1, != 1.16.0) - railties (8.0.2) - actionpack (= 8.0.2) - activesupport (= 8.0.2) + railties (8.1.3) + actionpack (= 8.1.3) + activesupport (= 8.1.3) irb (~> 1.13) rackup (>= 1.0.0) rake (>= 12.2) thor (~> 1.0, >= 1.2.2) + tsort (>= 0.2) zeitwerk (~> 2.6) rainbow (3.1.1) - rake (13.3.0) - rdoc (6.14.0) + rake (13.4.2) + rbs (4.0.3) + logger + prism (>= 1.6.0) + tsort + rdoc (8.0.0) erb - psych (>= 4.0.0) - regexp_parser (2.10.0) - reline (0.6.1) + prism (>= 1.6.0) + rbs (>= 4.0.0) + tsort + regexp_parser (2.12.0) + reline (0.6.3) io-console (~> 0.5) responders (3.1.1) actionpack (>= 5.2) railties (>= 5.2) - rexml (3.4.1) - rubocop (1.75.8) + rexml (3.4.4) + rubocop (1.88.2) json (~> 2.3) language_server-protocol (~> 3.17.0.2) lint_roller (~> 1.1.0) - parallel (~> 1.10) + parallel (>= 1.10) parser (>= 3.3.0.2) rainbow (>= 2.2.2, < 4.0) regexp_parser (>= 2.9.3, < 3.0) - rubocop-ast (>= 1.44.0, < 2.0) + rubocop-ast (>= 1.49.0, < 2.0) ruby-progressbar (~> 1.7) unicode-display_width (>= 2.4.0, < 4.0) - rubocop-ast (1.44.1) + rubocop-ast (1.50.0) parser (>= 3.3.7.2) - prism (~> 1.4) + prism (~> 1.7) rubocop-capybara (2.22.1) lint_roller (~> 1.1) rubocop (~> 1.72, >= 1.72.1) - rubocop-minitest (0.38.1) + rubocop-minitest (0.40.0) lint_roller (~> 1.1) rubocop (>= 1.75.0, < 2.0) rubocop-ast (>= 1.38.0, < 2.0) - rubocop-performance (1.25.0) + rubocop-performance (1.26.1) lint_roller (~> 1.1) rubocop (>= 1.75.0, < 2.0) - rubocop-ast (>= 1.38.0, < 2.0) - rubocop-rails (2.32.0) + rubocop-ast (>= 1.47.1, < 2.0) + rubocop-rails (2.33.4) activesupport (>= 4.2.0) lint_roller (~> 1.1) rack (>= 1.1) @@ -340,27 +350,27 @@ GEM rubocop-rubycw (0.2.2) lint_roller (~> 1.1) rubocop (~> 1.72, >= 1.72.1) - ruby-next-core (1.1.1) + ruby-next-core (1.1.2) ruby-progressbar (1.13.0) ruby2_keywords (0.0.5) rubyzip (2.4.1) - safety_net_attestation (0.4.0) - jwt (~> 2.0) - sass-embedded (1.89.1) + safety_net_attestation (0.5.0) + jwt (>= 2.0, < 4.0) + sass-embedded (1.89.2) google-protobuf (~> 4.31) rake (>= 13) - sass-embedded (1.89.1-arm64-darwin) + sass-embedded (1.89.2-arm64-darwin) google-protobuf (~> 4.31) - sass-embedded (1.89.1-x86_64-linux-gnu) + sass-embedded (1.89.2-x86_64-linux-gnu) google-protobuf (~> 4.31) securerandom (0.4.1) - selenium-webdriver (4.33.0) + selenium-webdriver (4.34.0) base64 (~> 0.2) logger (~> 1.4) rexml (~> 3.2, >= 3.2.5) rubyzip (>= 1.2.2, < 3.0) websocket (~> 1.0) - semantic_logger (4.16.1) + semantic_logger (4.17.0) concurrent-ruby (~> 1.0) shrine (3.6.0) content_disposition (~> 1.0) @@ -369,53 +379,54 @@ GEM docile (~> 1.1) simplecov-html (~> 0.11) simplecov_json_formatter (~> 0.1) - simplecov-html (0.13.1) + simplecov-html (0.13.2) simplecov_json_formatter (0.1.4) spy (1.0.5) - sqlite3 (2.6.0) + sqlite3 (2.7.4) mini_portile2 (~> 2.8.0) - sqlite3 (2.6.0-arm64-darwin) - sqlite3 (2.6.0-x86_64-linux-gnu) - stringio (3.1.7) - thor (1.3.2) + sqlite3 (2.7.4-arm64-darwin) + sqlite3 (2.7.4-x86_64-linux-gnu) + thor (1.5.0) timecop (0.9.10) - timeout (0.4.3) - tpm-key_attestation (0.14.0) + timeout (0.6.1) + tpm-key_attestation (0.14.1) bindata (~> 2.4) openssl (> 2.0) openssl-signature_algorithm (~> 1.0) + tsort (0.2.0) tzinfo (2.0.6) concurrent-ruby (~> 1.0) - unicode-display_width (3.1.4) - unicode-emoji (~> 4.0, >= 4.0.4) - unicode-emoji (4.0.4) - uri (1.0.3) + unicode-display_width (3.2.0) + unicode-emoji (~> 4.1) + unicode-emoji (4.2.0) + uri (1.1.1) useragent (0.16.11) - view_component (3.23.2) - activesupport (>= 5.2.0, < 8.1) + view_component (4.12.0) + actionview (>= 7.1.0) + activesupport (>= 7.1.0) concurrent-ruby (~> 1) - method_source (~> 1.0) warden (1.2.9) rack (>= 2.0.9) warden-webauthn (0.3.0) warden webauthn (>= 3) - webauthn (3.4.0) + webauthn (3.4.3) android_key_attestation (~> 0.3.0) bindata (~> 2.4) cbor (~> 0.5.9) cose (~> 1.1) openssl (>= 2.2) - safety_net_attestation (~> 0.4.0) + safety_net_attestation (~> 0.5.0) tpm-key_attestation (~> 0.14.0) websocket (1.2.11) - websocket-driver (0.8.0) + websocket-driver (0.8.2) base64 websocket-extensions (>= 0.1.0) websocket-extensions (0.1.5) xpath (3.2.0) nokogiri (~> 1.8) - zeitwerk (2.7.3) + yaml (0.4.0) + zeitwerk (2.8.2) PLATFORMS arm64-darwin @@ -441,6 +452,7 @@ DEPENDENCIES practical! propshaft puma + rubocop rubocop-capybara rubocop-minitest rubocop-performance diff --git a/README.md b/README.md index 774d54d..54b361e 100644 --- a/README.md +++ b/README.md @@ -1,18 +1,18 @@ # `practical`: The Practical Framework -_This gem is currently being open-sourced from a private repo, while also being singificantly reorganized. Its 3.0 release will be live soon!_ +_This gem has been open-sourced from a private repo, while also being singificantly reorganized. The code is functional and (mostly) tested; but it is marked as alpha until it can be properly documented, and existing apps switched over to use 3.0_ ## Installation -TODO: Replace `UPDATE_WITH_YOUR_GEM_NAME_IMMEDIATELY_AFTER_RELEASE_TO_RUBYGEMS_ORG` with your gem name right after releasing it to RubyGems.org. Please do not do it earlier due to security reasons. Alternatively, replace this section with instructions to install your gem from git if you don't plan to release to RubyGems.org. +TODO: Replace `practical` with your gem name right after releasing it to RubyGems.org. Please do not do it earlier due to security reasons. Alternatively, replace this section with instructions to install your gem from git if you don't plan to release to RubyGems.org. Install the gem and add to the application's Gemfile by executing: - $ bundle add UPDATE_WITH_YOUR_GEM_NAME_IMMEDIATELY_AFTER_RELEASE_TO_RUBYGEMS_ORG + $ bundle add practical --source "https://beta.gem.coop/@practical-computer" If bundler is not being used to manage dependencies, install the gem by executing: - $ gem install UPDATE_WITH_YOUR_GEM_NAME_IMMEDIATELY_AFTER_RELEASE_TO_RUBYGEMS_ORG + $ gem "practical", source: "https://beta.gem.coop/@practical-computer" ## Usage diff --git a/app/components/practical/views/button_to_component.rb b/app/components/practical/views/button_to_component.rb new file mode 100644 index 0000000..4b44c3b --- /dev/null +++ b/app/components/practical/views/button_to_component.rb @@ -0,0 +1,23 @@ +# frozen_string_literal: true + +class Practical::Views::ButtonToComponent < Practical::Views::BaseComponent + include Practical::Views::Button::Styling + attr_accessor :url, :options, :appearance, :color_variant, :size, :html_options + + def initialize(url:, options:, appearance: nil, color_variant: nil, size: nil, html_options: {}) + self.url = url + self.options = options + self.html_options = html_options + initialize_style_utilities(appearance: appearance, color_variant: color_variant, size: size) + end + + def call + html_option_defaults = { + class: css_classes_from_style_utilities, + data: {disable: true} + } + + finalized_html_options = mix(html_option_defaults, html_options) + helpers.button_to(url, **options, **finalized_html_options) { content } + end +end diff --git a/app/components/practical/views/flash_messages_component.rb b/app/components/practical/views/flash_messages_component.rb index d924825..4e0b7fd 100644 --- a/app/components/practical/views/flash_messages_component.rb +++ b/app/components/practical/views/flash_messages_component.rb @@ -2,15 +2,20 @@ class Practical::Views::FlashMessagesComponent < Practical::Views::BaseComponent def call + messages = toasts tag.aside(class: 'notification-messages wa-stack') do - safe_join([ - alert_toast, - notice_toast, - success_toast, - ]) + safe_join(messages) if messages.present? end end + def toasts + [ + alert_toast, + notice_toast, + success_toast, + ].compact + end + def success_toast render_toast( color_variant: :success, @@ -51,8 +56,11 @@ def render_toast(color_variant:, data:, default_icon:) component = Practical::Views::ToastComponent.new(color_variant: color_variant) render component do |component| - if icon.present? && icon.is_a?(Hash) - icon = Practical::Views::IconComponent.new(**icon.to_h.symbolize_keys) + if icon.present? && (icon.is_a?(Hash) || icon.is_a?(ViewComponent::Base)) + if icon.is_a?(Hash) + icon = Practical::Views::IconComponent.new(**icon.to_h.symbolize_keys) + end + component.with_icon do render icon end diff --git a/app/components/practical/views/form/error_list_component.rb b/app/components/practical/views/form/error_list_component.rb index d1c9c5b..2ee587e 100644 --- a/app/components/practical/views/form/error_list_component.rb +++ b/app/components/practical/views/form/error_list_component.rb @@ -8,7 +8,7 @@ def initialize(errors:) end def call - tag.ul(class: 'error-list') { + tag.ul { safe_join(errors.map{|error| render Practical::Views::Form::ErrorListItemComponent.new(error: error) }) } end diff --git a/app/components/practical/views/form/error_list_item_component.rb b/app/components/practical/views/form/error_list_item_component.rb index bb2c774..db7f232 100644 --- a/app/components/practical/views/form/error_list_item_component.rb +++ b/app/components/practical/views/form/error_list_item_component.rb @@ -12,9 +12,8 @@ def before_render end def call - tag.li(class: 'wa-flank', data: {"error-type": error.type}) { - render(icon_set.error_list_icon) + - tag.span(error.message) + tag.li(data: {"pf-error-type": error.type, "pf-error-visible": true}) { + tag.span(error.message, data: { "pf-error-message": true }) } end end diff --git a/app/components/practical/views/form/error_list_item_template_component.rb b/app/components/practical/views/form/error_list_item_template_component.rb index cab3a98..05c03d7 100644 --- a/app/components/practical/views/form/error_list_item_template_component.rb +++ b/app/components/practical/views/form/error_list_item_template_component.rb @@ -2,7 +2,7 @@ class Practical::Views::Form::ErrorListItemTemplateComponent < Practical::Views::BaseComponent def call - tag.template(id: 'error-list-item-template') { + tag.template(id: 'pf-error-list-item-template') { render Practical::Views::Form::ErrorListItemComponent.new(error: ActiveModel::Error.new(nil, nil, nil)) } end diff --git a/app/components/practical/views/form/fallback_errors_section_component.html.erb b/app/components/practical/views/form/fallback_errors_section_component.html.erb index dd5701f..f253302 100644 --- a/app/components/practical/views/form/fallback_errors_section_component.html.erb +++ b/app/components/practical/views/form/fallback_errors_section_component.html.erb @@ -1,7 +1,10 @@ -<%= tag.section(**finalized_options) do %> -
- <%= blurb %> -
+<%= tag.wa_callout(variant: :danger) do %> + <%= render icon_set.error_callout_icon %> + <%= tag.section(**finalized_options) do %> +
+ <%= blurb %> +
- <%= render Practical::Views::Form::ErrorListComponent.new(errors: remaining_errors) %> + <%= render Practical::Views::Form::ErrorListComponent.new(errors: remaining_errors) %> + <% end %> <% end %> \ No newline at end of file diff --git a/app/components/practical/views/form/fallback_errors_section_component.rb b/app/components/practical/views/form/fallback_errors_section_component.rb index 2c91007..b51d95a 100644 --- a/app/components/practical/views/form/fallback_errors_section_component.rb +++ b/app/components/practical/views/form/fallback_errors_section_component.rb @@ -11,7 +11,7 @@ def initialize(f:, id:, blurb:, options:) def finalized_options mix({ - class: ["error-section", "fallback-error-section", "wa-callout", "wa-danger"], + data: {"pf-error-container": true, "pf-fallback-error-section": true}, id: id }, @options) end diff --git a/app/components/practical/views/form/field_errors_component.html.erb b/app/components/practical/views/form/field_errors_component.html.erb new file mode 100644 index 0000000..940a190 --- /dev/null +++ b/app/components/practical/views/form/field_errors_component.html.erb @@ -0,0 +1,8 @@ + + <%= render icon_set.error_callout_icon %> + + <%= label(object_method, nil, finalized_options) do %> +
<%= multiple_errors_blurb %>
+ <%= render Practical::Views::Form::ErrorListComponent.new(errors: errors) %> + <% end %> +
\ No newline at end of file diff --git a/app/components/practical/views/form/field_errors_component.rb b/app/components/practical/views/form/field_errors_component.rb index 2ddeafd..cdc4887 100644 --- a/app/components/practical/views/form/field_errors_component.rb +++ b/app/components/practical/views/form/field_errors_component.rb @@ -1,28 +1,31 @@ # frozen_string_literal: true class Practical::Views::Form::FieldErrorsComponent < Practical::Views::BaseComponent - attr_reader :f, :object_method, :options + attr_reader :f, :object_method, :multiple_errors_blurb, :options - def initialize(f:, object_method:, options:) + def initialize(f:, object_method:, multiple_errors_blurb:, options:) @f = f @object_method = object_method + @multiple_errors_blurb = multiple_errors_blurb @options = options end - def call + def errors + f.errors_for(object_method) || [] + end + + def finalized_options id = f.field_errors_id(object_method) - classes = ["error-section", "wa-callout", "wa-danger"] - errors = f.errors_for(object_method) + classes = ["error-section"] if errors.blank? classes << ["no-server-errors"] - errors = [] end - finalized_options = mix({id: id, class: classes}, options) - - return label(object_method, nil, finalized_options) { - render Practical::Views::Form::ErrorListComponent.new(errors: errors) - } + return mix({ + id: id, + class: classes, + data: {'pf-error-container': true} + }, options) end end diff --git a/app/components/practical/views/form/input_component.html.erb b/app/components/practical/views/form/input_component.html.erb index e80f66f..74674db 100644 --- a/app/components/practical/views/form/input_component.html.erb +++ b/app/components/practical/views/form/input_component.html.erb @@ -1,4 +1,4 @@ -
+
<%= tag.label(**label_options) do %> <%= label %> <%= field %> diff --git a/app/components/practical/views/form/input_component.rb b/app/components/practical/views/form/input_component.rb index 85b3e67..1d3e5f3 100644 --- a/app/components/practical/views/form/input_component.rb +++ b/app/components/practical/views/form/input_component.rb @@ -1,6 +1,7 @@ # frozen_string_literal: true class Practical::Views::Form::InputComponent < Practical::Views::BaseComponent + class FieldOptionsNotCalledError < StandardError; end attr_accessor :f, :object_method, :label_options renders_one :label @@ -17,6 +18,19 @@ def field_errors_id end def field_options(**options) - mix({"aria-describedby": field_errors_id}, options) + @_field_options_called = true + return mix({ + "aria-describedby": field_errors_id, + "data": {"pf-initial-load-errors": f.errors_for(object_method)&.any? } + }, options) + end + + def around_render + result = yield + unless @_field_options_called + raise FieldOptionsNotCalledError, "field_options was not called when rendering an InputComponent; which means this the input & its error container are not connected." + end + + return result end end diff --git a/app/components/practical/views/modal_dialog_component.html.erb b/app/components/practical/views/modal_dialog_component.html.erb index 3ab1cdd..177d2c7 100644 --- a/app/components/practical/views/modal_dialog_component.html.erb +++ b/app/components/practical/views/modal_dialog_component.html.erb @@ -1,5 +1,5 @@ <%= tag.dialog(**finalized_options) do %> -
+