flipper-ui 1.2.2 → 1.3.0

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.
Files changed (33) hide show
  1. checksums.yaml +4 -4
  2. data/examples/ui/basic.ru +3 -0
  3. data/flipper-ui.gemspec +2 -1
  4. data/lib/flipper/ui/action.rb +20 -44
  5. data/lib/flipper/ui/actions/actors_gate.rb +0 -6
  6. data/lib/flipper/ui/actions/add_feature.rb +0 -9
  7. data/lib/flipper/ui/actions/feature.rb +0 -7
  8. data/lib/flipper/ui/actions/features.rb +0 -7
  9. data/lib/flipper/ui/actions/groups_gate.rb +0 -5
  10. data/lib/flipper/ui/actions/settings.rb +0 -3
  11. data/lib/flipper/ui/configuration.rb +16 -2
  12. data/lib/flipper/ui/public/css/bootstrap.min.css +6 -0
  13. data/lib/flipper/ui/public/js/application.js +17 -33
  14. data/lib/flipper/ui/public/js/bootstrap.min.js +7 -0
  15. data/lib/flipper/ui/public/js/popper.min.js +6 -0
  16. data/lib/flipper/ui/sources.json +5 -0
  17. data/lib/flipper/ui/views/add_actor.erb +8 -4
  18. data/lib/flipper/ui/views/add_feature.erb +3 -3
  19. data/lib/flipper/ui/views/add_group.erb +14 -9
  20. data/lib/flipper/ui/views/feature.erb +81 -66
  21. data/lib/flipper/ui/views/features.erb +29 -29
  22. data/lib/flipper/ui/views/layout.erb +27 -19
  23. data/lib/flipper/ui/views/settings.erb +4 -4
  24. data/lib/flipper/version.rb +1 -1
  25. data/spec/flipper/ui/actions/actors_gate_spec.rb +2 -2
  26. data/spec/flipper/ui/actions/add_feature_spec.rb +1 -1
  27. data/spec/flipper/ui/actions/groups_gate_spec.rb +1 -1
  28. data/spec/flipper/ui/configuration_spec.rb +9 -4
  29. data/spec/flipper/ui_spec.rb +19 -31
  30. metadata +33 -12
  31. data/lib/flipper/ui/public/css/bootstrap-4.6.0.min.css +0 -7
  32. data/lib/flipper/ui/public/js/bootstrap-4.6.0.min.js +0 -7
  33. data/lib/flipper/ui/public/js/popper-1.12.9.min.js +0 -5
@@ -21,15 +21,20 @@
21
21
  <div class="card">
22
22
  <%# Gate State Header %>
23
23
  <div class="card-header">
24
- <span class="status <%= @feature.color_class %> mr-2"></span>
25
- <%= @feature.gate_state_title %>
24
+ <div class="row align-items-center">
25
+ <h4 class="col text-truncate mb-0"><%= feature_name %></h4>
26
+ <div class="col-auto">
27
+ <span class="status <%= @feature.color_class %> me-2"></span>
28
+ <%= @feature.gate_state_title %>
29
+ </div>
30
+ </div>
26
31
  </div>
27
32
 
28
33
  <% unless @feature.boolean_value %>
29
34
  <%# Actors Info and Form %>
30
35
  <div class="card-body border-bottom">
31
36
  <div class="row align-items-center js-toggle-container">
32
- <div class="col col-mr-auto toggle-block-when-off">
37
+ <div class="col col-me-auto toggle-block-when-off">
33
38
  <h6 class="m-0">
34
39
  <strong class="<%= "text-muted" if @feature.actors_value.empty? %>">
35
40
  <% if @feature.actors_value.count > 0 %>
@@ -42,14 +47,18 @@
42
47
  </div>
43
48
  <% if write_allowed? %>
44
49
  <div class="col col-auto toggle-block-when-off">
45
- <button class="btn btn-outline-secondary js-toggle-trigger" data-toggle="collapse" data-target="#add-actor">Add an actor</button>
50
+ <button class="btn btn-outline-secondary js-toggle-trigger" data-bs-toggle="collapse" data-bs-target="#add-actor">Add an actor</button>
46
51
  </div>
47
52
  <div class="col toggle-block-when-on">
48
- <form action="<%= script_name %>/features/<%= @feature.key %>/actors" method="post" class="form-inline">
53
+ <form action="<%= script_name %>/features/<%= @feature.key %>/actors" method="post" class="row">
49
54
  <%== csrf_input_tag %>
50
55
  <input type="hidden" name="operation" value="enable">
51
- <input type="text" name="value" placeholder="<%= Flipper::UI.configuration.add_actor_placeholder %>" class="form-control form-control-sm mr-sm-2 mb-2 mb-sm-0">
52
- <input type="submit" value="Add Actor" class="btn btn-sm btn-light">
56
+ <div class="col">
57
+ <input type="text" name="value" placeholder="<%= Flipper::UI.configuration.add_actor_placeholder %>" class="form-control">
58
+ </div>
59
+ <div class="col-auto">
60
+ <input type="submit" value="Add Actor" class="btn btn-primary">
61
+ </div>
53
62
  </form>
54
63
  </div>
55
64
  <div class="col col-auto toggle-block-when-on">
@@ -63,7 +72,7 @@
63
72
  <% @feature.actors_value.each do |item| %>
64
73
  <div class="card-body bg-lightest border-bottom py-3">
65
74
  <div class="row align-items-center">
66
- <div class="col col-mr-auto pl-md-5">
75
+ <div class="col col-me-auto ps-md-5">
67
76
  <h6 class="m-0">
68
77
  <% if Flipper::UI::Util.present?(@feature.actor_names[item]) %>
69
78
  <%== Sanitize.fragment("#{@feature.actor_names[item]} (#{item})", Sanitize::Config::BASIC) %>
@@ -78,7 +87,7 @@
78
87
  <%== csrf_input_tag %>
79
88
  <input type="hidden" name="operation" value="disable">
80
89
  <input type="hidden" name="value" value="<%= item %>">
81
- <button type="submit" value="Disable" class="btn btn-outline-danger" data-toggle="tooltip" title="Disable <%= item %>" data-placement="left">
90
+ <button type="submit" value="Disable" class="btn btn-outline-danger" data-bs-toggle="tooltip" title="Disable <%= item %>" data-bs-placement="left">
82
91
  Remove
83
92
  </button>
84
93
  </form>
@@ -91,7 +100,7 @@
91
100
  <%# Groups Info and Form %>
92
101
  <div class="card-body border-bottom">
93
102
  <div class="row align-items-center js-toggle-container">
94
- <div class="col col-mr-auto toggle-block-when-off">
103
+ <div class="col col-me-auto toggle-block-when-off">
95
104
  <h6 class="m-0">
96
105
  <strong class="<%= "text-muted" if @feature.groups_value.empty? %>">
97
106
  <% if @feature.groups_value.count > 0 %>
@@ -104,22 +113,24 @@
104
113
  </div>
105
114
  <% if write_allowed? %>
106
115
  <div class="col col-auto toggle-block-when-off">
107
- <button class="btn btn-outline-secondary js-toggle-trigger" data-toggle="collapse" data-target="#add-actor">Add a group</button>
116
+ <button class="btn btn-outline-secondary js-toggle-trigger" data-bs-toggle="collapse" data-bs-target="#add-actor">Add a group</button>
108
117
  </div>
109
118
  <div class="col collapse toggle-block-when-on">
110
119
  <% if @feature.disabled_groups.empty? %>
111
120
  All groups enabled.
112
121
  <% else %>
113
- <form action="<%= script_name %>/features/<%= @feature.key %>/groups" method="post" class="form-inline">
122
+ <form action="<%= script_name %>/features/<%= @feature.key %>/groups" method="post" class="row">
114
123
  <%== csrf_input_tag %>
115
124
  <input type="hidden" name="operation" value="enable">
116
- <select name="value" class="form-control form-control-sm mr-sm-2 mb-2 mb-sm-0">
117
- <option value="">Select a group...</option>
118
- <% @feature.disabled_groups.each do |group| %>
119
- <option value="<%= group.name %>"><%= group.name %></option>
120
- <% end %>
121
- </select>
122
- <input type="submit" value="Add Group" class="btn btn-sm btn-light btn">
125
+ <div class="col">
126
+ <select name="value" class="form-select">
127
+ <option value="">Select a group...</option>
128
+ <% @feature.disabled_groups.each do |group| %>
129
+ <option value="<%= group.name %>"><%= group.name %></option>
130
+ <% end %>
131
+ </select>
132
+ </div>
133
+ <div class="col-auto"><input type="submit" value="Add Group" class="btn btn-primary btn"></div>
123
134
  </form>
124
135
  <% end %>
125
136
  </div>
@@ -134,7 +145,7 @@
134
145
  <% @feature.groups_value.each do |item| %>
135
146
  <div class="card-body bg-lightest border-bottom py-3">
136
147
  <div class="row align-items-center">
137
- <div class="col col-mr-auto pl-md-5">
148
+ <div class="col col-me-auto ps-md-5">
138
149
  <h6 class="m-0"><%= item %></h6>
139
150
  </div>
140
151
  <div class="col col-auto">
@@ -143,7 +154,7 @@
143
154
  <%== csrf_input_tag %>
144
155
  <input type="hidden" name="operation" value="disable">
145
156
  <input type="hidden" name="value" value="<%= item %>">
146
- <button type="submit" value="Disable" class="btn btn-outline-danger" data-toggle="tooltip" title="Disable <%= item %>" data-placement="left">
157
+ <button type="submit" value="Disable" class="btn btn-outline-danger" data-bs-toggle="tooltip" title="Disable <%= item %>" data-bs-placement="left">
147
158
  Remove
148
159
  </button>
149
160
  </form>
@@ -157,7 +168,7 @@
157
168
  <div class="js-toggle-container">
158
169
  <div class="card-body border-bottom">
159
170
  <div class="row align-items-center">
160
- <div class="col col-mr-auto">
171
+ <div class="col col-me-auto">
161
172
  <h6 class="m-0"><strong class="<%= "text-muted" unless @feature.percentage_of_actors_value > 0 %>">Enabled for <%= @feature.percentage_of_actors_value %>% of actors</strong></h6>
162
173
  </div>
163
174
  <% if write_allowed? %>
@@ -173,24 +184,22 @@
173
184
 
174
185
  <% if write_allowed? %>
175
186
  <div class="card-body border-bottom toggle-block-when-on bg-lightest">
176
- <div class="row">
177
- <div class="col-12 col-md-6 mb-3 mb-md-0">
178
- <form action="<%= script_name %>/features/<%= @feature.key %>/percentage_of_actors" method="post">
179
- <%== csrf_input_tag %>
180
- <div class="btn-group">
181
- <% @percentages.each do |number| %>
182
- <input type="submit" name="value" value="<%= number %>%" class="btn btn-light btn-sm" <% if number == @feature.percentage_of_actors_value %>disabled<% end %>>
183
- <% end %>
184
- </div>
185
- </form>
186
- </div>
187
- <div class="col-12 col-md-6">
188
- <form action="<%= script_name %>/features/<%= @feature.key %>/percentage_of_actors" method="post" class="form-inline">
189
- <%== csrf_input_tag %>
190
- <input type="text" name="value" <% if @feature.percentage_of_actors_value > 0 %>value="<%= @feature.percentage_of_actors_value %>"<% end %> placeholder="custom (26, 32, etc.)" class="form-control form-control-sm mr-sm-2 mb-2 mb-md-0">
191
- <input type="submit" name="action" value="Save" class="btn btn-light btn-sm">
192
- </form>
193
- </div>
187
+ <div class="row align-items-center">
188
+ <form action="<%= script_name %>/features/<%= @feature.key %>/percentage_of_actors" method="post" class="col">
189
+ <%== csrf_input_tag %>
190
+ <div class="btn-group">
191
+ <% @percentages.each do |number| %>
192
+ <input type="submit" name="value" value="<%= number %>%" class="btn btn-light" <% if number == @feature.percentage_of_actors_value %>disabled<% end %>>
193
+ <% end %>
194
+ </div>
195
+ </form>
196
+ <form action="<%= script_name %>/features/<%= @feature.key %>/percentage_of_actors" method="post" class="col-auto row">
197
+ <%== csrf_input_tag %>
198
+ <div class="col-auto">
199
+ <input style="width:5em;" type="number" max="100" min="0" name="value" <% if @feature.percentage_of_actors_value > 0 %>value="<%= @feature.percentage_of_actors_value %>"<% end %> title="Custom percentage (26, 32, etc.)" placeholder="0" class="form-control">
200
+ </div>
201
+ <div class="col-auto ms-auto"><input type="submit" name="action" value="Save" class="btn btn-primary"></div>
202
+ </form>
194
203
  </div>
195
204
  </div>
196
205
  <% end %>
@@ -200,7 +209,7 @@
200
209
  <div class="js-toggle-container">
201
210
  <div class="card-body border-bottom">
202
211
  <div class="row align-items-center">
203
- <div class="col col-mr-auto">
212
+ <div class="col col-me-auto">
204
213
  <h6 class="m-0"><strong class="<%= "text-muted" unless @feature.percentage_of_time_value > 0 %>">Enabled for <%= @feature.percentage_of_time_value %>% of time</strong></h6>
205
214
  </div>
206
215
  <% if write_allowed? %>
@@ -216,24 +225,22 @@
216
225
 
217
226
  <% if write_allowed? %>
218
227
  <div class="card-body border-bottom toggle-block-when-on bg-lightest">
219
- <div class="row">
220
- <div class="col-12 col-md-6 mb-3 mb-md-0">
221
- <form action="<%= script_name %>/features/<%= @feature.key %>/percentage_of_time" method="post">
222
- <%== csrf_input_tag %>
223
- <div class="btn-group">
224
- <% @percentages.each do |number| %>
225
- <input type="submit" name="value" value="<%= number %>%" class="btn btn-light btn-sm" <% if number == @feature.percentage_of_time_value %>disabled<% end %>>
226
- <% end %>
227
- </div>
228
- </form>
229
- </div>
230
- <div class="col-12 col-md-6">
231
- <form action="<%= script_name %>/features/<%= @feature.key %>/percentage_of_time" method="post" class="form-inline">
232
- <%== csrf_input_tag %>
233
- <input type="text" name="value" <% if @feature.percentage_of_time_value > 0 %>value="<%= @feature.percentage_of_time_value %>"<% end %> placeholder="custom (26, 32, etc.)" class="form-control form-control-sm mr-sm-2 mb-2 mb-md-0">
234
- <input type="submit" name="action" value="Save" class="btn btn-light btn-sm">
235
- </form>
236
- </div>
228
+ <div class="row align-items-center">
229
+ <form action="<%= script_name %>/features/<%= @feature.key %>/percentage_of_time" method="post" class="col">
230
+ <%== csrf_input_tag %>
231
+ <div class="btn-group">
232
+ <% @percentages.each do |number| %>
233
+ <input type="submit" name="value" value="<%= number %>%" class="btn btn-light" <% if number == @feature.percentage_of_time_value %>disabled<% end %>>
234
+ <% end %>
235
+ </div>
236
+ </form>
237
+ <form action="<%= script_name %>/features/<%= @feature.key %>/percentage_of_time" method="post" class="col-auto row">
238
+ <%== csrf_input_tag %>
239
+ <div class="col-auto">
240
+ <input style="width:5em;" type="number" max="100" min="0" name="value" <% if @feature.percentage_of_time_value > 0 %>value="<%= @feature.percentage_of_time_value %>"<% end %> title="Custom percentage (26, 32, etc.)" placeholder="0" class="form-control">
241
+ </div>
242
+ <div class="col-auto ms-auto"><input type="submit" name="action" value="Save" class="btn btn-primary"></div>
243
+ </form>
237
244
  </div>
238
245
  </div>
239
246
  <% end %>
@@ -247,10 +254,11 @@
247
254
 
248
255
  <div class="row">
249
256
  <% unless @feature.boolean_value %>
250
- <div class="col">
251
- <button type="submit" name="action" value="Enable" <% if Flipper::UI.configuration.confirm_fully_enable %>id="enable_feature__button"<% end %> class="btn btn-outline-success btn-block">
252
- <span class="d-block" data-toggle="tooltip"
257
+ <div class="col d-grid">
258
+ <button type="submit" name="action" value="Enable" class="btn btn-outline-success">
259
+ <span class="d-block" data-bs-toggle="tooltip"
253
260
  <% if Flipper::UI.configuration.confirm_fully_enable %>
261
+ data-confirmation-prompt="Are you sure you want to fully enable this feature for everyone? Please enter the name of the feature to confirm it: <%= feature_name %>"
254
262
  data-confirmation-text="<%= feature_name %>"
255
263
  <% end %>
256
264
  title="Enable for everyone"
@@ -262,10 +270,11 @@
262
270
  <% end %>
263
271
 
264
272
  <% unless @feature.off? %>
265
- <div class="col">
266
- <button type="submit" name="action" value="Disable" <% if Flipper::UI.configuration.confirm_disable %>id="disable_feature__button"<% end %> class="btn btn-outline-danger btn-block">
267
- <span class="d-block" data-toggle="tooltip"
273
+ <div class="col d-grid">
274
+ <button type="submit" name="action" value="Disable" class="btn btn-outline-danger">
275
+ <span class="d-block" data-bs-toggle="tooltip"
268
276
  <% if Flipper::UI.configuration.confirm_disable %>
277
+ data-confirmation-prompt="Are you sure you want to disable this feature for everyone? Please enter the name of the feature to confirm it: <%= feature_name %>"
269
278
  data-confirmation-text="<%= feature_name %>"
270
279
  <% end %>
271
280
  title="Disable for everyone by clearing all percentages, groups and actors."
@@ -296,7 +305,13 @@
296
305
  <%== csrf_input_tag %>
297
306
  <input type="hidden" id="feature_name" name="_feature" value="<%= feature_name %>">
298
307
  <input type="hidden" name="_method" value="DELETE">
299
- <button type="submit" name="action" value="Delete" id="delete_feature__button" data-confirmation-text="<%= feature_name %>" class="btn btn-outline-danger" data-toggle="tooltip" title="Remove feature from list of features and disable it." data-placement="right">Delete</button>
308
+ <button type="submit" name="action" value="Delete" id="delete_feature__button" class="btn btn-outline-danger" data-bs-toggle="tooltip" data-bs-placement="right"
309
+ data-confirmation-prompt="Are you sure you want to remove this feature from the list of features and disable it for everyone? Please enter the name of the feature to confirm it: <%= feature_name%>"
310
+ data-confirmation-text="<%= feature_name %>"
311
+ title="Remove feature from list of features and disable it."
312
+ >
313
+ Delete
314
+ </button>
300
315
  </form>
301
316
  </div>
302
317
  </div>
@@ -1,11 +1,11 @@
1
1
  <% if @show_blank_slate %>
2
- <div class="jumbotron text-center m-0">
2
+ <div class="bg-light p-5 rounded-3 text-center m-0">
3
3
  <% if Flipper::UI.configuration.fun %>
4
4
  <h4>But I've got a blank space baby...</h4>
5
5
  <p>And I'll flip your features.</p>
6
6
  <%- if write_allowed? && Flipper::UI.configuration.feature_creation_enabled -%>
7
7
  <p>
8
- <a class="btn btn-primary btn-sm" href="<%= script_name %>/features/new">Add Feature</a>
8
+ <a class="btn btn-primary" href="<%= script_name %>/features/new">Add Feature</a>
9
9
  </p>
10
10
  <%- end -%>
11
11
  <% else %>
@@ -13,7 +13,7 @@
13
13
  <p class="mb-1">You have not added any features to configure yet.</p>
14
14
  <%- if write_allowed? && Flipper::UI.configuration.feature_creation_enabled -%>
15
15
  <p class="mt-2">
16
- <a class="btn btn-primary btn-sm" href="<%= script_name %>/features/new">Add Feature</a>
16
+ <a class="btn btn-primary btn-lg" href="<%= script_name %>/features/new">Add Feature</a>
17
17
  </p>
18
18
  <% else %>
19
19
  <p>
@@ -24,36 +24,36 @@
24
24
  <% end %>
25
25
  </div>
26
26
  <% else %>
27
- <div class="card">
28
- <div class="card-header">
29
- <%- if write_allowed? && Flipper::UI.configuration.feature_creation_enabled -%>
30
- <div class="float-right">
31
- <a class="btn btn-primary btn-sm" href="<%= script_name %>/features/new">Add Feature</a>
32
- </div>
33
- <%- end -%>
34
- <h4 class="m-0">Features</h4>
27
+ <div class="list-group">
28
+ <div class="list-group-item bg-light">
29
+ <div class="row align-items-center">
30
+ <h4 class="col m-0">Features</h4>
31
+ <%- if write_allowed? && Flipper::UI.configuration.feature_creation_enabled -%>
32
+ <div class="col-auto">
33
+ <a class="btn btn-primary btn-sm" href="<%= script_name %>/features/new">Add Feature</a>
34
+ </div>
35
+ <%- end -%>
36
+ </div>
35
37
  </div>
36
- <div class="card-body py-0">
37
- <% @features.each do |feature| %>
38
- <div class="feature row align-items-center mt-0 px-3 border-bottom">
39
- <div class="col-1 col-md-auto">
40
- <span class="status <%= feature.color_class %>" data-toggle="tooltip" title=<%= feature.state.to_s.capitalize %>></span>
38
+ <% @features.each do |feature| %>
39
+ <a href="<%= "#{script_name}/features/#{feature.key}" %>" class="list-group-item list-group-item-action">
40
+ <div class="row align-items-center">
41
+ <div class="col-1 text-center">
42
+ <span class="status <%= feature.color_class %>" data-bs-toggle="tooltip" title=<%= feature.state.to_s.capitalize %>></span>
41
43
  </div>
42
- <div class="col-10">
43
- <a href="<%= "#{script_name}/features/#{feature.key}" %>" class="d-block px-0 py-3 btn text-left text-dark">
44
- <div class="text-truncate" style="font-weight: 500"><%= feature.key %></div>
45
- <% if Flipper::UI.configuration.show_feature_description_in_list? && Flipper::UI::Util.present?(feature.description) %>
46
- <div class="text-muted font-weight-light" style="line-height: 1.4; white-space: initial; padding: 8px 0">
47
- <%== Sanitize.fragment(feature.description, Sanitize::Config::BASIC) %>
48
- </div>
49
- <% end %>
50
- <div class="text-muted text-truncate">
51
- <%== feature.gates_in_words %>
44
+ <div class="col-11">
45
+ <div class="text-truncate" style="font-weight: 500"><%= feature.key %></div>
46
+ <% if Flipper::UI.configuration.show_feature_description_in_list? && Flipper::UI::Util.present?(feature.description) %>
47
+ <div class="text-muted fw-light" style="line-height: 1.4; white-space: initial; padding: 8px 0">
48
+ <%== Sanitize.fragment(feature.description, Sanitize::Config::BASIC) %>
52
49
  </div>
53
- </a>
50
+ <% end %>
51
+ <div class="text-muted text-truncate">
52
+ <%== feature.gates_in_words %>
53
+ </div>
54
54
  </div>
55
55
  </div>
56
- <% end %>
57
- </div>
56
+ </a>
57
+ <% end %>
58
58
  </div>
59
59
  <% end %>
@@ -12,43 +12,52 @@
12
12
  <body class="py-4">
13
13
  <div class="container mw-600">
14
14
  <%- unless Flipper::UI.configuration.banner_text.nil? -%>
15
- <div class="alert alert-<%= Flipper::UI.configuration.banner_class %> text-center font-weight-bold">
15
+ <div class="alert alert-<%= Flipper::UI.configuration.banner_class %> text-center fw-bold">
16
16
  <%== Sanitize.fragment(Flipper::UI.configuration.banner_text, Sanitize::Config::BASIC) %>
17
17
  </div>
18
18
  <%- end -%>
19
19
 
20
- <div class="container text-muted small mb-3">
21
- <a href="https://www.flippercloud.io/docs/ui?utm_source=oss&utm_medium=ui&utm_campaign=docs">Docs</a> &bull;
22
- <a href="<%= script_name %>/settings">Settings</a> &bull;
23
- Version: <%= Flipper::VERSION %>
24
- <a href="#" class="badge badge-warning ml-2 d-none" style="font-size:100%" id="new-version-badge" data-version="<%= Flipper::VERSION %>">
20
+ <div class="text-muted small text-end">
21
+ <a href="#" class="badge text-bg-warning ms-2 d-none" style="font-size:100%" id="new-version-badge" data-version="<%= Flipper::VERSION %>">
25
22
  </a>
26
23
 
27
24
  <% if Flipper.deprecated_ruby_version? %>
28
- <a href="https://github.com/flippercloud/flipper/pull/776" class="badge badge-danger ml-2" style="font-size:100%">
25
+ <a href="https://github.com/flippercloud/flipper/pull/776" class="badge text-bg-danger ms-2" style="font-size:100%">
29
26
  Ruby <%= RUBY_VERSION %> deprecated
30
27
  </a>
31
28
  <% end %>
32
29
 
33
30
  <% if defined?(Rails) && Flipper::Engine.deprecated_rails_version? %>
34
- <a href="https://github.com/flippercloud/flipper/pull/776" class="badge badge-danger ml-2" style="font-size:100%">
31
+ <a href="https://github.com/flippercloud/flipper/pull/776" class="badge text-bg-danger ms-2" style="font-size:100%">
35
32
  Rails <%= Rails.version %> deprecated
36
33
  </a>
37
34
  <% end %>
38
35
  </div>
36
+ <nav>
37
+ <ul class="nav nav-underline mb-3 align-items-center">
38
+ <% if Flipper::UI.configuration.application_href %>
39
+ <li class="nav-item">
40
+ <a class="nav-link" href="<%= Flipper::UI.configuration.application_href %>">
41
+ ‹ App
42
+ </a>
43
+ </li>
44
+ <% end %>
39
45
 
40
- <nav aria-label="breadcrumb">
41
- <ol class="breadcrumb bg-white border align-items-center mb-4">
42
- <% @breadcrumbs.each do |breadcrumb| %>
43
- <li class="breadcrumb-item <% if breadcrumb.active? %>active<% end %>">
44
- <% if breadcrumb.active? %>
45
- <%= breadcrumb.text %>
46
- <% else %>
47
- <a href="<%= breadcrumb.href %>"><%= breadcrumb.text %></a>
48
- <% end %>
46
+
47
+ <% Flipper::UI.configuration.nav_items.each do |item| %>
48
+ <li class="nav-item">
49
+ <a href="<%= url_for(item[:href]) %>" class="nav-link<%= " active" if request.url.start_with?(url_for(item[:href])) %>">
50
+ <%= item[:title] %>
51
+ </a>
49
52
  </li>
50
53
  <% end %>
51
- </ol>
54
+
55
+ <li class="nav-item ms-auto text-muted small">
56
+ <a href="https://www.flippercloud.io/docs/ui?utm_source=oss&utm_medium=ui&utm_campaign=docs">Docs</a>
57
+ &bull;
58
+ Version: <%= Flipper::VERSION %>
59
+ </li>
60
+ </ul>
52
61
  </nav>
53
62
 
54
63
  <div>
@@ -71,7 +80,6 @@
71
80
  <% end %>
72
81
  </div>
73
82
 
74
- <script src="<%= script_name + jquery_js[:src] %>" integrity="<%= jquery_js[:hash] %>" crossorigin="anonymous"></script>
75
83
  <script src="<%= script_name + popper_js[:src] %>" integrity="<%= popper_js[:hash] %>" crossorigin="anonymous"></script>
76
84
  <script src="<%= script_name + bootstrap_js[:src] %>" integrity="<%= bootstrap_js[:hash] %>" crossorigin="anonymous"></script>
77
85
  <script src="<%= script_name %>/js/application.js?v=<%= Flipper::VERSION %>"></script>
@@ -6,7 +6,7 @@
6
6
  <form action="<%= script_name %>/settings/export" method="post">
7
7
  <%== csrf_input_tag %>
8
8
  <p>Download all your feature data in a single file.</p>
9
- <input type="submit" value="Download" class="btn btn-sm btn-light">
9
+ <input type="submit" value="Download" class="btn btn-light">
10
10
  </form>
11
11
  </div>
12
12
  </div>
@@ -17,10 +17,10 @@
17
17
  </div>
18
18
  <div class="card-body">
19
19
  <p>Import a backup file to restore your feature data. This will overwrite any of your existing feature data so be sure you are uploading the correct export.</p>
20
- <form action="<%= script_name %>/settings/import" method="post" class="form-inline" enctype="multipart/form-data">
20
+ <form action="<%= script_name %>/settings/import" method="post" class="row" enctype="multipart/form-data">
21
21
  <%== csrf_input_tag %>
22
- <input type="file" name="file" class="form-control form-control-sm mr-sm-2 mb-2 mb-sm-0">
23
- <input type="submit" value="Import" class="btn btn-sm btn-light">
22
+ <div class="col-auto"><input type="file" name="file" class="form-control"></div>
23
+ <div class="col-auto"><input type="submit" value="Import" class="btn btn-light"></div>
24
24
  </form>
25
25
  </div>
26
26
  </div>
@@ -1,5 +1,5 @@
1
1
  module Flipper
2
- VERSION = '1.2.2'.freeze
2
+ VERSION = '1.3.0'.freeze
3
3
 
4
4
  REQUIRED_RUBY_VERSION = '2.6'.freeze
5
5
  NEXT_REQUIRED_RUBY_VERSION = '3.0'.freeze
@@ -20,7 +20,7 @@ RSpec.describe Flipper::UI::Actions::ActorsGate do
20
20
  end
21
21
 
22
22
  it 'renders add new actor form' do
23
- form = '<form action="/features/search/actors" method="post" class="form-inline">'
23
+ form = '<form action="/features/search/actors" method="post" class="row">'
24
24
  expect(last_response.body).to include(form)
25
25
  end
26
26
  end
@@ -35,7 +35,7 @@ RSpec.describe Flipper::UI::Actions::ActorsGate do
35
35
  end
36
36
 
37
37
  it 'renders add new actor form' do
38
- form = '<form action="/features/a/b/actors" method="post" class="form-inline">'
38
+ form = '<form action="/features/a/b/actors" method="post" class="row">'
39
39
  expect(last_response.body).to include(form)
40
40
  end
41
41
  end
@@ -15,7 +15,7 @@ RSpec.describe Flipper::UI::Actions::AddFeature do
15
15
  end
16
16
 
17
17
  it 'renders template' do
18
- form = '<form action="/features" method="post" class="form-inline mb-2">'
18
+ form = '<form action="/features" method="post" class="row g-3 mb-2">'
19
19
  expect(last_response.body).to include(form)
20
20
  end
21
21
  end
@@ -26,7 +26,7 @@ RSpec.describe Flipper::UI::Actions::GroupsGate do
26
26
  end
27
27
 
28
28
  it 'renders add new group form' do
29
- form = '<form action="/features/search/groups" method="post" class="form-inline">'
29
+ form = '<form action="/features/search/groups" method="post" class="row">'
30
30
  expect(last_response.body).to include(form)
31
31
  end
32
32
  end
@@ -35,14 +35,19 @@ RSpec.describe Flipper::UI::Configuration do
35
35
  end
36
36
  end
37
37
 
38
- describe "#application_breadcrumb_href" do
38
+ describe "#application_href" do
39
39
  it "has default value" do
40
- expect(configuration.application_breadcrumb_href).to eq(nil)
40
+ expect(configuration.application_href).to eq(nil)
41
41
  end
42
42
 
43
43
  it "can be updated" do
44
- configuration.application_breadcrumb_href = 'http://www.myapp.com'
45
- expect(configuration.application_breadcrumb_href).to eq('http://www.myapp.com')
44
+ configuration.application_href = 'http://www.myapp.com'
45
+ expect(configuration.application_href).to eq('http://www.myapp.com')
46
+ end
47
+
48
+ it 'aliases application_breadcrumb_href to application_href' do
49
+ configuration.application_breadcrumb_href = "/myapp"
50
+ expect(configuration.application_href).to eq("/myapp")
46
51
  end
47
52
  end
48
53
 
@@ -76,56 +76,44 @@ RSpec.describe Flipper::UI do
76
76
  end
77
77
  end
78
78
 
79
- describe "application_breadcrumb_href" do
80
- it 'does not have an application_breadcrumb_href by default' do
81
- expect(configuration.application_breadcrumb_href).to be(nil)
79
+ describe "application_href" do
80
+ around do |example|
81
+ original_href = configuration.application_href
82
+ example.run
83
+ ensure
84
+ configuration.application_href = original_href
82
85
  end
83
86
 
84
- context 'with application_breadcrumb_href not set' do
85
- before do
86
- @original_application_breadcrumb_href = configuration.application_breadcrumb_href
87
- configuration.application_breadcrumb_href = nil
88
- end
89
-
90
- after do
91
- configuration.application_breadcrumb_href = @original_application_breadcrumb_href
92
- end
87
+ it 'does not have an application_href by default' do
88
+ expect(configuration.application_href).to be(nil)
89
+ end
93
90
 
94
- it 'does not add App breadcrumb' do
91
+ context 'with application_href not set' do
92
+ it 'does not add App link' do
95
93
  get '/features'
96
94
  expect(last_response.body).not_to include('<a href="/myapp">App</a>')
97
95
  end
98
96
  end
99
97
 
100
- context 'with application_breadcrumb_href set' do
98
+ context 'with application_href set' do
101
99
  before do
102
- @original_application_breadcrumb_href = configuration.application_breadcrumb_href
103
- configuration.application_breadcrumb_href = '/myapp'
100
+ configuration.application_href = '/myapp'
104
101
  end
105
102
 
106
- after do
107
- configuration.application_breadcrumb_href = @original_application_breadcrumb_href
108
- end
109
-
110
- it 'does add App breadcrumb' do
103
+ it 'does add App link' do
111
104
  get '/features'
112
- expect(last_response.body).to include('<a href="/myapp">App</a>')
105
+ expect(last_response.body).to match('<a.*href="/myapp"')
113
106
  end
114
107
  end
115
108
 
116
- context 'with application_breadcrumb_href set to full url' do
109
+ context 'with application_href set to full url' do
117
110
  before do
118
- @original_application_breadcrumb_href = configuration.application_breadcrumb_href
119
- configuration.application_breadcrumb_href = 'https://myapp.com/'
120
- end
121
-
122
- after do
123
- configuration.application_breadcrumb_href = @original_application_breadcrumb_href
111
+ configuration.application_href = "https://myapp.com/"
124
112
  end
125
113
 
126
- it 'does add App breadcrumb' do
114
+ it 'does add App link' do
127
115
  get '/features'
128
- expect(last_response.body).to include('<a href="https://myapp.com/">App</a>')
116
+ expect(last_response.body).to match('<a.*href="https://myapp.com/"')
129
117
  end
130
118
  end
131
119
  end