File Coverage

blib/lib/HTML/FormHandler/Widget/Wrapper/Bootstrap3.pm
Criterion Covered Total %
statement 77 104 74.0
branch 38 58 65.5
condition 19 36 52.7
subroutine 15 17 88.2
pod 0 10 0.0
total 149 225 66.2


line stmt bran cond sub pod time code
1             package HTML::FormHandler::Widget::Wrapper::Bootstrap3;
2             # ABSTRACT: Twitter Bootstrap 3.0 field wrapper
3             $HTML::FormHandler::Widget::Wrapper::Bootstrap3::VERSION = '0.40067';
4 6     6   4053 use Moose::Role;
  6         8  
  6         50  
5 6     6   21378 use namespace::autoclean;
  6         9  
  6         44  
6 6     6   1549 use HTML::FormHandler::Render::Util ('process_attrs');
  6         10  
  6         36  
7 6     6   980 use List::Util 1.33 ('any');
  6         167  
  6         6730  
8              
9             with 'HTML::FormHandler::Widget::Wrapper::Base';
10              
11              
12 3     3 0 7 sub is_b3 {1}
13              
14             sub build_wrapper_tags {
15             {
16 20     20 0 575 radio_element_wrapper => 1,
17             checkbox_element_wrapper => 1,
18             }
19             }
20              
21             sub wrap_field {
22 23     23 0 34 my ( $self, $result, $rendered_widget ) = @_;
23              
24 23         23 my $output;
25              
26             # create attribute string for wrapper
27 23 100       624 if ( $self->do_wrapper ) {
28 20         95 my $attr = $self->wrapper_attributes($result);
29             # no 'control-group' class for Hidden fields, 'form-actions' for submit/reset
30 20         31 my $div_class = 'form-group';
31 20         23 unshift @{$attr->{class}}, $div_class;
  20         60  
32 20         50 my $attr_str = process_attrs( $attr );
33             # wrapper is always a div
34 20         102 $output .= $self->get_tag('before_wrapper');
35 20         63 $output .= qq{\n<div$attr_str>};
36             }
37             # render the label
38 23 100       550 $output .= "\n" . $self->do_render_label($result, undef, ['control-label'] )
39             if $self->do_label;
40 23         60 $output .= $self->get_tag('before_element');
41              
42             # the controls div; used to have 'controls' class. Now it comes from
43             # the 'element_wrapper_class'. Used for column layout.
44 23         128 my $ew_attr = $self->element_wrapper_attributes($result);
45 23         55 my $element_wrapper_attrs = process_attrs( $ew_attr );
46 23 100       581 $output .= qq{\n<div$element_wrapper_attrs>}
47             unless !$self->do_wrapper;
48              
49             # yet another tag
50 23         60 $output .= $self->get_tag('before_element_inside_div');
51             # handle input-prepend and input-append
52 23 50 33     51 if( $self->get_tag('input_prepend') || $self->get_tag('input_append') ||
    100 33        
53             $self->get_tag('input_append_button') ) {
54 0         0 $rendered_widget = $self->do_prepend_append($rendered_widget);
55             }
56             elsif( lc $self->widget eq 'checkbox' ) {
57 7         21 $rendered_widget = $self->wrap_checkbox($result, $rendered_widget, 'b3_label_left')
58             }
59              
60 23         65 $output .= "\n$rendered_widget";
61             # various 'help-inline' bits: errors, warnings
62 23 50       52 unless( $self->get_tag('no_errors') ) {
63             $output .= qq{\n<span class="help-block">$_</span>}
64 23         778 for $result->all_errors;
65 23         749 $output .= qq{\n<span class="help-block">$_</span>} for $result->all_warnings;
66             }
67             # extra after element stuff
68 23         53 $output .= $self->get_tag('after_element');
69             # close element_wrapper 'control' div
70 23 100       546 $output .= '</div>' unless !$self->do_wrapper;
71             # close wrapper
72 23 100       531 if ( $self->do_wrapper ) {
73 20         27 $output .= "\n</div>";
74 20         44 $output .= $self->get_tag('after_wrapper');
75             }
76 23         148 return "$output";
77             }
78              
79             # don't render label for checkboxes
80             sub do_render_label {
81 16     16 0 23 my ( $self ) = @_;
82              
83 16 100       378 return '' if $self->type_attr eq 'checkbox';
84 13         878 HTML::FormHandler::Widget::Wrapper::Base::do_render_label(@_);
85             }
86              
87             sub add_standard_element_classes {
88 17     17 0 20 my ( $self, $result, $class ) = @_;
89 17 100       515 push @$class, 'has-error' if $result->has_errors;
90 17 50       520 push @$class, 'has-warning' if $result->has_warnings;
91 17 50       378 push @$class, 'disabled' if $self->disabled;
92 17 100 33     78 push @$class, 'form-control'
      66        
      66        
93             if $self->html_element eq 'select' || $self->html_element eq 'textarea' ||
94             $self->type_attr eq 'text' || $self->type_attr eq 'password';
95             }
96              
97             sub add_standard_wrapper_classes {
98 20     20 0 26 my ( $self, $result, $class ) = @_;
99 20 100 66     655 push @$class, 'has-error' if ( $result->has_error_results || $result->has_errors );
100 20 50       608 push @$class, 'has-warning' if $result->has_warnings;
101             # TODO: has-success?
102             }
103              
104             sub add_standard_label_classes {
105 13     13 0 20 my ( $self, $result, $class ) = @_;
106 13 100       307 if ( my $classes = $self->form->get_tag('layout_classes') ) {
107 9         12 my $label_class = $classes->{label_class};
108 9 50 33 0   85 if ( $label_class && not any { $_ =~ /^col\-/ } @$class ) {
  0         0  
109 9         13 push @$class, @{$classes->{label_class}};
  9         29  
110             }
111             }
112             }
113              
114             sub add_standard_element_wrapper_classes {
115 23     23 0 31 my ( $self, $result, $class ) = @_;
116 23 100       602 if ( my $classes = $self->form->get_tag('layout_classes') ) {
117 17 100 66     127 if ( exists $classes->{element_wrapper_class} &&
118 2     2   10 not any { $_ =~ /^col\-/ } @$class ) {
119 15         14 push @$class, @{$classes->{element_wrapper_class}};
  15         24  
120             }
121 17 100 100     462 if ( exists $classes->{no_label_element_wrapper_class} &&
      33        
      66        
122             ( ! $self->do_label || $self->type_attr eq 'checkbox' ) &&
123 8     8   30 not any { $_ =~ /^col\-.*offset/ } @$class ) {
124 8         7 push @$class, @{$classes->{no_label_element_wrapper_class}};
  8         22  
125             }
126             }
127             }
128              
129             sub wrap_checkbox {
130 7     7 0 15 my ( $self, $result, $rendered_widget ) = @_;
131              
132             # use the regular label
133 7   33     198 my $label = $self->option_label || $self->label;
134 7 50       20 $label = $self->get_tag('label_no_filter') ? $self->_localize($label) : $self->html_filter($self->_localize($label));
135 7         172 my $id = $self->id;
136 7         16 my $for = qq{ for="$id"};
137              
138             # return unwrapped checkbox with 'checkbox-inline'
139 7 100       21 return qq{<label class="checkbox-inline" $for>$rendered_widget\n$label\n</label>}
140             if( $self->get_tag('inline') );
141              
142             # return wrapped checkbox, either on left or right
143 4 50       12 return qq{<div class="checkbox"><label$for>\n$label\n$rendered_widget</label></div>}
144             if( $self->get_tag('label_left') );
145 4         16 return qq{<div class="checkbox"><label$for>$rendered_widget\n$label\n</label></div>};
146             }
147              
148             sub do_prepend_append {
149 0     0 0   my ( $self, $rendered_widget ) = @_;
150              
151 0           my @class;
152 0 0         if( my $ip_tag = $self->get_tag('input_prepend' ) ) {
153 0           $rendered_widget = qq{<span class="input-group-addon">$ip_tag</span>$rendered_widget};
154 0           push @class, 'input-group';
155             }
156 0 0         if ( my $ia_tag = $self->get_tag('input_append' ) ) {
157 0           $rendered_widget = qq{$rendered_widget<span class="input-group-addon">$ia_tag</span>};
158 0           push @class, 'input-group';
159             }
160 0 0         if ( my $iab_tag = $self->get_tag('input_append_button') ) {
161 0           my $iab_element_attr_tag = $self->get_tag('input_append_button_element_attr');
162              
163 0           my ($btn_class, $attr);
164 0 0         if (ref $iab_element_attr_tag eq 'HASH') {
165 0 0         $btn_class = ref $iab_element_attr_tag->{class} eq 'ARRAY' ? shift @{$iab_element_attr_tag->{class}} : $iab_element_attr_tag->{class};
  0            
166 0           $attr = process_attrs( $iab_element_attr_tag );
167             }
168              
169 0 0         my @buttons = ref $iab_tag eq 'ARRAY' ? @$iab_tag : ($iab_tag);
170 0           my $group = qq{<span class="input-group-btn">};
171 0           foreach my $btn ( @buttons ) {
172 0           $group .= qq{<button type="button" class="btn $btn_class"$attr>$btn</button>};
173             }
174 0           $group .= qq{</span>};
175 0           $rendered_widget = qq{$rendered_widget$group};
176 0           push @class, 'input-group';
177             }
178 0           my $attr = process_attrs( { class => \@class } );
179 0           $rendered_widget =
180             qq{<div$attr>
181             $rendered_widget
182             </div>};
183 0           return $rendered_widget;
184             }
185              
186             1;
187              
188             __END__
189              
190             =pod
191              
192             =encoding UTF-8
193              
194             =head1 NAME
195              
196             HTML::FormHandler::Widget::Wrapper::Bootstrap3 - Twitter Bootstrap 3.0 field wrapper
197              
198             =head1 VERSION
199              
200             version 0.40067
201              
202             =head1 SYNOPSIS
203              
204             Wrapper to implement Bootstrap 3.0 style form element rendering.
205              
206             =head1 DESCRIPTION
207              
208             Differences from the Bootstrap 2.0 wrapper:
209              
210             The wrapper class is 'form-group' instead of 'control-group'
211             The div wrapping the form element does not
212             have the 'controls' class. Used for sizing css classes.
213             Input prepend & append use different classes
214             The input elements have a 'form-control' class
215              
216             Tags supported:
217              
218             label_no_filter -- don't html filter the label
219             label_after -- useful for putting a colon, or other trailing formatting
220             before_element -- insert tag before input, outside element's control div
221             before_element_inside_div -- insert tag before input element, inside control div
222             input_prepend -- for Bootstrap 'input-prepend' class
223             input_append -- for Bootstrap 'input-append' class
224             input_append_button -- 'input-append' with button instead of span
225             no_errors -- don't append error to field rendering
226             after_element -- insert tag after input element
227              
228             =head1 AUTHOR
229              
230             FormHandler Contributors - see HTML::FormHandler
231              
232             =head1 COPYRIGHT AND LICENSE
233              
234             This software is copyright (c) 2016 by Gerda Shank.
235              
236             This is free software; you can redistribute it and/or modify it under
237             the same terms as the Perl 5 programming language system itself.
238              
239             =cut