Adding styling to rails react component wrapping div - ruby-on-rails-4

I'm using react_component in my Rails project. This inserts a wrapping div for the react.js component, e.g.
<div data-react-class="ButtonSwitchToTab" data-react-props="{...}">
<a href="#" class="btn btn-primary btn-lg" ... data-reactid=".2">
Add / Invite People
</a>
</div>
What I really need is to insert style information into this wrapping div so that components align appropriately, like so:
<div data-react-class="ButtonSwitchToTab" data-react-props="{...}"
style="display:inline-block"> <<<<<------
<a href="#" class="btn btn-primary btn-lg" ... data-reactid=".2">
Add / Invite People
</a>
</div>
What's the appropriate way to do that?

Ah, dove deeper into react_rails doco (Helper's signature) and found that I could add pass-through html_options.
So:
<%= react_component('ButtonSwitchToTab', {prop: .., prop: ..},
{:style => "display:inline-block"}) %>

I needed to style the component mount point as well, and I just assigned a custom ID to my component:
= react_component 'LoginPage', id: 'login-page-container'
(HAML template)

Related

How to identify button selected in cakephp 3.x?

I viewed the answer Which Submit Button was Clicked in CakePHP?. That situation doesn't apply to me, because I have the same action for each button.
I want to reuse a bootstrap modal and I want to know which item was selected when the modal was invoked. Very simply, I have a table with grades for each school object. When the user clicks the add button, I want to invoke the modal and add a grade for that object. I want to know which object was selected, because I want to reuse the modal for all objects. How can I do that in cakephp 3.x ?
After a teacher wants to add a grade and press the + button how do I know if he/she selected Mathematics or English if I use the same modal for grade saving? .
okey, most simple way is in modal to have hidden field, which contains a subject. I think this has not much to do with cakephp.
Example should look like this:
function modalopen(subject) {
$('#modal #subject').val(subject);
$('#modal').modal('toggle');
}
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title></title>
<script src="https://code.jquery.com/jquery-3.2.1.slim.min.js" integrity="sha384-KJ3o2DKtIkvYIK3UENzmM7KCkRr/rE9/Qpg6aAZGJwFDMVNA/GpGFF93hXpG5KkN" crossorigin="anonymous"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/popper.js/1.11.0/umd/popper.min.js" integrity="sha384-b/U6ypiBEHpOf/4+1nzFpr53nxSS+GLCkfwBdFNTxtclqqenISfwAzpKaMNFNmj4" crossorigin="anonymous"></script>
<script src="https://maxcdn.bootstrapcdn.com/bootstrap/4.0.0-beta/js/bootstrap.min.js" integrity="sha384-h0AbiXch4ZDo7tp9hKZ4TsHbi047NrKGLO3SEJAg45jXxnGIfYzk4Si90RDIqNm1" crossorigin="anonymous"></script>
<link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/4.0.0-beta/css/bootstrap.min.css" integrity="sha384-/Y6pD6FV/Vv2HJnA6t+vslU6fwYXjCFtcEpHbNJ0lyAFsXTsjBbfaDjzALeQsN6M" crossorigin="anonymous">
</head>
<body>
<button type="button" class="btn btn-info" onclick="modalopen('english')">+</button>
<button type="button" class="btn btn-info" onclick="modalopen('math')">+</button>
<div class="modal fade" id="modal" tabindex="-1" role="dialog" aria-labelledby="" aria-hidden="true">
<div class="modal-dialog">
<div class="modal-content">
<div class="modal-header">
<button type="button" class="close" data-dismiss="modal" aria-hidden="true">×</button>
<h4 class="modal-title" id=""></h4>
</div>
<div class="modal-body">
sub (will be hidden):<br>
<input type="text" name="subject" id ="subject" value="" placeholder="will be hidden"><br>
Mark:<br>
<input type="text" name="mark" id ="mark" value="" placeholder="mark">
</div>
<div class="modal-footer">
<button type="button" class="btn btn-default" data-dismiss="modal">Close</button>
<button type="button" class="btn btn-primary">Save</button>
</div>
</div>
</div>
</div>
</body>
</html>
The only way of determining which button was pressed is to use Javascript. This means not using the html-tag-option based method on the button to launch the modal, ie:
<!-- Button trigger modal: CAN *NOT* USE THIS TECHNIQUE!! -->
<button type="button" class="btn btn-primary" data-toggle="modal" data-target="#exampleModal">
Launch demo modal
</button>
I assume the form inside your modal is a cake-generated form, and the submit button is a normal form submit, which triggers a redraw of the page, effectively killing the modal (IE there's no "modal takedown").
To keep as close as possible to Cake's paradigm, I would send it back to the server in a hidden form field.
Something like:
on the cake side in your ctp while creating your form:
// HTML WRAPPER FOR MODAL
<?= $this->Form->create(yourentity); ?>
(your form stuff)
<?= $this->Form->hidden("subject_id",["id"=>"subject-id-field"]);
(end of form stuff including submit)
<?= $this->Form->end(); ?>
// HTML WRAPPER FOR MODAL
This will generate something in your form like
<input type="hidden" name="subject_id" id="subject-id-field"/>
We'll need to grab this hidden field in Javascript, so I'm giving it both a name (form-specific) and an id (global), since I prefer referring to everything with #id-syntax, but you could also use form[name=subject_id] and get rid of the id clause
On the browser side in HTML, to create your buttons:
<button type="button" class="btn btn-primary" onclick="launch_dialog('MATH')">Add Math</button>
On the browser side in javascript, the function to call when the button is clicked, which sets the subject id in the form and then launches the modal/form:
<script>
function launch_dialog(subject) {
$("#subject-id-field").val(subject); // use the id of the hidden field
$("#your-modal").modal("show"); // or whatever you do to launch the modal
}
</script>
On the server side in the function that the form targets:
# in your controller php file
function formAction() {
if($this->request->data["subject_id"]=="MATH") { // use the name of the hidden field
// do math record
}
// etc
}
Another note - if your grade record really does have a subject_id field which belongsTo a Subject record, you can have the button's onclick function call the launch_dialog function with that constant, and then you won't need any IF function inside the server action code. Just make sure to use the original record to generate the id, eg:
In controller before render:
$this->set("subjects",$this->[entity]->Subjects->find("list");
In ctp file, something like:
<?php foreach($subjects as $id=>$name): ?>
<button type="button" class="btn btn-primary"
onclick="launch_dialog(<?= $id ?>)">Add <?= $name ?></button>
<?php endforeach; ?>

Selenium & django testing

I'm trying to click on a link in navbar to trigger modal for third party authentication, which I need to test. I have some help before about how to fetch and click with selenium, and everything was good until this.
This is the link I'm trying to click
<li class="pull-right hidden-xs hidden-sm" id="showlogin"><button type="button" class="btn btn-sm btn-info round">GET STARTED</button></li>
my test structure is:
def test_live_societe_login_using_third_party_modal(self):
"""TODO: Docstring for test_live_societe_login_third_party_modal.
:returns: return modal for third party authentication
"""
WebDriverWait(self.browser, 10).until(lambda browser:
self.browser.find_element_by_id
('showlogin')).click()
self.assertIn('http://societe.herokuapp.com/contact', self.browser.current_url)
I was able to test all application pages, but hitting this one I got selenium.common.exceptions.ElementNotVisibleException: Message: Element is not currently visible and so may not be interacted with exception. I'm a little confused about it, can someone help me over come this, thanks.
Ok, I was locking at this the wrong way, in my navbar I have constructed GET STARTED button like this:
<li class="pull-right hidden-xs hidden-sm" id="showlogin"><button type="button" class="btn btn-sm btn-info round">GET STARTED</button></li>
<li class="dropdown hidden-md hidden-lg" id="showlogin">
<button type="button" class="btn btn-sm btn-info round">GET STARTED</button>
<ul class="dropdown-menu">
<li class="btn-group-vertical"><a id="facebook_login" href="/accounts/facebook/login" method="js-sdk" class="btn btn-default navbar-btn"><i class="fa fa-facebook"></i> Sign in with Facebook</a>
<a id="linkedin_login" href="/accounts/linkedin/login" class="btn btn-default navbar-btn"><i class="fa fa-linkedin"></i> Sign in with LinkedIn</a>
<a id="twitter_login" href="/accounts/twitter/login" class="btn btn-default navbar-btn"><i class="fa fa-twitter"></i> Sign in with Twitter</a></li>
</ul>
</li>
You can see that I have two <li></li> tags, one is a button for larger viewport and the other tag have dropdown menu for smaller viewport. In the first place I was not selecting the right <li></li> tag. When selenium start testing he opened a tablet view browser (≥768px) and that changed the situation in the sense of I needed to select dropdown and then find a button to click and trigger third party authentication.
I can select dropdown with Css selector, so I just find_element_by_css_selector('div#navbar ul li.dropdown'), after this I selected right social button. All I need to do now is handle social authentication.
This is the code for selecting dropdown and social button for third party authentication:
def test_live_societe_login_using_third_party_modal(self):
"""TODO: Docstring for test_live_societe_login_third_party_modal.
:returns: return modal for third party authentication
"""
dropSelect = WebDriverWait(self.browser, 20).until(
lambda browser: (self.browser.find_element_by_css_selector('div#navbar ul li.dropdown')))
dropSelect.click()
twitter_choice = dropSelect.find_element_by_id('twitter_login')
twitter_choice.click()

Dynamically set properties on a view from Handlebars

I'm trying to DRY up my templates by creating views for common layout elements. I have the following views defined
App.SplitListView = Em.View.extend({
tagName: 'div',
classNames: [ 'panel', 'panel-default' ]
});
App.SplitListHeaderView = Em.View.extend({
classNames: [ 'panel-heading' ],
templateName: 'split-list-header-view-layout'
});
The template for the SplitListView is a simple {{yield}}
The template for the SplitListHeaderView is
<span class="panel-title">{{view.headerText}}</span>
{{#if view.showCreateButton}}
<span class="pull-right">
<button type="button" class="btn btn-primary btn-sm">
<i class="fa fa-plus fa-lg" style="padding-right: 10px;"></i>{{view.createButtonText}}
</button>
</span>
{{/if}}
Then the template for the submodule:
{{#view App.SplitListView}}
{{view App.SplitListHeaderView headerTextBinding="Sandwiches" showCreateButtonBinding=true createButtonTextBinding="Make me a sandwich!"}}
{{/view}}
The desired end result is that I'd like to use the SplitListView and SplitListHeaderView everywhere in my app that I use that layout and just set the relevant bits of text via the controller. But so far it's just not working. I feel like this should be easy to do and I'm just missing something. I found this question which looks to be along the same lines as my question but the solution did not work for me.
Does anyone have any experience with something like this or am I off my rocker in trying to use views in this manner?
I believe you have three options here:
1) Use a component instead of a view. Components are reusable, self-contained 'modules' that allow you to bind properties from the component's context in exactly the same manner as you are trying with your view.
2) If the only thing you're reusing between the current non-DRY templates is the html/handlebars you should use a {{partial}} instead of a view because that doesn't create any scope within the view hierarchy and will allow you to bind the handlebars in the partial directly to route's view/controller properties without specifying additional property bindings or scope.
3) Use a Em.Handlebars.helper to accept three arguments (headerText, buttonText, and showCreateButton) and return a new Handlebars.SafeString('some html string'); or something along those lines.
A solution
If I were you, I would utilize methods 2 and 3 together as follows:
First, use a helper (I'm using a helper with a globally accessible method) instead of App.SplitListView to wrap some html around the buffer (i.e. content) inside of the opening and closing handlebars:
// Handy method you can use across many different areas of the app
// to wrap content in simple html
Utils.wrapBuffer = function(open, close, options, context) {
options.data.buffer.push('\n' + open);
if (options.fn(context)) {
options.data.buffer.push('\n' + options.fn(context));
}
options.data.buffer.push('\n' + close);
}
Then the actual helper
App.Handlebars.helper('split-list', function(options) {
var open = '<div class="panel panel-default">;
var close = '</div>';
Utils.wrapBuffer(open, close, options, this);
});
Now, your template will look like:
{{#split-list}}
// This is where our content will go
{{/split-list}}
This has the advantage of wrapping what is inbetween the handlebars opening and closing tags with html without adding or changing scope. Thus, property bindings will work seamlessly.
Now, replace App.SplitListheaderView with a component set up in a similar manner:
App.SplitListHeaderComponent = Em.Component.extend({
classNames: ['panel-heading'],
headerText: null,
createButtonText: null,
showCreateButton: false,
});
You layout (components use layouts, not templates) will be located at templates/components/split-list-header.hbs. it will look as follows:
<span class="panel-title">{{headerText}}</span>
{{#if showCreateButton}}
<span class="pull-right">
<button type="button" class="btn btn-primary btn-sm">
<i class="fa fa-plus fa-lg" style="padding-right: 10px;"></i>{{createButtonText}}
</button>
</span>
{{/if}}
Note, that properties are something not view.something and each property is declared allowing it to be overwritten in the handlebars helper. Now your submodule's template will look like:
{{#split-list}}
{{split-list-header
headerText='Sandwiches'
showCreateButton=true
createButtonText='Make me a sandwich!'
}}
{{/split-list}}
If you wanted, you could bing these properties to a property on your controller or view instead of writing them in the template every time.
Another Improvement
You could go one step further and scrap the wrapping helper all together because it's not doing anything except adding HTML. In this case, the component would look like {{split-list headerText=blah...}} and would have the following template:
<div class="panel-heading">
<span class="panel-title">{{view.headerText}}</span>
{{#if view.showCreateButton}}
<span class="pull-right">
<button type="button" class="btn btn-primary btn-sm">
<i class="fa fa-plus fa-lg" style="padding-right: 10px;"></i>{{view.createButtonText}}
</button>
</span>
{{/if}}
</div>

Ember.js: Toggle Nested Views

I have a header with some login/signup forms that popup when you click the respective buttons.
While it was working fine using just jQuery, I've now started to integrate Ember into the application and I'm running into some trouble with some simple toggle functionality.
Here's the basic HTML markup:
<header>
<h1>Page Title<h1>
<nav>
<a id="toggles-login" class="button {{active_class_binding}}">Login</a>
<a id="toggles-signup" class="button {{active_class_binding}}">Signup</a>
</nav>
<div id="popup-forms">
<div id="login-form"></div>
<div id="signup-form"></div>
</div>
<header>
I'm completely new to Ember and I really have no idea how to set this up. The only thing I want is to be able to set the popup forms up as Ember.View objects and toggle them with some action helpers.
I really am lost on this one.
A simple solution would be to trigger simple actions to show the respective forms:
<a id="toggles-login" class="button {{active_class_binding}}" {{action showLoginForm target="view"}}>Login</a>
<a id="toggles-signup" class="button {{active_class_binding}}" {{action showSignupForm target="view"}}>Signup</a>
The corresponding view would have to implement both actions:
App.YourView = Ember.View.extend({
showLoginForm : function(){
this.$("#login-form").toggle();
},
showSignupForm : function(){
this.$("#signup-form").toggle();
}
});

Bootstrap accordion with ember

Hi there i have a small question that belonging to my small ember application.
JSFiddle upload is here. I used bootstrap accordion to visualize my tickets. When i click on the "click" it adds another accordion into my view. But sadly it cannot be opened or used. Every accordion i dynamically created cannot be opened or closed. There is no error or exception thrown and from my point of view everything should work fine. My click-function looks like this:
click: function() {
this.counter++,
name = this.name+this.counter.toString(),
tre = App.Ticket.create({
Text: "try",
id: name
});
this.pushObject(tre);
}});
The belonging html is here:
<div class="accordion-group">
{{#each content}}
<div class="accordion-heading">
<a class="accordion-toggle" data-toggle="collapse" data-parent="#accordion2" {{bindAttr href="id"}}>
Ticket ID/Störfall
</a>
</div>
<div {{bindAttr id="id"}} class="accordion-body collapse in ">
<div class="accordion-inner">
{{Text}}
</div>
</div>
{{/each}}
</div>
I hope you can help me.
You can add an action helper to the accordion link title
<a class="accordion-toggle" data-toggle="collapse"
data-parent="#accordion2"
{{bindAttr href="item.id"}}
{{action "click" item target="view"}}>
Ticket ID/Störfall
</a>
Then implement a click handler event in your view
App.TicketView = Em.View.extend({
click:function(context) {
var el = this.$('a[href='+context.get('id')+']');
el.toggleClass('collapsed');
this.$('#'+el.attr('href')).toggleClass('in');
}
});
Here's a working fiddle