Git Product home page Git Product logo

komponent's Introduction

Komposable

Maintainability

Komposable allows you to quickly build a modular back-office for your Rails app. It offers standard features and allows you to implement you own specific needs easily.

It's based on the following existing gems:

This gem has been inspired by fae. It is developed by Ouvrages and Etamin Studio.

Getting started

# Gemfile

gem "komposable"

# For now, we have to add it manually
# TODO: include it in the gemspec dependencies
gem 'meta-tags', github: 'etaminstudio/meta-tags', branch: 'rename-title-helper'

Copy and run the migrations from the engine:

bin/rails komposable:install:migrations
bin/rails db:migrate

Add at the beginning of the routes:

# config/routes.rb

namespace :admin do
  root "pages#index"
  resources :pages
end

mount Komposable::Engine => "/admin"

Add at the end of the file:

# config/initializers/assets.rb

Rails.application.config.assets.precompile += %w( admin.js admin.css )

Add the engine path to resolved paths for webpacker and postcss:

# config/webpacker.yml

  resolved_paths:
    - frontend
    - vendor/komposable/frontend
# .postcssrc.yml

  postcss-import: {
    path: [
      'frontend',
      'vendor/komposable/frontend'
    ]
  }

Create a new javascript pack:

// frontend/packs/admin.js

import "komposable/frontend/packs/admin";

Navigation

To define your own navigation in the admin, you can override the admin/header component.

$ mkdir frontend/components/admin
$ cp -r vendor/komposable/frontend/components/admin/header frontend/components/admin

komponent's People

Contributors

alecrust avatar andrzejsliwa avatar apauly avatar descovi avatar florentferry avatar gkemmey avatar mittchobaroco avatar nicolas-brousse avatar olimart avatar otimo avatar robink avatar sigmike avatar spone avatar stevschmid avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

komponent's Issues

Can't access content_for defined in component

I'm trying to set a content_for in a component, and then access it in my layout:

/ frontend/components/admin/form/items/_items.html.slim

.admin-form-items
  / [...]
  - content_for :after_wrapper do
    .test Hello World
/ app/views/layouts/admin.html.slim

doctype html
html
  head
    / [...]

  body.is-admin
    = c "admin/header"
    = yield
    = yield :after_wrapper

But nothing gets yielded in the layout. Any idea?

Unable to use component helper

As per the README, component helper module should(?) be wired up to the component 'view' right out of the box:

Helpers

If your partial becomes too complex and you want to extract logic from it, you may want to define custom helpers in the ButtonComponent module:

# frontend/components/button/button_component.rb

module ButtonComponent
  property :href, required: true
  property :text, default: "My button"

  def external_link?
    @href.starts_with? "http"
  end
end

However, none of the methods defined on my helpers are accessible from the component 'view':

#frontend/components/direct_attachment_field/direct_attachment_field_component.rb
module DirectAttachmentFieldComponent
  extend ComponentHelper

  def something
    raw('something')
  end
end
#frontend/components/direct_attachment_field/_direct_attachment_field-html.erb
<div data-controller="direct-attachment-field" data-target="report-form.fileFieldGroup" data-index="0" class="direct-attachment-field input-group">
  <%= @attacheable.file_field :document, direct_upload: true,
                              class: 'form-control-file',
                              data: { action: 'report-form#loadFile', target: 'report-form.fileField'} %>
  <label class="file-field-label">Seleccionar archivo...</label>
  <%= something %>
</div>

Always returns:
undefined local variable or method 'something' for #<#<Class:0x007fab224dbca8>:0x007fab21b09b58>

Is there any extra configuration I might be missing or it is not behaving as expected?

Thanks

Rubocop not target the right ruby version

Since I changed the gemspec to require ruby version 2.2 and the travis matrix targeting 2.2 until ruby-head, the rubocop configuration no longer targeting the right version. We need to change RubyTargetVersion option of rubocop from 2.4 to 2.2.

Rendering partial into component

Problem

We need the ability to render partial inside a component. If that partial is inside the component, we need a way to prevent to give full path.

Example:

  components/
    button/
       _button.html.slim
       _icon.html.slim
/ frontend/components/button/_button.html.slim

= render("components/button/icon")

Solution 1


Add components folder and all subfolders to lookup. But the drawback is the addition of a lot of directories to lookup.

ActionController::Base.prepend_vew_path Dir["#{app.config.root}/frontend/**/*"]
/ frontend/components/button/_button.html.slim

= render("button/icon")

Solution 2


Add a render_partial helper to render partial inside component. We inject current component path to lookup in order to render the component, and restore default behavior after rendering.

def render_partial(partial_name, locals = {}, &block)
  benchmark("Rendered partial #{partial_name}") do
    context = controller.view_context
    view_paths = context.lookup_context.view_paths.dup
    components_path = Rails.root.join "frontend/components"

    capture_block = proc { capture(&block) } if block

    current_dir = Pathname.new(@virtual_path).dirname

    context.lookup_context.prefixes.prepend current_dir
    context.lookup_context.view_paths.unshift components_path

    partial_rendered = capture do 
      context.render partial_name, locals, &capture_block
    end

    context.lookup_context.prefixes.delete current_dir
    context.lookup_context.view_paths = view_paths

    partial_rendered
  end
end
/ frontend/components/button/_button.html.slim

= render_partial("icon")

Solution 3


Allow to have nested components inside component.

  components/
    button/
      _button
      ...
      title/
        _title
        ...

component helper using relative paths

Hi, first of all congratulations for the job done, I really enjoy working with the gem. I was wondering if are there any plans to incorporate the possibility of rendering components using a relative path.
I know that's not railish enough, but I think its kind of a elementary feature for frontend frameworks, most of all, due to rehusability.

A case example would be a component which needs two children components. There should not be necessary to write the full path of the component, since both child components have no sense alone.

A solution, in order to avoid simulating the javascript imports (like './component_name'), would be to start the lookup of a component from the current directory. So in this example:

one_component/
  _one_component.html.erb
  other_component/
    _other_component.html.erb
other_component/
  _other_component.html.erb

Calling

component('other_component')

from _one_component.html.erb would consider first the partial under one_component/other_component/_other_component.html.erb

Could you please let me know if that makes sense for you, or if you think that it is not a necessary feature? In any case, I could also work on it.

Thanks

application.scss silently breaks javascript

versions & Setup

pachages.json

"dependencies": {
    "@rails/webpacker": "^3.2.2",
    "rails-ujs": "^5.1.5",
    "stimulus": "^1.0.1"
  },
  "devDependencies": {
    "webpack-dev-server": "^2.11.1"
  }

Gemfile.lock

komponent (1.1.3)
      webpacker (>= 3.0.0)

application.html.erb

<%= stylesheet_pack_tag    'application' %>

frontent/packs/application.js

import "rails-ujs";
import 'components';
console.log("Hello world");

Steps to Reproduce:

A)

  1. Create an empty file: frontent/packs/application.scss
  2. Launch servers

Expected: "Hello World" to be displayed in console
Actual: No "Hello World" message even tho compilation finishes with no error.

B)

  1. Rename file frontent/packs/application.scss to frontent/packs/application.css
  2. Launch servers

Expected: "Hello World" to be displayed in console
Actual: Success!

Note

webpack-dev-server has to be restarted between changes to see the issue.

encapsulation problem

Hi

I see small problem with encapsulation, original idea was based on passing only locals,
but you are converting them on the fly to instance variables so there is couple issues with it:

  • I have global scope, which is breaking encapsulation (I can call @var from child component), which is breaking the original concept of component isolation
  • I can overwrite instance variables passed from controller, by passing them with same name, on any level to any component

If i got it correctly you did it to have ability of defining helpers methods with same name in component module.
I would go back to using locals, and wrap them not with modules but with instances of class (similar to decorators/presenters). You can keep properties helper on class level, which will lets you generate default constructor with kwargs (with base object + properties, then you can make properties to be instance variables in such auto-constructor).

What do you think about it?

Make install generator idempotent

I should be able to run the rails generate komponent:install command several times, for instance if I want to run it again with other options.

My use case was that I wanted to setup Stimulus after running the install.

Return values from component with yield

I'm trying to create a component to have a form container. And so use yield to pass the form helper to the view from the component.
So I did the following.

# app/views/post/_form.html.slim

= c "form", model: @post, local: true do |f|
  div
    = f.label :name
    = f.text_field :name
# frontend/components/form/_form.html.slim

= form_with(model: @model, local: @local) do |form|
  .form(data-controller="form")
    = yield(form)

But I got this error : ActionView::Template::Error (undefined method label' for nil:NilClass)`.

I tried to print the content of form or f. I have the form helper object inside the container, but in the view f is nil.

Does the block inside the view is rendered before the component?

Sass & HAML

Would it be possible to just set the template language in an initializer? I use the hamlit gem (not haml-rails or something) but now komponent generates .erb files.

And for sass/scss, could it be possible to import one index file with all sass component partials? Since variables can't be used globally when each file is imported by the javascript.

Having components present on bin/rails stats command

I guess it could be nice to have stats who come from components into bin/rails stats command.

https://github.com/rails/rails/blob/20c91119903f70eb19aed33fe78417789dbf070f/railties/lib/rails/tasks/statistics.rake

This is how rspec-rails do it.
https://github.com/rspec/rspec-rails/blob/e8054a1cd03044f725030fe8315952cf3799a395/lib/rspec/rails/tasks/rspec.rake#L8

May be we could have something similar to push components in Rails stats.

What do you think about?

Component helper methods aren't included in remote renders

A component that uses a helper method defined in its associated helper module returns a NoMethodError, when a controller action is called via ajax with a remote: true call.

# /components/foo_component/foo_component.html.haml 
=button_to foo_helper("foo"), foo_path, remote: true
# /components/foo_component/foo_component.rb
Module FooComponent
    extend ComponentHelper
    def foo_helper(text)
       return text
    end
end
# /app/controllers/foos_controller.rbs
...
def foo
    render partial: 'components/foo_component/foo_component'
end

If the helper method is included in the app/helpers/foo.rb it works just fine.

App is on komponent 1.1.4 & Rails 5.1.

Add a generator for namespaced components

rails g component admin/header should create a header component in frontent/components/admin/header

Open to discussion:

  • how should we name the Ruby module? AdminHeaderComponent? Admin::HeaderComponent?
  • how should we name the CSS class? admin_header?
  • how should we name the CSS and JS files? admin_header.js|css? just header.js|css?

Wrong import css in component generator

When we use sass or scss stylesheet engine, and we generate component rails g component button, the import './button.css' is always asking css file then than in folder we have scss or sass file.

We have to change that line in both templates/js.erb and templates/stimulus_controller_js.erb.

import "./<%= name_with_namespace %>.css";

to:

import "./<%= name_with_namespace %>.<%= stylesheet_engine %>";

We have to update features in features/component_generator/stylesheet_engine.feature for all scenarios:

    Then the file named "awesome_button/awesome_button.js" should contain:
    """
    import "./awesome_button.[stylesheet_engine]";
    """

And update features/component_generator/stimulus.feature with a check to scss at least:

    Then the file named "awesome_button/awesome_button.js" should contain:
    """
    import "./awesome_button.scss";
    """
    Then the file named "awesome_button/awesome_button_controller.js" should contain:
    """
    import "./awesome_button.scss";
    """

Standardize the component names

I can currently pass AwesomeButton, awesome_button or awesome-button to the generator, and create 3 different components.

We should probably enforce a specific case for component naming.

I think Rails partial files expect _awesome_button.html.slim

On the contrary, I usually name CSS classes with dashes: .awesome-button

Maybe stick to an existing convention? BEM, SmaCSS...

Caching implementation

I'm wondering how we could implement fragment caching for the components.

(First, if you're not familiar with the topic, I suggest you read the Rails guide about caching)

Actually, we can currently wrap manually the whole component partial in a cache helper, in order to cache it. But maybe Komponent should have a built-in way to do that.

I think we could have an API similar to the render partial one, where you can pass cached: true. Something like: = component "button", cached: true
It would then rely on Rails to do the work.

The main question is: how can we define the key for the cache entry? It could be a method within the Ruby module of the component (cache_key?)

Feel free to comment with your ideas below!

Create a file for (S)CSS variables on install

When we run rails g komponent:install, let's create a file in frontend/base/variables.(s)css

When we generate a component, we should @import the variables file by default in the generated (S)CSS file. Make sure that the path is right (especially for nested components).

Or maybe we could add some configuration to be able to always import with @import "base/variables";
It's possible with https://github.com/postcss/postcss-import but we have to check with Sass.

Style not being loaded

Salut,
très bonne idée ce gem.

J'ai suivi l'exemple du readme avec Stimulus mais il semble que le CSS dans le fichier button.scss ne soit pas chargé/compilé.

J'ai une application de test à disposition si besoin.

Dot prefix for locales

With this change the locale prefix becomes longer. It's good because it prevents conflicts but it makes the prefix even longer, so more painful to use.

I think we should :

  1. either override translate (and t) to make keys starting with a dot look into the component locales (like rails does)
  2. or use the Rails "dot look up", i.e. generate a locale file with a default hierarchy including the full namespace, for example for an "admin/button" component: <locale>.components.admin.button.admin_button (to match the partial path).

I think 1. is better because the hierarchy is simpler and if you refactor your component by moving things into partial you don't have to change your keys.

Style guide view

Do you think the gem could contain a style guide generator like mountain_view does? (Or kss)

Could be nice to have this kind of page, in development environment, that list all the components with some documentation and example(s).

import to namespace get subtracted even if there is other component

Expected Behavior

When I remove a component on a namespace using the rails destroy command, the import does not get remove from components/index.js.

Actual Behavior

When I am using rails d component admin/test import "components/admin"; get subtracted even if there is more than one component into the admin folder.

Steps to Reproduce the Problem

  • rails generate component admin/test1
  • rails generate component admin/test2
  • rails destroy component admin/test2

Specifications

  • Ruby version: 2.5.0
  • Rails version: 5.2.0.rc1
  • Komponent version: 1.1.1
  • Platform: Mac OS 10.13.2

Generate scss files without sass-rails

Hi there!

In #28 you've added generation of scss files by using to the Rails.application.config.generators.stylesheet_engine variable.

I think (correct me if I'm wrong!) this only works because a default Rails app comes with sass-rails. When using webpacker + komponent this library seems obsolete as webpacker will compile my scss into css and the whole assets pipeline is skipped.

Without sass-rails however Rails will not generate scss files, even if the generator is correctly set, i.e. Rails.application.config.generators.stylesheet_engine = :sass

Wouldn't it be better to also give the possibility to generate .scss files in a different way? Or would this be an issue that Rails itself has to solve? I.e. generators should be able to generate scss files without sass-rails if there is a proper loader installed for Webpack?

Generator to rename a component

It would be great to have a generator to rename a component:

  • change the folder name
  • change the file names
  • replace occurrences of the old name with the new name in all the files

Component generator error with single quotes string

On a project we use single quotes on js files. So our frontend/components/index.js has list of components with single quotes.

The problem with when we generate a new component, the import_to_packs method seems to remove all components import and adding the new one.

- import "components/alert/alert";
- import "components/button/button";
- import "components/container/container";
- import "components/footer/footer";
- import "components/navigation/navigation";
- import "components/pagination/pagination";
+ import "components/test/test";

Not sure what could be done. May be using the Komponent::Component.all method a created on #95, could help us to do the import list directly by getting the list of present components.

Add a method to return the list of components

If I want to create a styleguide / pattern library in my app, I'll need access to the list of available components.

I think Komponent should make a helper available to my views and/or add a class that I can use in my controllers.

Stimulus integration

We need an option to integrate stimulus into Komponent.

Installer generator changes

Append some extra lines to frontend/packs/application.js and run yarn add stimulus.

import {Application} from 'stimulus';
import {autoload} from 'stimulus/webpack-helpers';

const application = Application.start();
const controllers = require.context('../components', true, /\.js$/);
autoload(controllers, application);

Component generator changes

Due to autoloading from stimulus, import in frontend/components/index.js is unecessary, and naming convention require to change JS filename from xxx.js to xxx_controller.js.

We have the need to append some extra lines to the JS file too.

import { Controller } from "stimulus"
export default class extends Controller {
...
}

Naming changes

https://github.com/stimulusjs/stimulus/blob/aa075344f2b6806124c560bee67485382bb07007/packages/%40stimulus/webpack-helpers/index.ts#L28.

frontend/components/admin/notice/_admin_notice.html.slim
frontend/components/admin/notice/admin_notice_component.rb
frontend/components/admin/notice/admin_notice.css
frontend/components/admin/notice/admin_notice.js

#18

how to import styles using an engine

Hello,

I'm trying import styles from components that are hosted in an engine. I followed the directions for using engine.js, but receiving a webpacker can't find engine.css in /Users/jonathanbernesser/native/kompsetup/public/packs/manifest.json when doing the same instructions for stylesheet pack_tag.

If i create an engine.scssfile next to the engine.js file, and import it into the engine.js, the error disappears, but no styles from the engine are compiled. Does anyone have an example setup to make this a reality?

Thanks!

block_given? from component module

When calling block_given? from a method in a component module, it returns false, even if the component is called with a block.

If I call block_given? from the partial, it works as expected.

Is there a way to improve this?

Module:

module TestComponent
  extend ComponentHelper

  def modifiers
    @modifiers ||= ""
    @modifiers += " has-block" if block_given? # this doesn't work
    @modifiers
  end

Partial:

.test(class=modifiers)
  - if block_given?
    .test-content= yield

Rendering the component with a block:

= c "test"
  p This is a test

Add an example of generic javascript files

In the documentation, we don't have an example on how to handle generic javascript file, only javascript file specific to component. We need to show where to set that kind of files in our component-based hierarchy, in frontend ?

A lot of people shoulda use turbolinks or rails-ujs so let's show how to handle that kind of modules in our structure.

Problem with haml generated.

Hi! Thanks for your great work and ideas.

When I generate a component with haml i get this

schermata 2018-07-20 alle 12 45 36

When with haml can be enough

.print-btn

or (if you want to be more verbose)

div{class: 'print-btn'}

throw an exception when false or nil is passed into a component property

Expected Behavior

When passing a false or nil it should not crash

Actual Behavior

throwing a NameError: undefined local variable or method options' for`

Steps to Reproduce the Problem

passing a false or nil on any value of a component like this <%= c "test", value: false %>

Context

I have a component that's taking in a boolean and display a check mark or a cross depending if it's a true or nil

Specifications

Ruby version: 2.5.0
Rails version: 5.2.0.rc1
Komponent version: 1.1.1
Platform: Mac OS 10.13.2

Configuration via initializer and components path

Found this gem through the Evil Martians blog, nice idea to make a gem out of it.

How does the configuration via initializer work exactly, like so?

Komponent.configure = {
  stimulus: true,
  locale: true
}

Also is it possible to change the default component path from frontend to say app/frontend?

Generate component when no available locale is defined

When the available_locales config key is not defined and you run rails g component button --locales, by default Rails considers that all locales are available and the generator creates dozens of files.

How can we handle this graciously? Maybe a friendly reminder to define the available_locales before running the generator? But how can we determine if it has been defined or not?

Gem example

Do you have a working example of a gem that leverages Komponent ready for a Rails app.?

How access the given block inside the helper

I'm looking for using the given block to my component inside the helper. But I didn't found how to retrieve the block.

I used block_given_to_component? and that's works, but if I use @block_given_to_component.call but it returns me the content of my parent component.

Is it a missing feature? Or a mistake of usage?

I do some test by extended the ComponentHelper module like following:

module ComponentHelper
  def yield_or_property(property_name)
    if block_given_to_component?
      block_given_to_component
    elsif properties.key? property_name
      properties[property_name]
    else
      raise ArgumentError, "Missing required block component or parameter: #{property_name}"
    end
  end

  def property_or_yield(property_name)
    if properties.key? property_name
      properties[property_name]
    elsif block_given_to_component?
      block_given_to_component
    else
      raise ArgumentError, "Missing required block component or parameter: #{property_name}"
    end
  end

  private

  def block_given_to_component
    @_block_given_to_component = @block_given_to_component.yield if block_given_to_component?
  end
end

And by using this method yield_or_property(:text) into the components views.

But it returns me a LocalJumpError.

Can't append to inexistant file

Running generator fails due to inexistant file (append frontend/components/index.js).

We need to provide some extra informations in readme.

mv app/javascript ./frontend
mkdir frontend/components
touch frontend/components/index.js

# frontend/packs/application.js
import '../components/index'

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.