bootstrap_feedbacker 0.5.0 → 1.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 7fcf1b3b1b691f4919e562650859e0409029a94668c6e987ee9471c3369c6a4a
4
- data.tar.gz: 50500796c58e2858b18f418cda4ec9e9ef14cdadf450ae361ff9a3743a91449e
3
+ metadata.gz: 030fbf4be8402a47970dcc5222361857019f2314757df81ae10068b163f0ddcc
4
+ data.tar.gz: 442e62e32fe40bdf7551506e79354128b36a1d47ccb33f582d33a8a3a45e5a45
5
5
  SHA512:
6
- metadata.gz: 9ab358d81eda954c83a746848e6e591a34459f0988b26ed17617d537b718bbfc1159ff0e47e947348aedd5087300c8ad7ac3378272ef2463e75fd55524c24d77
7
- data.tar.gz: f9c6b739bca165bb1cf3397499b92cd57f19e2b953580f855625d1743bee529fe55eca554a4f204f1ece00f1cdfcf0bb8c7e84504940e59dfb0154b726f1b86b
6
+ metadata.gz: d3b049102364ba8a80c662de058f75170a5826362f461849f656a45761ac341d866e0e2ca1fdc2f7f2acc32b528f65e77383fdab5840d4c88980b5d1b9e4c504
7
+ data.tar.gz: 37baa68a20025d9e6390c33b77fdc077d0ac4ffa6e0015ceaae00cb059a98c299c6235b19ef5315ceff931691e943a08aa9cb894761fc11bc852272cbaf2b783
data/README.md CHANGED
@@ -1,59 +1,81 @@
1
- [![Gem Version](https://badge.fury.io/rb/bootstrap_feedbacker.png)](http://badge.fury.io/rb/bootstrap_feedbacker)
2
- [![Code Climate](https://codeclimate.com/github/wwidea/bootstrap_feedbacker.png)](https://codeclimate.com/github/wwidea/bootstrap_feedbacker)
3
-
4
1
  # BootstrapFeedbacker
5
- BootstrapFeedbacker provides a modal form to collect user feedback and sends it in an email including the current page URL to the configured email address.
2
+
3
+ BootstrapFeedbacker displays a modal form for collecting feedback and emails it to a configured
4
+ address, along with the referring page URL (when available) and the current user's details.
6
5
 
7
6
  ## Requirements
8
7
 
9
- * Rails 6.0 or higher with Import Maps, Turbo Frames, and Stimulus
10
- * Bootstrap version 4
11
- * **User** model with an **email** attribute
12
- * **current_user** method defined in **ApplicationController**
8
+ - Ruby 3.3 or later and Rails 7.1 or later
9
+ - Import Maps, Turbo, and Stimulus
10
+ - Bootstrap 5 CSS and JavaScript, with `window.bootstrap.Modal` available
11
+ - A user model with an `email` attribute and a name method (default: `name`)
12
+ - A `current_user` method available to the application's controllers
13
+ - Action Mailer configured to deliver email
13
14
 
14
15
  ## Installation
15
16
 
16
- 1. Add the `bootstrap_feedbacker` gem to your Gemfile:
17
- ```ruby
18
- gem "bootstrap_feedbacker"
19
- ```
17
+ 1. Add the gem to your Gemfile and run `bundle install`:
20
18
 
21
- 2. Run `bundle install`.
19
+ ```ruby
20
+ gem "bootstrap_feedbacker"
21
+ ```
22
22
 
23
- 3. Run `rails bootstrap_feedbacker:install:migrations`.
23
+ 2. Install and run the migrations:
24
24
 
25
- 4. Run `rails db:migrate` to add the **bootstrap_feedbacker_remarks** table to your schema.
25
+ ```sh
26
+ bin/rails bootstrap_feedbacker:install:migrations
27
+ bin/rails db:migrate
28
+ ```
26
29
 
27
- 5. Mount the engine in your **routes.rb** file:
28
- ```ruby
29
- mount BootstrapFeedbacker::Engine, at: '/bootstrap_feedbacker'
30
- ```
30
+ 3. Mount the engine in `config/routes.rb`:
31
31
 
32
- 6. Add the bootstrap feedbacker link and modal container to a view file:
33
- ```erb
34
- <%= bootstrap_feedbacker_link %>
35
- <%= bootstrap_feedbacker_modal %>
36
- ```
32
+ ```ruby
33
+ mount BootstrapFeedbacker::Engine, at: "/bootstrap_feedbacker"
34
+ ```
35
+
36
+ 4. Include Bootstrap 5 CSS and JavaScript in your application. For example, with the Bootstrap
37
+ gem's JavaScript assets and Import Maps, pin the non-ES module build in `config/importmap.rb`:
38
+
39
+ ```ruby
40
+ pin "bootstrap", to: "bootstrap.min.js"
41
+ ```
42
+
43
+ Import it in `app/javascript/application.js`:
44
+
45
+ ```javascript
46
+ import "bootstrap"
47
+ ```
48
+
49
+ The gem's Stimulus controller uses `window.bootstrap.Modal` to open the form. Ensure your
50
+ application also loads its Stimulus controllers, for example with `lazyLoadControllersFrom`.
51
+
52
+ 5. Add the feedback link and modal container to a view:
53
+
54
+ ```erb
55
+ <%= bootstrap_feedbacker_link %>
56
+ <%= bootstrap_feedbacker_modal %>
57
+ ```
37
58
 
38
- ## Configure
59
+ ## Configuration
39
60
 
40
- Create `config/initializersbootstrap_feedbacker.rb` and set your email address:
61
+ Set the recipient address in `config/initializers/bootstrap_feedbacker.rb`:
41
62
 
42
63
  ```ruby
43
- BootstrapFeedbacker::SETTINGS.email_to = 'support@yourdomain.com'
64
+ BootstrapFeedbacker::SETTINGS.email_to = "support@example.com"
44
65
  ```
45
66
 
46
- Or set your email address and override any of these default settings:
67
+ The same address is used as the sender; replies go to the current user's email address.
68
+ You can also configure these settings:
47
69
 
48
70
  ```ruby
49
- BootstrapFeedbacker::SETTINGS.update do |settings|
50
- settings.email_to = 'support@yourdomain.com'
51
- settings.email_prefix = '[FEEDBACKER]' # default
52
- settings.modal_content_id = 'bootstrap_feedbacker_remark_modal_content' # default
53
- settings.modal_id = 'bootstrap_feedbacker_remark_modal' # default
54
- settings.user_class = 'User' # default
55
- settings.user_name_method = :name # default
56
- end
71
+ BootstrapFeedbacker::SETTINGS.update do |settings|
72
+ settings.email_to = "support@example.com"
73
+ settings.email_prefix = "[FEEDBACKER]" # default
74
+ settings.modal_content_id = "bootstrap_feedbacker_remark_modal_content" # default
75
+ settings.modal_id = "bootstrap_feedbacker_remark_modal" # default
76
+ settings.user_class = "User" # default
77
+ settings.user_name_method = :name # default
78
+ end
57
79
  ```
58
80
 
59
81
  ## License
data/Rakefile CHANGED
@@ -1,8 +1,8 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require "bundler/setup"
2
4
 
3
5
  APP_RAKEFILE = File.expand_path("test/dummy/Rakefile", __dir__)
4
6
  load "rails/tasks/engine.rake"
5
7
 
6
- load "rails/tasks/statistics.rake"
7
-
8
8
  require "bundler/gem_tasks"
@@ -5,6 +5,6 @@ export default class extends Controller {
5
5
  static targets = [ "modal" ]
6
6
 
7
7
  modalTargetConnected(element) {
8
- $(element).modal('show')
8
+ window.bootstrap.Modal.getOrCreateInstance(element).show()
9
9
  }
10
10
  }
@@ -4,16 +4,16 @@ module BootstrapFeedbacker
4
4
  class RemarksController < ApplicationController
5
5
  def new
6
6
  @remark = BootstrapFeedbacker::Remark.new
7
- render_update(SETTINGS.modal_id, partial: 'new')
7
+ render_update(SETTINGS.modal_id, partial: "new")
8
8
  end
9
9
 
10
10
  def create
11
11
  @remark = Remark.new(remark_params)
12
12
  if @remark.save
13
13
  FeedbackMailer.feedback(@remark).deliver_now
14
- render_update(SETTINGS.modal_content_id, partial: 'thank_you')
14
+ render_update(SETTINGS.modal_content_id, partial: "thank_you")
15
15
  else
16
- render_update(SETTINGS.modal_content_id, partial: 'error', status: :unprocessable_entity)
16
+ render_update(SETTINGS.modal_content_id, partial: "error", status: :unprocessable_content)
17
17
  end
18
18
  end
19
19
 
@@ -22,12 +22,12 @@ module BootstrapFeedbacker
22
22
  def remark_params
23
23
  params.require(:remark).permit(:content).merge(
24
24
  user_id: current_user.id,
25
- source_url: request.env['HTTP_REFERER'] || 'no referrer'
25
+ source_url: request.env["HTTP_REFERER"] || "no referrer"
26
26
  )
27
27
  end
28
28
 
29
29
  def render_update(dom_id, partial:, status: :ok)
30
- render(turbo_stream: turbo_stream.update(dom_id, partial: partial), status:)
30
+ render(turbo_stream: turbo_stream.update(dom_id, partial:), status:)
31
31
  end
32
32
  end
33
33
  end
@@ -1,7 +1,9 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module BootstrapFeedbacker
2
4
  module ModalsHelper
3
- def bootstrap_feedbacker_link(name: 'Feedback', **html_options)
4
- link_to(name, bootstrap_feedbacker.new_remark_path, 'data-turbo-stream': true, **html_options)
5
+ def bootstrap_feedbacker_link(name: "Feedback", **html_options)
6
+ link_to(name, bootstrap_feedbacker.new_remark_path, "data-turbo-stream": true, **html_options)
5
7
  end
6
8
 
7
9
  def bootstrap_feedbacker_modal
@@ -0,0 +1,7 @@
1
+ # frozen_string_literal: true
2
+
3
+ module BootstrapFeedbacker
4
+ class ApplicationMailer < ActionMailer::Base
5
+ layout "bootstrap_feedbacker/mailer"
6
+ end
7
+ end
@@ -1,14 +1,14 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module BootstrapFeedbacker
4
- class FeedbackMailer < ActionMailer::Base
4
+ class FeedbackMailer < ApplicationMailer
5
5
  def feedback(remark)
6
6
  @remark = remark
7
7
  mail(
8
- to: BootstrapFeedbacker::SETTINGS.email_to,
9
- from: BootstrapFeedbacker::SETTINGS.email_to,
10
- reply_to: remark.user.email,
11
- subject: "#{BootstrapFeedbacker::SETTINGS.email_prefix} Feedback Notification"
8
+ to: BootstrapFeedbacker::SETTINGS.email_to,
9
+ from: BootstrapFeedbacker::SETTINGS.email_to,
10
+ reply_to: remark.user.email,
11
+ subject: "#{BootstrapFeedbacker::SETTINGS.email_prefix} Feedback Notification"
12
12
  )
13
13
  end
14
14
  end
@@ -0,0 +1,7 @@
1
+ # frozen_string_literal: true
2
+
3
+ module BootstrapFeedbacker
4
+ class ApplicationRecord < ActiveRecord::Base
5
+ self.abstract_class = true
6
+ end
7
+ end
@@ -1,11 +1,11 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module BootstrapFeedbacker
4
- class Remark < ActiveRecord::Base
5
- belongs_to :user, class_name: BootstrapFeedbacker::SETTINGS.user_class
4
+ class Remark < BootstrapFeedbacker::ApplicationRecord
5
+ belongs_to :user, class_name: BootstrapFeedbacker::SETTINGS.user_class.to_s
6
6
 
7
7
  def user_name
8
- return user.send(BootstrapFeedbacker::SETTINGS.user_name_method)
8
+ user.send(BootstrapFeedbacker::SETTINGS.user_name_method)
9
9
  end
10
10
  end
11
11
  end
@@ -0,0 +1,7 @@
1
+ Feedback Received
2
+
3
+ From: <%= @remark.user_name %> [<%= @remark.user.email %>]
4
+ Page: <%= @remark.source_url %>
5
+
6
+ Comments:
7
+ <%= @remark.content %>
@@ -1 +1 @@
1
- <button type="button" class="btn btn-info" data-dismiss="modal">Close</button>
1
+ <button type="button" class="btn btn-secondary" data-bs-dismiss="modal">Close</button>
@@ -1,6 +1,6 @@
1
1
  <div class="modal-body text-danger">
2
- <%= t('.message') %>
2
+ <%= t(".message") %>
3
3
  </div>
4
4
  <div class="modal-footer">
5
- <%= render('close_button') %>
5
+ <%= render("close_button") %>
6
6
  </div>
@@ -1,12 +1,12 @@
1
1
  <%= form_for(@remark) do |f| %>
2
2
  <div class="modal-body">
3
3
  <div class="mb-2">
4
- <%= t('.greeting') %>
4
+ <%= t(".greeting") %>
5
5
  </div>
6
- <%= f.text_area :content, class: "form-control" %>
6
+ <%= f.text_area(:content, class: "form-control") %>
7
7
  </div>
8
8
  <div class="modal-footer">
9
- <%= f.submit('Submit Feedback', class: "btn btn-success") %>
10
- <%= render('close_button') %>
9
+ <%= render("close_button") %>
10
+ <%= f.submit("Submit Feedback", class: "btn btn-success") %>
11
11
  </div>
12
12
  <% end %>
@@ -1,15 +1,19 @@
1
1
  <div data-controller="bootstrap-feedbacker--remarks">
2
- <div class="modal fade" tabindex="-1" role="dialog" aria-labelledby="modalFeedbackLabel" data-bootstrap-feedbacker--remarks-target="modal">
3
- <div class="modal-dialog" role="document">
2
+ <div
3
+ class="modal fade"
4
+ tabindex="-1"
5
+ aria-labelledby="modalFeedbackLabel"
6
+ aria-hidden="true"
7
+ data-bootstrap-feedbacker--remarks-target="modal"
8
+ >
9
+ <div class="modal-dialog">
4
10
  <div class="modal-content">
5
11
  <div class="modal-header">
6
- <h4 class="modal-title" id="modalFeedbackLabel"><%= t('.title') %></h4>
7
- <button type="button" class="close" data-dismiss="modal" aria-label="Close">
8
- <span aria-hidden="true">&times;</span>
9
- </button>
12
+ <h1 class="modal-title fs-5" id="modalFeedbackLabel"><%= t(".title") %></h1>
13
+ <button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button>
10
14
  </div>
11
- <%= tag.div id: BootstrapFeedbacker::SETTINGS.modal_content_id do %>
12
- <%= render('form') %>
15
+ <%= tag.div(id: BootstrapFeedbacker::SETTINGS.modal_content_id) do %>
16
+ <%= render("form") %>
13
17
  <% end %>
14
18
  </div>
15
19
  </div>
@@ -1,6 +1,6 @@
1
1
  <div class="modal-body text-success">
2
- <%= t('.message') %>
2
+ <%= t(".message") %>
3
3
  </div>
4
4
  <div class="modal-footer">
5
- <%= render('close_button') %>
5
+ <%= render("close_button") %>
6
6
  </div>
@@ -0,0 +1,10 @@
1
+ <!DOCTYPE html>
2
+ <html>
3
+ <head>
4
+ <meta http-equiv="Content-Type" content="text/html; charset=utf-8">
5
+ </head>
6
+
7
+ <body>
8
+ <%= yield %>
9
+ </body>
10
+ </html>
@@ -0,0 +1 @@
1
+ <%= yield %>
data/config/importmap.rb CHANGED
@@ -1 +1,3 @@
1
+ # frozen_string_literal: true
2
+
1
3
  pin_all_from File.expand_path("../app/assets/javascripts/controllers", __dir__), under: "controllers"
@@ -3,7 +3,7 @@ en:
3
3
  bootstrap_feedbacker:
4
4
  remarks:
5
5
  error:
6
- message: An error occured saving your feedback. Please try again.
6
+ message: We couldn't save your feedback due to an error.
7
7
  form:
8
8
  greeting: What's on your mind?
9
9
  new:
data/config/routes.rb CHANGED
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  BootstrapFeedbacker::Engine.routes.draw do
4
- resources :remarks, only: %i(new create)
4
+ resources :remarks, only: %i[new create]
5
5
  end
@@ -2,6 +2,7 @@ class ConvertSourceUrlToText < ActiveRecord::Migration[5.1]
2
2
  def up
3
3
  change_column :bootstrap_feedbacker_remarks, :source_url, :text
4
4
  end
5
+
5
6
  def down
6
7
  change_column :bootstrap_feedbacker_remarks, :source_url, :string
7
8
  end
@@ -9,15 +9,11 @@ module BootstrapFeedbacker
9
9
  end
10
10
 
11
11
  initializer "bootstrap_feedbacker.assets" do
12
- if Rails.application.config.respond_to?(:assets)
13
- Rails.application.config.assets.precompile += %w( controllers/bootstrap_feedbacker/remarks_controller.js )
14
- end
12
+ Rails.application.config.assets.precompile += %w[controllers/bootstrap_feedbacker/remarks_controller.js] if Rails.application.config.respond_to?(:assets)
15
13
  end
16
14
 
17
15
  initializer "bootstrap_feedbacker.importmap", before: "importmap" do |app|
18
- if Rails.application.respond_to?(:importmap)
19
- app.config.importmap.paths << Engine.root.join("config/importmap.rb")
20
- end
16
+ app.config.importmap.paths << Engine.root.join("config/importmap.rb") if Rails.application.respond_to?(:importmap)
21
17
  end
22
18
  end
23
19
  end
@@ -5,11 +5,11 @@ module BootstrapFeedbacker
5
5
  attr_accessor :email_to, :email_prefix, :modal_content_id, :modal_id, :user_name_method, :user_class
6
6
 
7
7
  def initialize
8
- self.email_to = ''
9
- self.email_prefix = '[FEEDBACKER]'
10
- self.modal_content_id = 'bootstrap_feedbacker_remark_modal_content'
11
- self.modal_id = 'bootstrap_feedbacker_remark_modal'
12
- self.user_class = 'User'
8
+ self.email_to = ""
9
+ self.email_prefix = "[FEEDBACKER]"
10
+ self.modal_content_id = "bootstrap_feedbacker_remark_modal_content"
11
+ self.modal_id = "bootstrap_feedbacker_remark_modal"
12
+ self.user_class = "User"
13
13
  self.user_name_method = :name
14
14
  end
15
15
 
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module BootstrapFeedbacker
4
- VERSION = "0.5.0"
4
+ VERSION = "1.0.0"
5
5
  end
metadata CHANGED
@@ -1,30 +1,71 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: bootstrap_feedbacker
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.5.0
4
+ version: 1.0.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Aaron Baldwin
8
8
  - Brightways Learning
9
- autorequire:
10
9
  bindir: bin
11
10
  cert_chain: []
12
- date: 2022-11-07 00:00:00.000000000 Z
11
+ date: 1980-01-02 00:00:00.000000000 Z
13
12
  dependencies:
14
13
  - !ruby/object:Gem::Dependency
15
- name: rails
14
+ name: actionmailer
16
15
  requirement: !ruby/object:Gem::Requirement
17
16
  requirements:
18
17
  - - ">="
19
18
  - !ruby/object:Gem::Version
20
- version: 6.0.0
19
+ version: 7.1.0
21
20
  type: :runtime
22
21
  prerelease: false
23
22
  version_requirements: !ruby/object:Gem::Requirement
24
23
  requirements:
25
24
  - - ">="
26
25
  - !ruby/object:Gem::Version
27
- version: 6.0.0
26
+ version: 7.1.0
27
+ - !ruby/object:Gem::Dependency
28
+ name: activerecord
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - ">="
32
+ - !ruby/object:Gem::Version
33
+ version: 7.1.0
34
+ type: :runtime
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - ">="
39
+ - !ruby/object:Gem::Version
40
+ version: 7.1.0
41
+ - !ruby/object:Gem::Dependency
42
+ name: stimulus-rails
43
+ requirement: !ruby/object:Gem::Requirement
44
+ requirements:
45
+ - - ">="
46
+ - !ruby/object:Gem::Version
47
+ version: '0'
48
+ type: :runtime
49
+ prerelease: false
50
+ version_requirements: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - ">="
53
+ - !ruby/object:Gem::Version
54
+ version: '0'
55
+ - !ruby/object:Gem::Dependency
56
+ name: turbo-rails
57
+ requirement: !ruby/object:Gem::Requirement
58
+ requirements:
59
+ - - ">="
60
+ - !ruby/object:Gem::Version
61
+ version: '0'
62
+ type: :runtime
63
+ prerelease: false
64
+ version_requirements: !ruby/object:Gem::Requirement
65
+ requirements:
66
+ - - ">="
67
+ - !ruby/object:Gem::Version
68
+ version: '0'
28
69
  description: Bootstrap Feedbacker opens a modal window for users to send application
29
70
  feedback over email that includes the referring url.
30
71
  email:
@@ -40,14 +81,19 @@ files:
40
81
  - app/controllers/bootstrap_feedbacker/application_controller.rb
41
82
  - app/controllers/bootstrap_feedbacker/remarks_controller.rb
42
83
  - app/helpers/bootstrap_feedbacker/modals_helper.rb
84
+ - app/mailers/bootstrap_feedbacker/application_mailer.rb
43
85
  - app/mailers/bootstrap_feedbacker/feedback_mailer.rb
86
+ - app/models/bootstrap_feedbacker/application_record.rb
44
87
  - app/models/bootstrap_feedbacker/remark.rb
45
88
  - app/views/bootstrap_feedbacker/feedback_mailer/feedback.html.erb
89
+ - app/views/bootstrap_feedbacker/feedback_mailer/feedback.text.erb
46
90
  - app/views/bootstrap_feedbacker/remarks/_close_button.html.erb
47
- - app/views/bootstrap_feedbacker/remarks/_error.erb
91
+ - app/views/bootstrap_feedbacker/remarks/_error.html.erb
48
92
  - app/views/bootstrap_feedbacker/remarks/_form.html.erb
49
93
  - app/views/bootstrap_feedbacker/remarks/_new.html.erb
50
94
  - app/views/bootstrap_feedbacker/remarks/_thank_you.html.erb
95
+ - app/views/layouts/bootstrap_feedbacker/mailer.html.erb
96
+ - app/views/layouts/bootstrap_feedbacker/mailer.text.erb
51
97
  - config/importmap.rb
52
98
  - config/locales/bootstrap_feedbacker.en.yml
53
99
  - config/routes.rb
@@ -62,8 +108,7 @@ licenses:
62
108
  - MIT
63
109
  metadata:
64
110
  homepage_uri: https://github.com/wwidea/bootstrap_feedbacker
65
- source_code_uri: https://github.com/wwidea/bootstrap_feedbacker
66
- post_install_message:
111
+ rubygems_mfa_required: 'true'
67
112
  rdoc_options: []
68
113
  require_paths:
69
114
  - lib
@@ -71,15 +116,14 @@ required_ruby_version: !ruby/object:Gem::Requirement
71
116
  requirements:
72
117
  - - ">="
73
118
  - !ruby/object:Gem::Version
74
- version: '0'
119
+ version: 3.3.0
75
120
  required_rubygems_version: !ruby/object:Gem::Requirement
76
121
  requirements:
77
122
  - - ">="
78
123
  - !ruby/object:Gem::Version
79
124
  version: '0'
80
125
  requirements: []
81
- rubygems_version: 3.3.10
82
- signing_key:
126
+ rubygems_version: 4.0.23
83
127
  specification_version: 4
84
128
  summary: User feedback form in a Bootstrap modal.
85
129
  test_files: []