Skip to content

Commit

Permalink
[RMS 2] Tailored Select Generator (#129)
Browse files Browse the repository at this point in the history
This PR adds a new generator for the Tailored Select package. It also adds some developer tools around it for using it in tests and with simple form.

---------

Co-authored-by: Braden Rich <[email protected]>
  • Loading branch information
Jeremy-Walton and Braden-077 authored Aug 30, 2024
1 parent f594ba2 commit 7a205cb
Show file tree
Hide file tree
Showing 15 changed files with 191 additions and 10 deletions.
2 changes: 1 addition & 1 deletion Gemfile.lock
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
PATH
remote: .
specs:
rolemodel_rails (0.9.0)
rolemodel_rails (0.10.0)

GEM
remote: https://rubygems.org/
Expand Down
1 change: 1 addition & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -92,6 +92,7 @@ bin/rails g
* [Kaminari](./lib/generators/rolemodel/kaminari)
* [GoodJob](./lib/generators/rolemodel/good_job)
* [Editors](./lib/generators/rolemodel/editors)
* [Tailored Select](./lib/generators/rolemodel/tailored_select)

## Development

Expand Down
4 changes: 2 additions & 2 deletions example_rails7/Gemfile
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
source "https://rubygems.org"
git_source(:github) { |repo| "https://github.com/#{repo}.git" }

ruby "3.2.2"
ruby "3.3.0"

# Bundle edge Rails instead: gem "rails", github: "rails/rails", branch: "main"
gem "rails", "~> 7.0.5"
Expand Down Expand Up @@ -68,4 +68,4 @@ group :test do
gem "webdrivers"
end

gem 'rolemodel_rails', '~> 0.8.0', group: :development, path: '..'
gem 'rolemodel_rails', '~> 0.10.0', group: :development, path: '..'
10 changes: 6 additions & 4 deletions example_rails7/Gemfile.lock
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
PATH
remote: ..
specs:
rolemodel_rails (0.8.0)
rolemodel_rails (0.10.0)

GEM
remote: https://rubygems.org/
Expand Down Expand Up @@ -114,6 +114,7 @@ GEM
matrix (0.4.2)
method_source (1.0.0)
mini_mime (1.1.2)
mini_portile2 (2.8.7)
minitest (5.18.0)
msgpack (1.7.1)
net-imap (0.3.6)
Expand All @@ -126,7 +127,8 @@ GEM
net-smtp (0.3.3)
net-protocol
nio4r (2.5.9)
nokogiri (1.15.2-arm64-darwin)
nokogiri (1.15.2)
mini_portile2 (~> 2.8.2)
racc (~> 1.4)
pg (1.5.3)
public_suffix (5.0.1)
Expand Down Expand Up @@ -221,7 +223,7 @@ DEPENDENCIES
puma (~> 5.0)
rails (~> 7.0.5)
redis (~> 4.0)
rolemodel_rails (~> 0.8.0)!
rolemodel_rails (~> 0.10.0)!
selenium-webdriver
sprockets-rails
stimulus-rails
Expand All @@ -231,7 +233,7 @@ DEPENDENCIES
webdrivers

RUBY VERSION
ruby 3.2.2p53
ruby 3.3.0p0

BUNDLED WITH
2.4.14
1 change: 1 addition & 0 deletions lib/generators/rolemodel/all_generator.rb
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ def run_all_the_generators
generate 'rolemodel:good_job'
generate 'rolemodel:kaminari'
generate 'rolemodel:editors'
generate 'rolemodel:tailored_select'
end
end
end
Original file line number Diff line number Diff line change
@@ -0,0 +1,75 @@
# frozen_string_literal: true

# TailoredSelectInput is a custom input type for SimpleForm that renders a tailored select web component
#
# Options:
# Any options available for SimpleForm's CollectionSelectInput (A.K.A a normal HTML select)
#
# Usage:
# <%= f.input :my_field, as: :tailored_select %>

module ActionView
module Helpers
class FormBuilder
def tailored_select(method, collection, value_method, text_method, options = {}, html_options = {})
@template.tailored_select(@object_name, method, collection, value_method, text_method,
objectify_options(options), @default_html_options.merge(html_options))
end
end

module FormOptionsHelper
def tailored_select(object, method, collection, value_method, text_method, options = {}, html_options = {})
Tags::TailoredSelect.new(object, method, self, collection, value_method, text_method, options,
html_options).render
end
end

module Tags
class TailoredSelect < CollectionSelect
private

def select_content_tag(option_tags, options, html_options)
html_options = html_options.stringify_keys
%i[required multiple size].each do |prop|
html_options[prop.to_s] = options.delete(prop) if options.key?(prop) && !html_options.key?(prop.to_s)
end

add_default_name_and_id(html_options)

if placeholder_required?(html_options)
if options[:include_blank] == false
raise ArgumentError,
'include_blank cannot be false for a required field.'
end

options[:include_blank] ||= true unless options[:prompt]
end

value = options.fetch(:selected) { value() }
# For real, this is the only line that changed from CollectionSelect, just changed the tag name
select = content_tag('tailored-select', add_options(option_tags, options, value), html_options)

if html_options['multiple'] && options.fetch(:include_hidden, true)
tag('input', disabled: html_options['disabled'], name: html_options['name'], type: 'hidden', value: '',
autocomplete: 'off') + select
else
select
end
end
end
end
end
end

class TailoredSelectInput < SimpleForm::Inputs::CollectionSelectInput
def input(wrapper_options = nil)
@builder.tailored_select(
attribute_name, collection, *detect_collection_methods.reverse, input_options,
merge_wrapper_options(input_html_options, wrapper_options)
)
end

def input_html_classes
[]
end
end
7 changes: 7 additions & 0 deletions lib/generators/rolemodel/tailored_select/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
# TailoredSelect Generator

`rails g rolemodel:tailored_select`

## What you get

The [Tailored Select](https://github.com/RoleModel/tailored-select) web component
5 changes: 5 additions & 0 deletions lib/generators/rolemodel/tailored_select/USAGE
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
Description:
runs the tailored select generator

Example:
rails generate rolemodel:tailored_select
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
# frozen_string_literal: true

module Rolemodel
class TailoredSelectGenerator < Rails::Generators::Base
source_root File.expand_path('templates', __dir__)

def add_tailored_select_package
say 'Installing Tailored Select package', :green

run 'yarn add @rolemodel/tailored-select'
end
end
end
3 changes: 2 additions & 1 deletion lib/generators/rolemodel/testing/rspec/rspec_generator.rb
Original file line number Diff line number Diff line change
Expand Up @@ -25,8 +25,9 @@ def add_spec_files
template '.rspec', '.rspec'
template 'support/capybara_drivers.rb', 'spec/support/capybara_drivers.rb'
template 'support/capybara_testid.rb', 'spec/support/capybara_testid.rb'
template 'support/helpers/test_element_helper.rb', 'spec/support/helpers/test_element_helper.rb'
template 'support/helpers/action_cable_helper.rb', 'spec/support/helpers/action_cable_helper.rb'
template 'support/helpers/select_helper.rb', 'spec/support/helpers/select_helper.rb'
template 'support/helpers/test_element_helper.rb', 'spec/support/helpers/test_element_helper.rb'
template 'support/helpers.rb', 'spec/support/helpers.rb'
template 'support/webpacker.rb', 'spec/support/webpacker.rb'
append_file '.gitignore', 'spec/examples.txt'
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@

RSpec.configure do |c|
# for example, given you have a spec/support/helpers/login_helpers.rb
c.include TestElementHelper, type: :system
c.include ActionCableHelper, type: :system
c.include SelectHelper, type: :system
c.include TestElementHelper, type: :system
end
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
# frozen_string_literal: true

module SelectHelper
include Capybara::DSL

def smart_select(value, from:)
label = find(:label, text: from, match: :first)
input = find(id: label['for'], visible: :all)

if input.tag_name == 'input' && input.native.attribute(:'aria-controls').include?('ts-dropdown')
tom_select(input, value)
elsif input.tag_name == 'tailored-select'
tailored_select(input, value)
else
regular_select(value.to_s, from:)
end
end

def tailored_select(input, value)
option = input.find(:option, value, visible: :all)
execute_script('arguments[0].click();', option)
end

def tom_select(input, value)
input.ancestor('.ts-control').click
input.send_keys(value)
sleep 0.5 # required due to bug in tom-select
input.send_keys(:enter)
end

alias regular_select select
alias select smart_select
end
2 changes: 1 addition & 1 deletion lib/rolemodel_rails/version.rb
Original file line number Diff line number Diff line change
@@ -1,3 +1,3 @@
module RolemodelRails
VERSION = '0.9.0'
VERSION = '0.10.0'
end
21 changes: 21 additions & 0 deletions spec/generators/rolemodel/rspec_generator_spec.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
require 'spec_helper'
require 'generators/rolemodel/testing/rspec/rspec_generator'

RSpec.describe Rolemodel::Testing::RspecGenerator, type: :generator do
destination File.expand_path('tmp/', File.dirname(__FILE__))

before(:all) do
prepare_test_app
run_generator
end

after(:all) do
cleanup_test_app
end

it 'adds the correct helpers' do
assert_file 'spec/support/helpers/action_cable_helper.rb'
assert_file 'spec/support/helpers/select_helper.rb'
assert_file 'spec/support/helpers/test_element_helper.rb'
end
end
21 changes: 21 additions & 0 deletions spec/generators/rolemodel/tailored_select_generator_spec.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
require 'spec_helper'
require 'generators/rolemodel/tailored_select/tailored_select_generator'

RSpec.describe Rolemodel::TailoredSelectGenerator, type: :generator do
destination File.expand_path('tmp/', File.dirname(__FILE__))

before(:all) do
prepare_test_app
FileUtils.cd(destination_root) { run_generator }
end

after(:all) do
cleanup_test_app
end

it 'adds tailored select to package.json' do
assert_file 'package.json' do |content|
expect(content).to include('"@rolemodel/tailored-select":')
end
end
end

0 comments on commit 7a205cb

Please sign in to comment.