Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 10 additions & 1 deletion CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,17 +5,26 @@ The format is based on [Keep a Changelog](http://keepachangelog.com/)
and this project adheres to [Semantic Versioning](http://semver.org/).

## [Unreleased]

### Added
- Added a `tag` option to change the tag used to render the component (default is `div`)

## 0.2.0 - 2017-03-20

### Added
- support for Turbolinks 5, Turbolinks 2.4 and PJAX. Components will be mounted and unmounted when Turbolinks-specific events occur. Also, the integration works with Turbolinks 5 cache.
- New `WebpackerReact.setup({Component1, Component2, ...})` initialization API. The old API couldn't properly detect the components' names, thus user is required to provide the names in the configuration object's keys.

### Removed
- `WebpackerReact.register(Component)` has been dropped in favor of `WebpackerReact.setup({Component})`


## 0.1.0 - 2017-02-23

### Added
- First released version
- render React components from views using the `react_component` helper
- render React components from controllers using `render react_component: 'name'` (#1 by @daninfpj)
- basic Hot Module Remplacement (#7 by @mfazekas)

[Unreleased]: https://github.com/renchap/webpacker-react/compare/v0.1.0...HEAD
[Unreleased]: https://github.com/renchap/webpacker-react/compare/v0.2.0...HEAD
17 changes: 15 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -70,12 +70,19 @@ Now you can render React components from your views or your controllers.

### Rendering from a view

Use the `react_component` helper:
Use the `react_component` helper. The first argument is your component's name, the second one is the `props`:

```erb
<%= react_component('Hello', name: 'React') %>
```

You can pass a `tag` argument to render the React component in another tag than the default `div`. All other arguments will be passed to `content_tag`:

```erb
<%= react_component('Hello', { name: 'React' }, tag: :span, class: 'my-custom-component') %>
# This will render <span class="my-custom-component" data-react-class="Hello" data-react-props="..."></div>
```

### Rendering from a controller

```rb
Expand All @@ -86,7 +93,13 @@ class PageController < ApplicationController
end
```

You can pass any of the usual arguments to render in this call: `layout`, `status`, `content_type`, etc.
You can use the `tag_options` argument to change the generated HTML, similar to the `react_component` method above:

```rb
render react_component: 'Hello', props: { name: 'React' }, tag_options: { tag: :span, class: 'my-custom-component' }
```

You can also pass any of the usual arguments to `render` in this call: `layout`, `status`, `content_type`, etc.

*Note: you need to have [Webpack process your code](https://github.com/rails/webpacker#binstubs) before it is available to the browser, either by manually running `./bin/webpack` or having the `./bin/webpack-watcher` process running.*

Expand Down
4 changes: 3 additions & 1 deletion lib/webpacker/react/component.rb
Original file line number Diff line number Diff line change
Expand Up @@ -11,8 +11,10 @@ def initialize(name)
end

def render(props = {}, options = {})
tag = options.delete(:tag) || :div
data = { data: { "react-class" => @name, "react-props" => props.to_json } }
content_tag(:div, nil, options.deep_merge(data))

content_tag(tag, nil, options.deep_merge(data))
end
end
end
Expand Down
3 changes: 2 additions & 1 deletion lib/webpacker/react/railtie.rb
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,8 @@ class Engine < ::Rails::Engine
initializer :webpacker_react_renderer, group: :all do |_app|
ActionController::Renderers.add :react_component do |component_name, options|
props = options.fetch(:props, {})
html = Webpacker::React::Component.new(component_name).render(props)
tag_options = options.fetch(:tag_options, {})
html = Webpacker::React::Component.new(component_name).render(props, tag_options)
render_options = options.merge(inline: html)
render(render_options)
end
Expand Down
8 changes: 8 additions & 0 deletions test/example_app/app/controllers/custom_tag_controller.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
class CustomTagController < ApplicationController
def view_component
end

def controller_component
render react_component: "HelloReact", props: { name: "a component rendered from a controller in a span" }, tag_options: { tag: :span }
end
end
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
<%= react_component('HelloReact', { name: 'a component rendered from a view in a span' }, tag: :span) %>
3 changes: 3 additions & 0 deletions test/example_app/config/routes.rb
Original file line number Diff line number Diff line change
Expand Up @@ -9,5 +9,8 @@

get "/two_packs/view_all", to: "two_packs#view_all"

get "/custom_tag_view", to: "custom_tag#view_component"
get "/custom_tag_controller", to: "custom_tag#controller_component"

root to: "pages#view_component"
end
17 changes: 17 additions & 0 deletions test/integration/custom_tag_test.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
require "test_helper"

class CustomTagTest < ActionDispatch::IntegrationTest
test "renders from a view with a custom tag" do
require_js

visit "/custom_tag_view"
assert_selector "span[data-react-class]", text: "Hello, I am a component rendered from a view in a span!"
end

test "renders from a controller with a custom tag" do
require_js

visit "/custom_tag_controller"
assert_selector "span[data-react-class]", text: "Hello, I am a component rendered from a controller in a span!"
end
end
6 changes: 0 additions & 6 deletions test/integration/renderer_test.rb
Original file line number Diff line number Diff line change
Expand Up @@ -30,10 +30,4 @@ def url_prefix
assert page.has_content? "component 1"
assert page.has_content? "component 2"
end

private

def require_js
Capybara.current_driver = Capybara.javascript_driver
end
end
6 changes: 0 additions & 6 deletions test/integration/renderer_with_two_packs_test.rb
Original file line number Diff line number Diff line change
Expand Up @@ -15,10 +15,4 @@ class RendererWithTwoPacksTest < ActionDispatch::IntegrationTest
assert page.has_content? "Component A"
assert page.has_content? "Component B"
end

private

def require_js
Capybara.current_driver = Capybara.javascript_driver
end
end
4 changes: 4 additions & 0 deletions test/test_helper.rb
Original file line number Diff line number Diff line change
Expand Up @@ -14,4 +14,8 @@ class ActionDispatch::IntegrationTest
def teardown
Capybara.current_driver = nil
end

def require_js
Capybara.current_driver = Capybara.javascript_driver
end
end
12 changes: 12 additions & 0 deletions test/webpacker/react/component_test.rb
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,18 @@ def test_it_accepts_html_options
)
end

def test_it_accepts_tag_option
html = Webpacker::React::Component.new(@component[:name])
.render(
@component[:props],
tag: "span"
)

assert(
html.include?("<span data-react-class="), "it outputs a span"
)
end

private

def escaped_props(props)
Expand Down