booties 0.0.1 → 0.0.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
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