booties 0.0.1 → 0.0.2

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 2d085cd32d4093b862593138682a49d28d80ba19
4
- data.tar.gz: ad4d954a6ae014dbe64ec24b0f9a4ac661781892
3
+ metadata.gz: 0fcf7ffcb28fe2eda72a78078eacab0f5d4e46bc
4
+ data.tar.gz: 01b4199a6284230d495d67d12813c28ef228300e
5
5
  SHA512:
6
- metadata.gz: b781658c589e18073d3339a3037f96f368f730a2236adc5138252f46dc50581a6ed38178d91cfe98cc44dba55b27fa90396f1eeee90484a571c80df0e90fdf6d
7
- data.tar.gz: 7c055485c578ee6ba5151a0fcad0854858789d9be12faaaf1da8d000525648464960edd185ce7bae39fc8289119bc2870d6abac206d0f276e904c056a2a20ce3
6
+ metadata.gz: 7d47f0151b0b117e0ef04c46b7d9456ef0f841210aba716979655e41085fc5a8886acb2259d5bae5e09e7a15c272edf2b032d278d53a174fac5512e3211431fe
7
+ data.tar.gz: 2565c2da18b7282c646a07ed2fec8fdbaea91aa8d9bae5993cb5738c787d23a395e65ab7a2c25d433b3d0c9a57a71a199dd29d755f6ebd75741d087cd5347d7b
@@ -2,7 +2,7 @@ module Booties
2
2
  module ApplicationHelper
3
3
  ##
4
4
  # Renders an ol tag with the "breadcrumb" class and fills it with the
5
- # content of the block.
5
+ # content of the block. The following:
6
6
  #
7
7
  # <% breadcrumbs do %>
8
8
  # <li>Foo</li>
@@ -10,7 +10,7 @@ module Booties
10
10
  # <% end %>
11
11
  # <%= yield :breadcrumb %>
12
12
  #
13
- # Produces:
13
+ # will produce:
14
14
  #
15
15
  # <ol class="breadcrumb">
16
16
  # <li>Foo</li>
@@ -27,54 +27,151 @@ module Booties
27
27
  ##
28
28
  # Renders a span tag with the "label" class and a contextual label class
29
29
  # derived from +context+. Defaults to "label-default". The content of the
30
- # tag is passed in as +content+.
30
+ # tag is passed in as +content+. The following:
31
31
  #
32
32
  # <%= flag 'foo' %>
33
33
  #
34
- # Produces:
34
+ # will produce:
35
35
  #
36
36
  # <span class="label label-default">foo</span>
37
37
  #
38
- # Alternatively, you can pass the content in as a block.
38
+ # Alternatively, you can pass the content in as a block. The following:
39
39
  #
40
40
  # <%= flag do %>
41
41
  # foo
42
42
  # <% end %>
43
43
  #
44
- # Produces:
44
+ # will produce:
45
45
  #
46
46
  # <span class="label label-default">foo</span>
47
47
  #
48
- # You can provide a different context for the label as +context+.
48
+ # You can provide a different context for the label as +context+. The
49
+ # following:
49
50
  #
50
51
  # <%= flag 'foo', context: :primary %>
51
52
  #
52
- # Produces:
53
+ # will produce:
53
54
  #
54
55
  # <span class="label label-primary">foo</span>
55
56
  #
56
57
  # Additional options passed in through +options+ will be passed to
57
- # #content_tag to added as attributes to the span tag.
58
+ # #content_tag to be added as attributes to the span tag. The following:
58
59
  #
59
60
  # <%= flag 'foo', id: 'bar' %>
60
61
  #
61
- # Produces:
62
+ # will produce:
62
63
  #
63
64
  # <span class="label label-default" id="bar">foo</span>
64
65
  #
65
- # If +options+ includes a :class key, the contents will be merged with the
66
- # classes required by Bootstrap labels.
66
+ # If the +:class+ option is passed in the contents will be merged with the
67
+ # classes required by Bootstrap labels. The following:
67
68
  #
68
69
  # <%= flag 'foo', class: 'bar' %>
69
70
  #
70
- # Produces:
71
+ # will produce:
71
72
  #
72
73
  # <span class="label label-default bar">foo</span>
73
74
  def flag(content = nil, context: :default, **options, &block)
74
- content ||= capture(&block)
75
- classes = %W[label label-#{context}]
76
- classes |= Array(options.delete(:class)).flat_map(&:split)
75
+ content ||= capture &block
76
+ classes = Booties.merge_classes %W[label label-#{context}],
77
+ options.delete(:class)
77
78
  content_tag :span, content, class: classes, **options
78
79
  end
80
+
81
+ ##
82
+ # Renders a span tag with the "badge" class. The content of the tag is
83
+ # passed in as +content+. The following:
84
+ #
85
+ # <%= badge 'foo' %>
86
+ #
87
+ # will produce:
88
+ #
89
+ # <span class="badge">foo</span>
90
+ #
91
+ # Alternatively, you can pass the content in as a block. The following:
92
+ #
93
+ # <%= badge do %> foo <% end %>
94
+ #
95
+ # will produce:
96
+ #
97
+ # <span class="badge">foo</span>
98
+ #
99
+ # Any options passed in through +options+ will be passed to #content_tag to
100
+ # be added as attributes to the span tag. The following:
101
+ #
102
+ # <%= badge 'foo', id: 'bar' %>
103
+ #
104
+ # will produce:
105
+ #
106
+ # <span class="badge" id="bar">foo</span>
107
+ #
108
+ # If the +:class+ option is passed in, the contents will be merged with the
109
+ # classes required by Bootstrap badges. The following:
110
+ #
111
+ # <%= badge 'foo', class: 'bar' %>
112
+ #
113
+ # will produce:
114
+ #
115
+ # <span class="badge bar">foo</span>
116
+ def badge(content = nil, **options, &block)
117
+ content ||= capture &block
118
+ classes = Booties.merge_classes ['badge'], options.delete(:class)
119
+ content_tag :span, content, class: classes, **options
120
+ end
121
+
122
+ ##
123
+ # Renders a modal dialog. The CSS id is provided by +id+. The fade effect
124
+ # will be enabled by default, but it will be disabled if +fade+ is falsey.
125
+ #
126
+ # The contents of the dialog are passed in as a block. An instance of
127
+ # Booties::Modal will be yielded as a parameter to the block (similar to
128
+ # the way a FormBuilder works in Rails).
129
+ #
130
+ # <%= modal 'foo' do |m| %>
131
+ # <%= m.header do %>
132
+ # Nesciunt qui iste vel a.
133
+ # <% end %>
134
+ # <%= m.body do %>
135
+ # <p>
136
+ # Autem atque perferendis veritatis. Molestiae aliquid nam
137
+ # reiciendis recusandae facere. Aut non nemo dicta.
138
+ # </p>
139
+ # <% end %>
140
+ # <%= m.footer do %>
141
+ # <%= m.dismiss class: 'btn btn-default' do %>
142
+ # Dismiss
143
+ # <% end %>
144
+ # <%= link_to @widget, class: 'btn btn-danger', method: :delete do %>
145
+ # Really Delete
146
+ # <% end %>
147
+ # <% end %>
148
+ # <% end %>
149
+ def modal(id, fade: true, with: Modal, &block)
150
+ with.new(self, id: id, fade: fade).render &block
151
+ end
152
+
153
+ ##
154
+ # Renders a panel. The defult panel context is +:default+ but can be
155
+ # specified through +options+. The contents of the panel should be passed
156
+ # in as a block. An instance of the Booties::Panel will be yielded as a
157
+ # parameter to the block (similar to the way a FormBuilder works in Rails).
158
+ #
159
+ # <%= panel do |p| %>
160
+ # <%= p.heading do %>
161
+ # <%= p.title 'Consequatur quibusdam quia vel et sed in.' %>
162
+ # <% end %>
163
+ # <%= p.body do %>
164
+ # <p>
165
+ # Est fuga iste reiciendis laudantium dicta. Perspiciatis vero ut
166
+ # autem quod vel modi. Ea error omnis aliquam aut est.
167
+ # </p>
168
+ # <% end %>
169
+ # <%= p.footer do %>
170
+ # Voluptatibus rerum et est quo dicta perspiciatis.
171
+ # <% end %>
172
+ # <% end %>
173
+ def panel(with: Panel, **options, &block)
174
+ with.new(self, **options).render &block
175
+ end
79
176
  end
80
177
  end
@@ -1,4 +1,9 @@
1
- require "booties/engine"
1
+ require 'booties/engine'
2
+ require 'booties/modal'
3
+ require 'booties/panel'
2
4
 
3
5
  module Booties
6
+ def self.merge_classes(a, b)
7
+ Array(a).flat_map(&:split) | Array(b).flat_map(&:split)
8
+ end
4
9
  end
@@ -0,0 +1,104 @@
1
+ require 'forwardable'
2
+
3
+ module Booties
4
+ class Modal
5
+ extend Forwardable
6
+
7
+ ##
8
+ # Instantiates a new Modal. Several helper methods like #content_tag will
9
+ # be delegated to +view_context+. The required keyword +id+ will be used as
10
+ # the DOM ID of the modal element. By default, the modal will exhibit
11
+ # fading behavior, but this can be disabled by setting +fade+ to a falsey
12
+ # value.
13
+ #
14
+ # TODO: Pass additional arguments as attributes to top-level div.
15
+ def initialize(view_context, id:, fade: true)
16
+ @view_context = view_context
17
+ @id = id
18
+ @fade = fade ? 'fade' : nil
19
+ end
20
+
21
+ def_delegators :@view_context, :button_tag, :capture, :content_tag,
22
+ :raw, :t, :translate
23
+
24
+ ##
25
+ # Renders the top-level div for the modal dialog. +block+ is passed to
26
+ # #dialog to fill in the content of the modal. +@id+ is used as the DOM ID
27
+ # of the modal. +@fade+ is used to include or exclude fading behavior.
28
+ def render(&block)
29
+ content_tag :div, class: ['modal', @fade], id: @id do
30
+ dialog &block
31
+ end
32
+ end
33
+
34
+ ##
35
+ # Renders the dialog section of the modal. +block+ is passed to #content to
36
+ # fill in the content of the dialog.
37
+ def dialog(&block)
38
+ content_tag :div, class: 'modal-dialog' do
39
+ content &block
40
+ end
41
+ end
42
+
43
+ ##
44
+ # Renders the content section of the modal. The content of the dialog is
45
+ # captured from +block+. The Modal object will be passed as a paramter to
46
+ # +block+.
47
+ def content(&block)
48
+ content_tag :div, class: 'modal-content' do
49
+ capture self, &block
50
+ end
51
+ end
52
+
53
+ ##
54
+ # Renders the header section of a modal. +block+ is passed to #title to
55
+ # render the title. In addition to the title, a dismissal button will be
56
+ # rendered. The content of the dismissal button is localized. It will look
57
+ # for +booties.modal.dismiss_html+. If that translation does not exist it
58
+ # will look for +booties.modal.dismiss+. If that does not exist either, it
59
+ # will default to the HTML times entity +&times;+.
60
+ def header(&block)
61
+ dismissal = t :'booties.modal.dismiss_html',
62
+ default: [:'booties.modal.dismiss', raw('&times;')]
63
+ content_tag :div, class: 'modal-header' do
64
+ dismiss(dismissal) << title(&block)
65
+ end
66
+ end
67
+
68
+ ##
69
+ # Renders the main body of the modal. The content of the section is
70
+ # captured from +block+.
71
+ def body(&block)
72
+ content_tag :div, capture(&block), class: 'modal-body'
73
+ end
74
+
75
+ ##
76
+ # Renders the footer of the modal. The content of the section is captured
77
+ # from +block+.
78
+ def footer(&block)
79
+ content_tag :div, capture(&block), class: 'modal-footer'
80
+ end
81
+
82
+ ##
83
+ # Renders the title of the modal. The content can be passed through the
84
+ # +content+ parameter. Otherwise it will be captured from +block+.
85
+ #
86
+ # #title is called automatically from #header.
87
+ def title(content = nil, &block)
88
+ content ||= capture &block
89
+ content_tag :h4, content, class: 'modal-title'
90
+ end
91
+
92
+ ##
93
+ # Renders a button for dismissing the modal. The label can be passed in
94
+ # through the +content+ parameter. Otherwise, it will be captured from
95
+ # +block+. Additional HTML attributes for the button can be passed in
96
+ # through +options+.
97
+ def dismiss(content = nil, **options, &block)
98
+ content ||= capture &block
99
+ options[:class] ||= 'close'
100
+ options.update data: { dismiss: 'modal' }, type: 'button'
101
+ button_tag content, options
102
+ end
103
+ end
104
+ end
@@ -0,0 +1,66 @@
1
+ require 'forwardable'
2
+
3
+ module Booties
4
+ class Panel
5
+ extend Forwardable
6
+
7
+ ##
8
+ # Instantiates a new Panel. Several helper methods like #content_tag will
9
+ # be delegated to +view_context+. The optional +context+ argument can be
10
+ # passed in to specify the panel context. +context+ defaults to +:default+.
11
+ # Any additional options will be included as attributes on the top-level
12
+ # panel div.
13
+ def initialize(view_context, context: :default, **options)
14
+ @view_context = view_context
15
+ @context = context
16
+ @options = options
17
+ end
18
+
19
+ def_delegators :@view_context, :capture, :content_tag
20
+
21
+ ##
22
+ # Renders the top-level div for the panel. +@context+ is used to specify
23
+ # the panel's context. The content is captured from +block+. The Panel
24
+ # object will be passed as a parameter to +block+.
25
+ def render(&block)
26
+ options = @options.dup
27
+ classes = Booties.merge_classes %W[panel panel-#@context],
28
+ options.delete(:class)
29
+ content_tag :div, class: classes, **options do
30
+ capture self, &block
31
+ end
32
+ end
33
+
34
+ ##
35
+ # Renders the panel heading. The content of the heading can be passed in
36
+ # through the +content+ parameter or as a block.
37
+ def heading(content = nil, &block)
38
+ content ||= capture &block
39
+ content_tag :div, content, class: 'panel-heading'
40
+ end
41
+
42
+ ##
43
+ # Renders the panel title. The content of the title can be passed in
44
+ # through the +content+ paramter or as a block.
45
+ def title(content = nil, &block)
46
+ content ||= capture &block
47
+ content_tag :h3, content, class: 'panel-title'
48
+ end
49
+
50
+ ##
51
+ # Renders the panel body. The content of the body can be passed in through
52
+ # the +content+ parameter or as a block.
53
+ def body(content = nil, &block)
54
+ content ||= capture &block
55
+ content_tag :div, content, class: 'panel-body'
56
+ end
57
+
58
+ ##
59
+ # Renders the panel footer. The content of the footer can be passed in
60
+ # through the +content+ parameter or as a block.
61
+ def footer(content = nil, &block)
62
+ content ||= capture &block
63
+ content_tag :div, content, class: 'panel-footer'
64
+ end
65
+ end
66
+ end
@@ -1,3 +1,3 @@
1
1
  module Booties
2
- VERSION = "0.0.1"
2
+ VERSION = "0.0.2"
3
3
  end
@@ -0,0 +1,11 @@
1
+ require 'test_helper'
2
+
3
+ class TestBooties < Minitest::Test
4
+ def test_merge_classes_removes_duplicates
5
+ assert_equal %w[a b c], Booties.merge_classes(%w[a b], %w[b c])
6
+ end
7
+
8
+ def test_merge_classes_converts_string_arguments_to_arrays
9
+ assert_equal %w[a b c], Booties.merge_classes('a b', 'b c')
10
+ end
11
+ end
@@ -0,0 +1,136 @@
1
+ require 'test_helper'
2
+ require 'stub_view'
3
+
4
+ module Booties
5
+ class ModalTest < Minitest::Test
6
+ def setup
7
+ @view_context = StubView.new
8
+ @backend = I18n.backend
9
+ I18n.backend = I18n::Backend::KeyValue.new({})
10
+ I18n.enforce_available_locales = false
11
+ end
12
+
13
+ def teardown
14
+ I18n.backend = @backend
15
+ end
16
+
17
+ def test_render_renders_a_fading_modal_with_an_id_and_a_dialog
18
+ modal = Modal.new @view_context, id: 'foo'
19
+ expected = '<div class="modal fade" id="foo"><div class="modal-dialog">' \
20
+ '<div class="modal-content"></div></div></div>'
21
+ assert_equal expected, modal.render {}
22
+ end
23
+
24
+ def test_render_renders_a_non_fading_modal_when_fade_is_falsey
25
+ modal = Modal.new @view_context, id: 'foo', fade: false
26
+ expected = '<div class="modal" id="foo"><div class="modal-dialog">' \
27
+ '<div class="modal-content"></div></div></div>'
28
+ assert_equal expected, modal.render {}
29
+ end
30
+
31
+ def test_dialog_renders_a_modal_dialog_with_a_modal_content
32
+ modal = Modal.new @view_context, id: 'foo'
33
+ expected = '<div class="modal-dialog"><div class="modal-content">' \
34
+ '</div></div>'
35
+ assert_equal expected, modal.dialog {}
36
+ end
37
+
38
+ def test_content_renders_a_modal_content
39
+ modal = Modal.new @view_context, id: 'foo'
40
+ expected = '<div class="modal-content"></div>'
41
+ assert_equal expected, modal.content {}
42
+ end
43
+
44
+ def test_content_yields_modal_to_block
45
+ modal = Modal.new @view_context, id: 'foo'
46
+ yielded_modal = nil
47
+ modal.content do |m|
48
+ yielded_modal = m
49
+ end
50
+ assert_same modal, yielded_modal
51
+ end
52
+
53
+ def test_header_renders_a_modal_header_with_a_title_and_a_dismissal
54
+ modal = Modal.new @view_context, id: 'foo'
55
+ expected = '<div class="modal-header">' \
56
+ '<button class="close" data-dismiss="modal" type="button">&times;' \
57
+ '</button><h4 class="modal-title">content</h4></div>'
58
+ assert_equal expected, modal.header { 'content' }
59
+ end
60
+
61
+ def test_header_translates_html_dismissals
62
+ icon = '<span class="glyphicon glyphicon-remove" aria-label="Dismiss"></span>'
63
+ translations = {}
64
+ I18n.backend = I18n::Backend::KeyValue.new translations
65
+ I18n.backend.store_translations I18n.locale,
66
+ { booties: { modal: { dismiss_html: icon } } }
67
+
68
+ modal = Modal.new @view_context, id: 'foo'
69
+ expected = %r{<button class="close" data-dismiss="modal" type="button">#{icon}</button>}
70
+ assert_match expected, modal.header { 'content' }
71
+ end
72
+
73
+ def test_header_translates_non_html_dismissals
74
+ translations = {}
75
+ I18n.backend = I18n::Backend::KeyValue.new translations
76
+ I18n.backend.store_translations I18n.locale,
77
+ { booties: { modal: { dismiss: 'close' } } }
78
+
79
+ modal = Modal.new @view_context, id: 'foo'
80
+ expected = %r{<button class="close" data-dismiss="modal" type="button">close</button>}
81
+ assert_match expected, modal.header { 'content' }
82
+ end
83
+
84
+ def test_body_renders_a_modal_body
85
+ modal = Modal.new @view_context, id: 'foo'
86
+ expected = '<div class="modal-body">content</div>'
87
+ assert_equal expected, modal.body { 'content' }
88
+ end
89
+
90
+ def test_footer_renders_a_modal_footer
91
+ modal = Modal.new @view_context, id: 'foo'
92
+ expected = '<div class="modal-footer">content</div>'
93
+ assert_equal expected, modal.footer { 'content' }
94
+ end
95
+
96
+ def test_title_renders_a_modal_title
97
+ modal = Modal.new @view_context, id: 'foo'
98
+ expected = '<h4 class="modal-title">content</h4>'
99
+ assert_equal expected, modal.title('content')
100
+ end
101
+
102
+ def test_title_accepts_content_as_a_block
103
+ modal = Modal.new @view_context, id: 'foo'
104
+ expected = '<h4 class="modal-title">content</h4>'
105
+ assert_equal expected, modal.title { 'content' }
106
+ end
107
+
108
+ def test_dismiss_renders_a_dismiss_button
109
+ modal = Modal.new @view_context, id: 'foo'
110
+ expected = '<button class="close" data-dismiss="modal" type="button">' \
111
+ 'content</button>'
112
+ assert_equal expected, modal.dismiss('content')
113
+ end
114
+
115
+ def test_dismiss_accepts_content_as_a_block
116
+ modal = Modal.new @view_context, id: 'foo'
117
+ expected = '<button class="close" data-dismiss="modal" type="button">' \
118
+ 'content</button>'
119
+ assert_equal expected, modal.dismiss { 'content' }
120
+ end
121
+
122
+ def test_dismiss_accepts_custom_class
123
+ modal = Modal.new @view_context, id: 'foo'
124
+ expected = '<button class="btn btn-default" data-dismiss="modal"' \
125
+ ' type="button">content</button>'
126
+ assert_equal expected, modal.dismiss('content', class: 'btn btn-default')
127
+ end
128
+
129
+ def test_dismiss_accepts_custom_class_and_block
130
+ modal = Modal.new @view_context, id: 'foo'
131
+ expected = '<button class="btn btn-default" data-dismiss="modal"' \
132
+ ' type="button">content</button>'
133
+ assert_equal expected, modal.dismiss(class: 'btn btn-default') { 'content' }
134
+ end
135
+ end
136
+ end