diff --git a/.editorconfig b/.editorconfig
new file mode 100644
index 0000000..0ad438d
--- /dev/null
+++ b/.editorconfig
@@ -0,0 +1,14 @@
+; top-most EditorConfig file
+root = true
+
+; Unix-style newlines
+[*]
+end_of_line = LF
+indent_style = space
+indent_size = 2
+charset = utf-8
+trim_trailing_whitespace = true
+insert_final_newline = true
+
+[*.md]
+trim_trailing_whitespace = false
diff --git a/.rubocop.yml b/.rubocop.yml
index 019df56..a96266d 100644
--- a/.rubocop.yml
+++ b/.rubocop.yml
@@ -54,9 +54,6 @@ Naming/PredicateName:
Naming/VariableName:
Enabled: true
-Lint/DeprecatedClassMethods:
- Enabled: true
-
Layout/AlignParameters:
Enabled: true
@@ -117,3 +114,6 @@ Layout/SpaceInsideParens:
Layout/TrailingWhitespace:
Enabled: true
+
+Lint/DeprecatedClassMethods:
+ Enabled: true
diff --git a/CHANGELOG.md b/CHANGELOG.md
index ef8a6d7..103e485 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -7,6 +7,10 @@
**Enhancements:**
- Komponent now reports component stats when you run `bin/rails stats`
+- Komponent now includes a styleguide engine that you can mount to your project
+ to document your components, and 2 new generators:
+ - `rails g komponent:styleguide` to set it up
+ - `rails g komponent:examples` to generate an `examples` file for each existing component
**Bug fixes:**
- Removed redundant `class` attribute in HAML templates
diff --git a/README.md b/README.md
index ed8968f..303fbf4 100644
--- a/README.md
+++ b/README.md
@@ -34,6 +34,7 @@ This gem has been inspired by our Rails development practices at [Ouvrages](http
- [Stimulus integration](#stimulus-integration)
- [Internationalization](#internationalization)
- [Available locales configuration](#available-locales-configuration)
+ - [Styleguide](#styleguide)
- [Configuration](#configuration)
- [Change default root path](#change-default-root-path)
- [Default options for the generators](#default-options-for-the-generators)
@@ -313,6 +314,34 @@ I18n.available_locales = [:en, :fr]
> If you have the `rails-i18n` gem in your `Gemfile`, you should whitelist locales to prevent creating a lot of
> locale files when you generate a new component.
+### Styleguide
+
+Komponent includes a basic styleguide engine that you can use in your project to document your components.
+
+![Komponent styleguide UI](https://user-images.githubusercontent.com/38524/41193700-45909330-6c10-11e8-87b7-59e628529200.png)
+
+To set it up, you can use the generator:
+
+```sh
+rails generate komponent:styleguide
+```
+
+This command will:
+
+* copy the styleguide components (`komponent/container`, `komponent/footer`, `komponent/header` and `komponent/sidebar`) to your components folder, so you can customize them
+* add a new `komponent.js` pack to your packs folder
+* mount the engine in your routes
+
+Then, for each component, you can describe it and render examples for each state in the `_example.html.slim` file from the component folder. The engine will then render it on the component page.
+
+If you have existing components, you can generate all their example files at once with:
+
+```sh
+rails generate komponent:examples
+```
+
+Finally, visit `http://localhost:3000/styleguide` to access your styleguide.
+
### Configuration
#### Change default root path
diff --git a/app/controllers/komponent/styleguide_controller.rb b/app/controllers/komponent/styleguide_controller.rb
new file mode 100644
index 0000000..5c8e8cd
--- /dev/null
+++ b/app/controllers/komponent/styleguide_controller.rb
@@ -0,0 +1,20 @@
+# frozen_string_literal: true
+
+module Komponent
+ class StyleguideController < ::ApplicationController
+ layout 'komponent'
+ rescue_from ActionView::MissingTemplate, with: :missing_template
+
+ def index; end
+
+ def show
+ @component = Komponent::Component.find(params[:id])
+ end
+
+ private
+
+ def missing_template
+ render 'komponent/styleguide/missing_template', status: :not_found
+ end
+ end
+end
diff --git a/app/views/komponent/styleguide/index.html.erb b/app/views/komponent/styleguide/index.html.erb
new file mode 100644
index 0000000..9c51264
--- /dev/null
+++ b/app/views/komponent/styleguide/index.html.erb
@@ -0,0 +1,9 @@
+
Styleguide
+
+<% if components.any? %>
+ Select one of the components from the side to view its examples and documentation.
+<% else %>
+ <Hint: You haven't created any component yet
+
You can generate your first component by running:
+ rails generate component component-name
+<% end %>
diff --git a/app/views/komponent/styleguide/missing_template.html.erb b/app/views/komponent/styleguide/missing_template.html.erb
new file mode 100644
index 0000000..43e9315
--- /dev/null
+++ b/app/views/komponent/styleguide/missing_template.html.erb
@@ -0,0 +1,3 @@
+Examples missing
+
+Please create <%= @component.path %>/_examples.html.<%= Rails.application.config.app_generators.rails[:template_engine] || :erb %>
file.
diff --git a/app/views/komponent/styleguide/show.html.erb b/app/views/komponent/styleguide/show.html.erb
new file mode 100644
index 0000000..716aad5
--- /dev/null
+++ b/app/views/komponent/styleguide/show.html.erb
@@ -0,0 +1 @@
+<%= render partial: @component.examples_view %>
diff --git a/app/views/layouts/komponent.html.erb b/app/views/layouts/komponent.html.erb
new file mode 100644
index 0000000..972c1c8
--- /dev/null
+++ b/app/views/layouts/komponent.html.erb
@@ -0,0 +1,17 @@
+
+
+
+ Styleguide
+ <%= javascript_pack_tag 'komponent' %>
+ <%= stylesheet_pack_tag 'komponent', media: 'all' %>
+ <%= csrf_meta_tags %>
+
+
+ <%= c 'komponent/header' %>
+ <%= c 'komponent/sidebar' %>
+ <%= c 'komponent/container' do %>
+ <%= yield %>
+ <% end %>
+ <%= c 'komponent/footer' %>
+
+
diff --git a/config/routes.rb b/config/routes.rb
new file mode 100644
index 0000000..7293246
--- /dev/null
+++ b/config/routes.rb
@@ -0,0 +1,5 @@
+# frozen_string_literal: true
+
+Komponent::Engine.routes.draw do
+ resources :styleguide, only: %i[index show]
+end
diff --git a/features/component_generator.feature b/features/component_generator.feature
index cc59f3f..47c0013 100644
--- a/features/component_generator.feature
+++ b/features/component_generator.feature
@@ -11,6 +11,7 @@ Feature: Component generator
| awesome_button/awesome_button.css |
| awesome_button/awesome_button.js |
| awesome_button/awesome_button_component.rb |
+ | awesome_button/_examples.html.erb |
And the file named "index.js" should contain:
"""
import "components/awesome_button/awesome_button";
@@ -30,6 +31,7 @@ Feature: Component generator
| admin/sub_admin/awesome_button/admin_sub_admin_awesome_button.css |
| admin/sub_admin/awesome_button/admin_sub_admin_awesome_button.js |
| admin/sub_admin/awesome_button/admin_sub_admin_awesome_button_component.rb |
+ | admin/sub_admin/awesome_button/_examples.html.erb |
And the file named "index.js" should contain:
"""
import "components/admin";
@@ -117,6 +119,7 @@ Feature: Component generator
When I run `rails generate component AwesomeButton`
And I cd to "frontend/components/awesome_button"
Then a file named "_awesome_button.html.erb" should exist
+ And a file named "_examples.html.erb" should exist
Scenario: Generate component with custom template engine defined to `haml`
Given a file named "config/initializers/custom_configuration.rb" with:
@@ -126,6 +129,7 @@ Feature: Component generator
When I run `rails generate component AwesomeButton`
And I cd to "frontend/components/awesome_button"
Then a file named "_awesome_button.html.haml" should exist
+ And a file named "_examples.html.haml" should exist
Scenario: Generate component with custom template engine defined to `slim`
Given a file named "config/initializers/custom_configuration.rb" with:
@@ -135,6 +139,7 @@ Feature: Component generator
When I run `rails generate component AwesomeButton`
And I cd to "frontend/components/awesome_button"
Then a file named "_awesome_button.html.slim" should exist
+ And a file named "_examples.html.slim" should exist
Scenario: Generate component with custom stylesheet engine defined to `scss`
Given a file named "config/initializers/custom_configuration.rb" with:
diff --git a/lib/generators/component/component_generator.rb b/lib/generators/component/component_generator.rb
index b2aaaca..c28db85 100644
--- a/lib/generators/component/component_generator.rb
+++ b/lib/generators/component/component_generator.rb
@@ -34,6 +34,10 @@ def create_locale_files
end
end
+ def create_examples_view_file
+ template "examples.html.#{template_engine}.erb", component_path + "_examples.html.#{template_engine}"
+ end
+
def import_to_packs
root_path = default_path
base_path = root_path + "components"
diff --git a/lib/generators/component/templates/examples.html.erb.erb b/lib/generators/component/templates/examples.html.erb.erb
new file mode 100644
index 0000000..54c8aa3
--- /dev/null
+++ b/lib/generators/component/templates/examples.html.erb.erb
@@ -0,0 +1,3 @@
+<%= @component_name %>
+
+<%%= cdoc "<%= component_name %>" %>
diff --git a/lib/generators/component/templates/examples.html.haml.erb b/lib/generators/component/templates/examples.html.haml.erb
new file mode 100644
index 0000000..851eec6
--- /dev/null
+++ b/lib/generators/component/templates/examples.html.haml.erb
@@ -0,0 +1,3 @@
+%h1 <%= @component_name %>
+
+= cdoc "<%= component_name %>"
diff --git a/lib/generators/component/templates/examples.html.slim.erb b/lib/generators/component/templates/examples.html.slim.erb
new file mode 100644
index 0000000..050dc7b
--- /dev/null
+++ b/lib/generators/component/templates/examples.html.slim.erb
@@ -0,0 +1,3 @@
+h1 <%= @component_name %>
+
+= cdoc "<%= component_name %>"
diff --git a/lib/generators/komponent/examples_generator.rb b/lib/generators/komponent/examples_generator.rb
new file mode 100644
index 0000000..bef5c02
--- /dev/null
+++ b/lib/generators/komponent/examples_generator.rb
@@ -0,0 +1,40 @@
+# frozen_string_literal: true
+
+require 'komponent/component'
+require File.expand_path('../utils', __FILE__)
+
+module Komponent
+ module Generators
+ class ExamplesGenerator < Rails::Generators::Base
+ include Utils
+
+ source_root File.expand_path('../../component/templates', __FILE__)
+
+ def create_examples_files
+ Komponent::Component.all.each do |name, component|
+ create_examples_view_file(name)
+ end
+ end
+
+ protected
+
+ def split_name(name)
+ name.split(/[:,::,\/]/).reject(&:blank?).map(&:underscore)
+ end
+
+ def component_path(component_name)
+ path_parts = [default_path, 'components', *split_name(component_name)]
+
+ Pathname.new(path_parts.join('/'))
+ end
+
+ private
+
+ def create_examples_view_file(component_name)
+ @component_name = split_name(component_name).last.underscore
+
+ template "examples.html.#{template_engine}.erb", component_path(component_name) + "_examples.html.#{template_engine}"
+ end
+ end
+ end
+end
diff --git a/lib/generators/komponent/install_generator.rb b/lib/generators/komponent/install_generator.rb
index 4e21b5c..9dd5646 100644
--- a/lib/generators/komponent/install_generator.rb
+++ b/lib/generators/komponent/install_generator.rb
@@ -1,8 +1,11 @@
# frozen_string_literal: true
+require File.expand_path('../utils', __FILE__)
module Komponent
module Generators
class InstallGenerator < Rails::Generators::Base
+ include Utils
+
class_option :stimulus, type: :boolean, default: false
def check_webpacker_dependency
@@ -80,14 +83,6 @@ def application_pack_path
komponent_root_directory.join("packs", "application.js")
end
- def komponent_root_directory
- default_path
- end
-
- def components_directory
- Rails.root.join(komponent_root_directory, "components")
- end
-
def webpacker_configuration_file
Rails.root.join("config", "webpacker.yml")
end
@@ -96,10 +91,6 @@ def webpacker_default_structure
Rails.root.join("app", "javascript")
end
- def komponent_already_installed?
- File.directory?(relative_path_from_rails)
- end
-
def dependencies_not_met_error_message
"Seems you don't have webpacker installed in your project. Please install webpacker, and follow instructions at https://github.com/rails/webpacker"
end
@@ -108,31 +99,6 @@ def stimulus?
return options[:stimulus] if options[:stimulus]
komponent_configuration[:stimulus]
end
-
- def default_path
- rails_configuration.komponent.root
- end
-
- def relative_path_from_rails
- default_path.relative_path_from(Rails.root)
- end
-
- private
-
- def komponent_configuration
- {
- stimulus: nil,
- locale: nil,
- }.merge(app_generators.komponent)
- end
-
- def rails_configuration
- Rails.application.config
- end
-
- def app_generators
- rails_configuration.app_generators
- end
end
end
end
diff --git a/lib/generators/komponent/styleguide_generator.rb b/lib/generators/komponent/styleguide_generator.rb
new file mode 100644
index 0000000..4e050c0
--- /dev/null
+++ b/lib/generators/komponent/styleguide_generator.rb
@@ -0,0 +1,41 @@
+# frozen_string_literal: true
+
+require File.expand_path('../utils', __FILE__)
+
+module Komponent
+ module Generators
+ class StyleguideGenerator < Rails::Generators::Base
+ include Utils
+
+ source_root File.expand_path('../templates/styleguide', __FILE__)
+
+ def check_komponent_dependency
+ unless komponent_already_installed?
+ raise Thor::Error, dependencies_not_met_error_message
+ end
+ end
+
+ def copy_styleguide_components
+ directory 'components', components_directory.join('komponent')
+ end
+
+ def create_komponent_pack
+ template 'packs/komponent.js', komponent_pack_path
+ end
+
+ def append_to_application_routes
+ route 'mount Komponent::Engine => \'/\' if Rails.env.development?'
+ end
+
+ protected
+
+ def komponent_pack_path
+ komponent_root_directory.join('packs', 'komponent.js')
+ end
+
+ def dependencies_not_met_error_message
+ 'Seems you don\'t have komponent installed in your project. Please install komponent, and follow instructions at https://github.com/komposable/komponent'
+ end
+ end
+ end
+end
diff --git a/lib/generators/komponent/templates/styleguide/components/container/_komponent_container.html.erb b/lib/generators/komponent/templates/styleguide/components/container/_komponent_container.html.erb
new file mode 100644
index 0000000..31dc85d
--- /dev/null
+++ b/lib/generators/komponent/templates/styleguide/components/container/_komponent_container.html.erb
@@ -0,0 +1,3 @@
+
+ <%= yield %>
+
diff --git a/lib/generators/komponent/templates/styleguide/components/container/komponent_container.css b/lib/generators/komponent/templates/styleguide/components/container/komponent_container.css
new file mode 100644
index 0000000..a41d825
--- /dev/null
+++ b/lib/generators/komponent/templates/styleguide/components/container/komponent_container.css
@@ -0,0 +1,17 @@
+/* stylelint-disable value-list-comma-newline-after */
+
+.komponent-container {
+ font-size: 16px;
+ font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto,
+ Oxygen-Sans, Ubuntu, Cantarell, "Helvetica Neue", sans-serif;
+ margin: 40px 60px 0 300px;
+
+ .komponent-code {
+ background-color: #333;
+ color: #fff;
+ margin: 10px 0;
+ padding: 10px;
+ }
+}
+
+/* stylelint-enable */
diff --git a/lib/generators/komponent/templates/styleguide/components/container/komponent_container.js b/lib/generators/komponent/templates/styleguide/components/container/komponent_container.js
new file mode 100644
index 0000000..70b1a82
--- /dev/null
+++ b/lib/generators/komponent/templates/styleguide/components/container/komponent_container.js
@@ -0,0 +1 @@
+import "./komponent_container.css";
diff --git a/lib/generators/komponent/templates/styleguide/components/container/komponent_container_component.rb b/lib/generators/komponent/templates/styleguide/components/container/komponent_container_component.rb
new file mode 100644
index 0000000..3a2362d
--- /dev/null
+++ b/lib/generators/komponent/templates/styleguide/components/container/komponent_container_component.rb
@@ -0,0 +1,5 @@
+# frozen_string_literal: true
+
+module KomponentContainerComponent
+ extend ComponentHelper
+end
diff --git a/lib/generators/komponent/templates/styleguide/components/footer/_komponent_footer.html.erb b/lib/generators/komponent/templates/styleguide/components/footer/_komponent_footer.html.erb
new file mode 100644
index 0000000..c3a1fae
--- /dev/null
+++ b/lib/generators/komponent/templates/styleguide/components/footer/_komponent_footer.html.erb
@@ -0,0 +1,3 @@
+
diff --git a/lib/generators/komponent/templates/styleguide/components/footer/komponent_footer.css b/lib/generators/komponent/templates/styleguide/components/footer/komponent_footer.css
new file mode 100644
index 0000000..95a03e4
--- /dev/null
+++ b/lib/generators/komponent/templates/styleguide/components/footer/komponent_footer.css
@@ -0,0 +1,27 @@
+/* stylelint-disable value-list-comma-newline-after */
+
+.komponent-footer {
+ bottom: 30px;
+ font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto,
+ Oxygen-Sans, Ubuntu, Cantarell, "Helvetica Neue", sans-serif;
+ font-size: 14px;
+ position: fixed;
+ right: 30px;
+ text-align: center;
+
+ &,
+ a {
+ color: #999;
+ }
+
+ a {
+ text-decoration: none;
+
+ &:hover,
+ &:focus {
+ color: #0038ea;
+ }
+ }
+}
+
+/* stylelint-enable */
diff --git a/lib/generators/komponent/templates/styleguide/components/footer/komponent_footer.js b/lib/generators/komponent/templates/styleguide/components/footer/komponent_footer.js
new file mode 100644
index 0000000..c1d61e6
--- /dev/null
+++ b/lib/generators/komponent/templates/styleguide/components/footer/komponent_footer.js
@@ -0,0 +1 @@
+import "./komponent_footer.css";
diff --git a/lib/generators/komponent/templates/styleguide/components/footer/komponent_footer_component.rb b/lib/generators/komponent/templates/styleguide/components/footer/komponent_footer_component.rb
new file mode 100644
index 0000000..9f64ef8
--- /dev/null
+++ b/lib/generators/komponent/templates/styleguide/components/footer/komponent_footer_component.rb
@@ -0,0 +1,5 @@
+# frozen_string_literal: true
+
+module KomponentFooterComponent
+ extend ComponentHelper
+end
diff --git a/lib/generators/komponent/templates/styleguide/components/header/_komponent_header.html.erb b/lib/generators/komponent/templates/styleguide/components/header/_komponent_header.html.erb
new file mode 100644
index 0000000..e4feb12
--- /dev/null
+++ b/lib/generators/komponent/templates/styleguide/components/header/_komponent_header.html.erb
@@ -0,0 +1,3 @@
+
diff --git a/lib/generators/komponent/templates/styleguide/components/header/komponent_header.css b/lib/generators/komponent/templates/styleguide/components/header/komponent_header.css
new file mode 100644
index 0000000..ac9f194
--- /dev/null
+++ b/lib/generators/komponent/templates/styleguide/components/header/komponent_header.css
@@ -0,0 +1,15 @@
+/* stylelint-disable value-list-comma-newline-after */
+
+.komponent-header {
+ align-items: center;
+ background-color: #0038ea;
+ color: white;
+ display: flex;
+ font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto,
+ Oxygen-Sans, Ubuntu, Cantarell, "Helvetica Neue", sans-serif;
+ font-size: 16px;
+ height: 60px;
+ padding: 0 20px;
+}
+
+/* stylelint-enable */
diff --git a/lib/generators/komponent/templates/styleguide/components/header/komponent_header.js b/lib/generators/komponent/templates/styleguide/components/header/komponent_header.js
new file mode 100644
index 0000000..b013393
--- /dev/null
+++ b/lib/generators/komponent/templates/styleguide/components/header/komponent_header.js
@@ -0,0 +1 @@
+import "./komponent_header.css";
diff --git a/lib/generators/komponent/templates/styleguide/components/header/komponent_header_component.rb b/lib/generators/komponent/templates/styleguide/components/header/komponent_header_component.rb
new file mode 100644
index 0000000..7d3d6b6
--- /dev/null
+++ b/lib/generators/komponent/templates/styleguide/components/header/komponent_header_component.rb
@@ -0,0 +1,5 @@
+# frozen_string_literal: true
+
+module KomponentHeaderComponent
+ extend ComponentHelper
+end
diff --git a/lib/generators/komponent/templates/styleguide/components/index.js b/lib/generators/komponent/templates/styleguide/components/index.js
new file mode 100644
index 0000000..878e312
--- /dev/null
+++ b/lib/generators/komponent/templates/styleguide/components/index.js
@@ -0,0 +1,4 @@
+import "components/komponent/container/komponent_container";
+import "components/komponent/footer/komponent_footer";
+import "components/komponent/header/komponent_header";
+import "components/komponent/sidebar/komponent_sidebar";
diff --git a/lib/generators/komponent/templates/styleguide/components/sidebar/_komponent_sidebar.html.erb b/lib/generators/komponent/templates/styleguide/components/sidebar/_komponent_sidebar.html.erb
new file mode 100644
index 0000000..195ade3
--- /dev/null
+++ b/lib/generators/komponent/templates/styleguide/components/sidebar/_komponent_sidebar.html.erb
@@ -0,0 +1,10 @@
+
diff --git a/lib/generators/komponent/templates/styleguide/components/sidebar/komponent_sidebar.css b/lib/generators/komponent/templates/styleguide/components/sidebar/komponent_sidebar.css
new file mode 100644
index 0000000..3ac9e59
--- /dev/null
+++ b/lib/generators/komponent/templates/styleguide/components/sidebar/komponent_sidebar.css
@@ -0,0 +1,43 @@
+/* stylelint-disable value-list-comma-newline-after */
+
+.komponent-sidebar {
+ background-color: #dbe1f3;
+ bottom: 0;
+ font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto,
+ Oxygen-Sans, Ubuntu, Cantarell, "Helvetica Neue", sans-serif;
+ left: 0;
+ overflow: auto;
+ padding: 20px;
+ position: absolute;
+ top: 60px;
+ width: 240px;
+
+ &-title {
+ color: #0038ea;
+ font-size: 14px;
+ font-weight: bold;
+ letter-spacing: 1px;
+ margin: 0 0 20px;
+ text-transform: uppercase;
+ }
+
+ &-items {
+ font-size: 14px;
+ letter-spacing: 0;
+ line-height: 1.25;
+ list-style: none;
+ margin: 0 0 30px;
+ padding: 0;
+ }
+
+ &-item {
+ margin: 0 0 5px;
+ }
+
+ a {
+ color: #333;
+ text-decoration: none;
+ }
+}
+
+/* stylelint-enable */
diff --git a/lib/generators/komponent/templates/styleguide/components/sidebar/komponent_sidebar.js b/lib/generators/komponent/templates/styleguide/components/sidebar/komponent_sidebar.js
new file mode 100644
index 0000000..2b3af41
--- /dev/null
+++ b/lib/generators/komponent/templates/styleguide/components/sidebar/komponent_sidebar.js
@@ -0,0 +1 @@
+import "./komponent_sidebar.css";
diff --git a/lib/generators/komponent/templates/styleguide/components/sidebar/komponent_sidebar_component.rb b/lib/generators/komponent/templates/styleguide/components/sidebar/komponent_sidebar_component.rb
new file mode 100644
index 0000000..3395063
--- /dev/null
+++ b/lib/generators/komponent/templates/styleguide/components/sidebar/komponent_sidebar_component.rb
@@ -0,0 +1,5 @@
+# frozen_string_literal: true
+
+module KomponentSidebarComponent
+ extend ComponentHelper
+end
diff --git a/lib/generators/komponent/templates/styleguide/packs/komponent.js b/lib/generators/komponent/templates/styleguide/packs/komponent.js
new file mode 100644
index 0000000..c31f602
--- /dev/null
+++ b/lib/generators/komponent/templates/styleguide/packs/komponent.js
@@ -0,0 +1,2 @@
+import 'components';
+import 'components/komponent';
diff --git a/lib/generators/komponent/utils.rb b/lib/generators/komponent/utils.rb
new file mode 100644
index 0000000..f70c267
--- /dev/null
+++ b/lib/generators/komponent/utils.rb
@@ -0,0 +1,49 @@
+# frozen_string_literal: true
+
+module Komponent
+ module Generators
+ module Utils
+ protected
+
+ def rails_configuration
+ Rails.application.config
+ end
+
+ def app_generators
+ rails_configuration.app_generators
+ end
+
+ def template_engine
+ app_generators.rails[:template_engine] || :erb
+ end
+
+ def default_path
+ rails_configuration.komponent.root
+ end
+
+ def relative_path_from_rails
+ default_path.relative_path_from(Rails.root)
+ end
+
+ def komponent_root_directory
+ default_path
+ end
+
+ def komponent_configuration
+ {
+ stimulus: nil,
+ locale: nil,
+ examples: nil,
+ }.merge(app_generators.komponent)
+ end
+
+ def components_directory
+ Rails.root.join(komponent_root_directory, 'components')
+ end
+
+ def komponent_already_installed?
+ File.directory?(relative_path_from_rails)
+ end
+ end
+ end
+end
diff --git a/lib/komponent.rb b/lib/komponent.rb
index 3d737e6..965007c 100644
--- a/lib/komponent.rb
+++ b/lib/komponent.rb
@@ -1,7 +1,7 @@
# frozen_string_literal: true
-require "komponent/version"
+require 'komponent/version'
module Komponent
- require 'komponent/railtie' if defined?(Rails)
+ require 'komponent/engine' if defined?(Rails)
end
diff --git a/lib/komponent/component.rb b/lib/komponent/component.rb
new file mode 100644
index 0000000..8cd991e
--- /dev/null
+++ b/lib/komponent/component.rb
@@ -0,0 +1,55 @@
+# frozen_string_literal: true
+
+module Komponent
+ class Component
+ class << self
+ def all
+ component_dirs = components_root.join('*/**/')
+
+ components = {}
+
+ # TODO: list only components directories
+ # use ComponentPathResolver?
+ Dir.glob(component_dirs).sort.each do |component_dir|
+ component_path = Pathname.new(component_dir).relative_path_from(components_root).to_s
+
+ next unless File.exist?(components_root.join(component_path)
+ .join("#{File.basename(component_path)}_component.rb"))
+
+ components[component_path] = Component.new(component_path)
+ end
+
+ components
+ end
+
+ def find(id)
+ components = all
+
+ raise Exception, "Component with ID=#{id} not found" unless components.keys.include? id
+ components.fetch(id)
+ end
+
+ def components_root
+ @components_root ||= Rails.application.config.komponent.root.join('components')
+ end
+ end
+
+ attr_reader :id
+
+ def initialize(id)
+ @id = id
+ end
+
+ def title
+ id.titleize
+ end
+
+ def path
+ Komponent::ComponentPathResolver.new.resolve(@id)
+ end
+
+ def examples_view
+ "components/#{id}/examples"
+ end
+ end
+end
diff --git a/lib/komponent/component_path_resolver.rb b/lib/komponent/component_path_resolver.rb
index 2ae69d9..4a1010a 100644
--- a/lib/komponent/component_path_resolver.rb
+++ b/lib/komponent/component_path_resolver.rb
@@ -17,6 +17,12 @@ def resolve(component_name)
root_path.join(*component_name)
end
+ def component_paths
+ komponent_configuration.component_paths.map do |path|
+ Pathname.new(path)
+ end
+ end
+
protected
def path_has_component?(path, component_name)
@@ -27,12 +33,6 @@ def path_has_component?(path, component_name)
File.exist?(file_name)
end
- def component_paths
- komponent_configuration.component_paths.map do |path|
- Pathname.new(path)
- end
- end
-
def komponent_configuration
app_configuration.komponent
end
diff --git a/lib/komponent/railtie.rb b/lib/komponent/engine.rb
similarity index 65%
rename from lib/komponent/railtie.rb
rename to lib/komponent/engine.rb
index 2e77e43..a965e25 100644
--- a/lib/komponent/railtie.rb
+++ b/lib/komponent/engine.rb
@@ -4,10 +4,13 @@
require 'komponent/component_helper'
require 'komponent/component_path_resolver'
require 'komponent/component_renderer'
+require 'komponent/component'
require 'komponent/translation'
module Komponent
- class Railtie < Rails::Railtie
+ class Engine < Rails::Engine
+ isolate_namespace Komponent
+
rake_tasks do
load 'komponent/rails/tasks/komponent.rake'
end
@@ -26,6 +29,9 @@ class Railtie < Rails::Railtie
app.config.komponent.component_paths.prepend(
app.config.komponent.root.join("components")
)
+ app.config.komponent.component_paths.append(
+ Komponent::Engine.root.join('frontend/components')
+ )
ActiveSupport.on_load :action_view do
require 'komponent/komponent_helper'
@@ -38,5 +44,15 @@ class Railtie < Rails::Railtie
)
end
end
+
+ initializer "my_engine.action_dispatch" do |app|
+ ActiveSupport.on_load :action_controller do
+ ActionController::Base.prepend_view_path Komponent::Engine.root.join("frontend")
+ end
+ end
+
+ initializer 'komponent.autoload', before: :set_autoload_paths do |app|
+ app.config.autoload_paths << Komponent::Engine.root.join('frontend')
+ end
end
end
diff --git a/lib/komponent/komponent_helper.rb b/lib/komponent/komponent_helper.rb
index 1b9d7df..bc8a564 100644
--- a/lib/komponent/komponent_helper.rb
+++ b/lib/komponent/komponent_helper.rb
@@ -1,5 +1,7 @@
# frozen_string_literal: true
+require 'komponent/component'
+
module KomponentHelper
def component(component_name, locals = {}, options = {}, &block)
captured_block = proc { |args| capture(args, &block) } if block_given?
@@ -14,4 +16,30 @@ def component(component_name, locals = {}, options = {}, &block)
)
end
alias :c :component
+
+ def components
+ Komponent::Component.all
+ end
+
+ def component_with_doc(component_name, locals = {}, options = {}, &block)
+ captured_output = component(component_name, locals, options, &block)
+
+ captured_doc = capture do
+ content_tag :pre, class: "komponent-code" do
+ content_tag :code do
+ "= component \"#{component_name}\"" + (locals.present? ? ", #{pretty_locals(locals)}" : "")
+ end
+ end
+ end
+
+ captured_output + captured_doc
+ end
+ alias :cdoc :component_with_doc
+
+ private
+
+ def pretty_locals(locals)
+ return nil if locals.blank?
+ JSON.pretty_generate(locals).gsub(/^(\s+)"(\w+)":/, "\\1\\2:")
+ end
end
diff --git a/test/komponent/component_test.rb b/test/komponent/component_test.rb
new file mode 100644
index 0000000..93efc4a
--- /dev/null
+++ b/test/komponent/component_test.rb
@@ -0,0 +1,46 @@
+# frozen_string_literal: true
+
+require 'test_helper'
+
+class ComponentTest < ActionView::TestCase
+ def test_all_returns_components
+ all = Komponent::Component.all
+
+ assert all.is_a?(Hash)
+ assert_equal all.count, 9
+ assert all["foo"].is_a?(Komponent::Component)
+ end
+
+ def test_find_raises_exception_when_missing
+ assert_raise Exception do
+ Komponent::Component.find("missing")
+ end
+ end
+
+ def test_find_returns_component
+ assert \
+ Komponent::Component.find("foo").is_a?(Komponent::Component)
+ assert_equal \
+ "foo",
+ Komponent::Component.find("foo").id
+ end
+
+ def test_returns_title
+ assert_equal \
+ "Foo Bar",
+ Komponent::Component.new("foo_bar").title
+ end
+
+ def test_returns_examples_view
+ assert_equal \
+ "components/foo/examples",
+ Komponent::Component.new("foo").examples_view
+ end
+
+ def test_returns_path
+ path = Komponent::Component.new("foo").path
+
+ assert path.is_a?(Pathname)
+ assert path.to_s.include?("frontend/components/foo")
+ end
+end
diff --git a/test/komponent/komponent_helper_test.rb b/test/komponent/komponent_helper_test.rb
index b67dc3f..f9ac73e 100644
--- a/test/komponent/komponent_helper_test.rb
+++ b/test/komponent/komponent_helper_test.rb
@@ -77,4 +77,31 @@ def test_helper_supports_content_for_across_components
%(Greetings from Ping
),
component('pong').chomp
end
+
+ def test_helper_lists_components
+ assert_equal(
+ [
+ 'all',
+ 'bar',
+ 'foo',
+ 'foo_bar',
+ 'hello',
+ 'ping',
+ 'pong',
+ 'required',
+ 'world',
+ ],
+ components.keys
+ )
+ end
+
+ def test_helper_renders_with_doc
+ assert_equal \
+ %(🌎 😎
+= component "all", {
+ world: "🌎",
+ sunglasses: "😎"
+}
),
+ component_with_doc('all', world: "🌎", sunglasses: "😎").chomp
+ end
end