File Coverage

blib/lib/HTML/FormBuilder/Field.pm
Criterion Covered Total %
statement 111 121 91.7
branch 50 62 80.6
condition 15 20 75.0
subroutine 11 11 100.0
pod 2 2 100.0
total 189 216 87.5


line stmt bran cond sub pod time code
1              
2             use strict;
3 7     7   68 use warnings;
  7         15  
  7         206  
4 7     7   42 use 5.008_005;
  7         15  
  7         150  
5 7     7   100  
  7         23  
6             use Carp;
7 7     7   36 use Scalar::Util qw(weaken blessed);
  7         16  
  7         435  
8 7     7   45  
  7         22  
  7         387  
9             use Moo;
10 7     7   3345 use namespace::clean;
  7         69468  
  7         35  
11 7     7   12379 extends qw(HTML::FormBuilder::Base);
  7         69653  
  7         48  
12              
13             our $VERSION = '0.13'; ## VERSION
14              
15             has data => (
16             is => 'ro',
17             isa => sub {
18             my $data = shift;
19             croak('data should be a hashref') unless ref($data) eq 'HASH';
20             },
21             default => sub {
22             {};
23             },
24             );
25              
26             my (undef, @args) = @_;
27             my %args = (@args % 2) ? %{$args[0]} : @args;
28 97     97 1 11677  
29 97 50       364 my $data = $args{data};
  0         0  
30              
31 97         182 # normalize: if 'input' is not an array, then make it as an array, so that
32             # we can process the array directly
33             if ($data->{input} && ref($data->{input}) ne 'ARRAY') {
34             $data->{input} = [$data->{input}];
35 97 100 100     396 }
36 61         149  
37             return \%args;
38             }
39 97         1653  
40             my $self = shift;
41             my $env = shift;
42              
43 71     71 1 113 my $data = $self->{data};
44 71         135  
45             my $stacked = $env->{stacked};
46 71         124 my $classes = $self->classes;
47              
48 71         103 my $div_span = "div";
49 71         173 my $label_column = $classes->{label_column};
50             my $input_column = $classes->{input_column};
51 71         99 $input_column = $data->{override_input_class} if ($data->{override_input_class});
52 71         113  
53 71         104 if ($stacked == 0) {
54 71 50       152 $div_span = "span";
55             $label_column = "";
56 71 50       140 $input_column = "";
57 0         0 }
58 0         0 my $input_fields_html = '';
59 0         0  
60             my $stacked_attr = {};
61 71         98  
62             if ($stacked == 1) {
63 71         110 my $class = $data->{'class'} ? " $data->{class}" : '';
64              
65 71 50       137 if ($data->{'type'} and $data->{'type'} eq 'hidden') {
66 71 50       139 $stacked_attr->{class} = $class;
67             } else {
68 71 50 33     185 $stacked_attr->{class} = "$classes->{row_padding} $classes->{row} clear$class";
69 0         0 }
70             }
71 71         211  
72             #create the field label
73             if (defined $data->{'label'}) {
74             my $label_text = $data->{'label'}->{'text'} || '';
75             undef $data->{'label'}->{'text'};
76 71 100       171 my $required_mark = delete $data->{label}{required_mark} || 0;
77 56   100     136 my $label_html = $self->_build_element_and_attributes('label', $data->{'label'}, $label_text, {required_mark => $required_mark},);
78 56         87  
79 56   100     199 # add a tooltip explanation if given
80 56         194 if ($data->{'label'}{'tooltip'}) {
81              
82             # img_url is the url of question mark picture
83 56 100       144 my $tooltip = _tooltip($data->{'label'}{'tooltip'}{'desc'}, $data->{'label'}{tooltip}{img_url});
84              
85             $input_fields_html .= qq{<div class="$classes->{extra_tooltip_container}">$label_html$tooltip</div>};
86 1         5 } else {
87             my $hide_mobile = $label_text ? "" : $classes->{hide_mobile};
88 1         14  
89             $input_fields_html .= qq{<$div_span class="$label_column $hide_mobile form_label">$label_html</$div_span>};
90 55 100       102 }
91             }
92 55         180  
93             # create the input field
94             if (defined $data->{'input'}) {
95              
96             #if there are more than 1 input field in a single row then we generate 1 by 1
97 71 100       172 my $inputs = $data->{input};
98             $input_fields_html .= qq{<$div_span class="$input_column">};
99             foreach my $input (@{$inputs}) {
100 61         103 $input_fields_html .= $self->_build_input($input, $env);
101 61         150 }
102 61         86 }
  61         115  
103 76         157  
104             if (defined $data->{'comment'}) {
105             $data->{'comment'}->{'class'} ||= '';
106              
107 71 100       167 unless ($data->{comment}->{no_new_line}) {
108 1   50     15 $input_fields_html .= '<br>';
109             }
110 1 50       4 $input_fields_html .= $self->_build_element_and_attributes('p', $data->{'comment'}, $data->{'comment'}->{'text'});
111 1         3 }
112              
113 1         6 if (defined $data->{'error'}) {
114              
115             my @errors =
116 71 100       160 ref($data->{'error'}) eq 'ARRAY'
117             ? @{$data->{error}}
118             : $data->{error};
119              
120 1         4 foreach my $error_box (@errors) {
121 48 100       158 $input_fields_html .= $self->_build_element_and_attributes('p', $error_box, $error_box->{text});
122             }
123 48         92  
124 48         136 }
125              
126             #close the input tag
127             if (defined $data->{'input'}) {
128             $input_fields_html .= '</' . $div_span . '>';
129             }
130 71 100       158  
131 61         122 if ($stacked == 1) {
132             $input_fields_html = $self->_build_element_and_attributes('div', $stacked_attr, $input_fields_html);
133             }
134 71 50       136  
135 71         166 return $input_fields_html;
136             }
137              
138 71         359 #####################################################################
139             # Usage : build the input field its own attributes
140             # Purpose : perform checking build the input field according to its own
141             # characteristics
142             # Returns : input field with its attributes in string
143             # Parameters : $input_field in HASH ref for example
144             # $attributes = {'id' => 'test', 'name' => 'test', 'class' => 'myclass'}
145             # Comments : check pod below to understand how to create different input fields
146             # See Also :
147             #####################################################################
148             my $self = shift;
149             my $input_field = shift;
150              
151             my $html = '';
152 76     76   128  
153 76         107 # delete this so that it doesn't carry on to the next field
154             # I don't know why should delete it(undef it)
155 76         104 my $heading = delete $input_field->{'heading'};
156             my $trailing = delete $input_field->{'trailing'};
157              
158             # wrap <input>, heading & trailing <span> in <div>
159 76         162 my ($wrap_input, $wrap_heading, $wrap_trailing);
160 76         106 if ($input_field->{wrap_in_div_class}) {
161             ($wrap_input, $wrap_heading, $wrap_trailing) = @{$input_field->{wrap_in_div_class}}{'input', 'heading', 'trailing'};
162             }
163 76         136  
164 76 50       154 #create the filed input
165 0         0 if (eval { $input_field->can('widget_html') }) {
  0         0  
166             $html = $input_field->widget_html;
167             } elsif ($input_field->{'type'} and $input_field->{'type'} eq 'textarea') {
168             undef $input_field->{'type'};
169 76 100 100     115 my $textarea_value = $input_field->{'value'} || '';
  76 100       597  
    100          
170 15         40 undef $input_field->{'value'};
171             $html = $self->_build_element_and_attributes('textarea', $input_field, $textarea_value);
172 7         15 } elsif ($input_field->{'type'}) {
173 7   50     20 my $type = $input_field->{'type'};
174 7         12 if ($type =~ /^(?:text|password)$/i) {
175 7         18 $input_field->{'class'} .= ' text';
176             } elsif ($type =~ /button|submit/) {
177 53         102 $input_field->{'class'} .= ' button';
178 53 100       235 }
    100          
179 28         76  
180             my $tag = ($type =~ /button|submit/ ? 'button' : 'input');
181 1         4  
182             $html = $self->_build_element_and_attributes($tag, $input_field);
183              
184 53 100       145 if ($type =~ /button|submit/) {
185             $html = qq{<span class="$input_field->{class}">$html</span>};
186 53         141 }
187             }
188 53 100       178 if ($wrap_input) {
189 1         5 $html = qq{<div class="$wrap_input">$html</div>};
190             }
191              
192 76 50       164 if ($heading) {
193 0         0 my $heading_html = qq{<span id="inputheading">$heading</span>};
194             if ($wrap_heading) {
195             $heading_html = qq{<div class="$wrap_heading">$heading_html</div>};
196 76 100       182 }
197 3         7  
198 3 50       7 if ($input_field->{'type'}
199 0         0 && ($input_field->{'type'} =~ /radio|checkbox/i))
200             {
201             $html .= qq{$heading_html<br />};
202 3 100 66     17 } else {
203             $html = $heading_html . $html;
204             }
205 1         4 }
206              
207 2         7 if ($trailing) {
208             my $trailing_html = qq{<span class="$self->{classes}{inputtrailing}">$trailing</span>};
209             if ($wrap_trailing) {
210             $trailing_html = qq{<div class="$wrap_trailing">$trailing_html</div>};
211 76 100       143 }
212 1         5 $html .= $trailing_html;
213 1 50       4 }
214 0         0  
215             return $html;
216 1         3 }
217              
218             #####################################################################
219 76         269 # Usage : _tooltip($content, $url)
220             # Purpose : create tooltip html code
221             # Returns : HTML
222             # Comments :
223             # See Also :
224             #####################################################################
225             my $content = shift;
226             my $url = shift;
227             $content =~ s/\'/&apos;/g; # Escape for quoting below
228              
229             return qq{ <a href='#' title='$content' rel='tooltip'><img src="$url" /></a>};
230 1     1   3 }
231 1         2  
232 1         2 1;
233              
234 1         4 =head1 NAME
235              
236             HTML::FormBuilder::Field - Field container used by HTML::FormBuilder
237              
238             =head1 SYNOPSIS
239              
240             my $form = HTML::FormBuilder->new(data => {id => 'testform});
241              
242             my $fieldset = $form->add_fieldset({id => 'fieldset1'});
243              
244             $fieldset->add_field({input => {type => 'text', value => 'Join'}});
245              
246             # Text only, without input fields
247             $fieldset->add_field({
248             comment => {
249             text => 'Please check on checkbox below:',
250             class => 'grd-grid-12',
251             # no extra <br/> before <span> for comment
252             no_new_line => 1,
253             },
254             });
255              
256             # checkbox & explanation text
257             $fieldset->add_field({
258             input => {
259             id => 'tnc',
260             name => 'tnc',
261             type => 'checkbox',
262             trailing => 'I have read & agree to all terms & condition.',
263             # wrap <input> & trailing <span> respectively in <div>, with class:
264             wrap_in_div_class => {
265             input => 'grd-grid-1',
266             trailing => 'grd-grid-11'
267             },
268             },
269             error => {
270             id => 'error_tnc',
271             class => 'errorfield',
272             },
273             validation => [{
274             type => 'checkbox_checked',
275             err_msg => localize('Please agree in order to proceed.'),
276             }],
277             # override div container class for input
278             override_input_class => 'grd-grid-12',
279             });
280              
281             $form->add_field($fieldset_index, {input => {type => 'text', value => 'Join'}});
282              
283             =head1 METHODS
284              
285             =head2 BUILDARGS
286              
287             =head2 build
288              
289             =head2 data
290              
291             =head1 AUTHOR
292              
293             Chylli L<mailto:chylli@binary.com>
294              
295             =head1 CONTRIBUTOR
296              
297             Fayland Lam L<mailto:fayland@binary.com>
298              
299             Tee Shuwn Yuan L<mailto:shuwnyuan@binary.com>
300              
301             =head1 COPYRIGHT AND LICENSE
302              
303             =cut
304