inline_forms 8.1.65 → 8.1.66
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 +4 -4
- data/CHANGELOG.md +36 -0
- data/app/assets/javascripts/inline_forms/inline_forms.js +23 -7
- data/app/assets/stylesheets/inline_forms/foundation_and_overrides.scss +8 -1
- data/app/assets/stylesheets/inline_forms/inline_forms.scss +10 -0
- data/app/helpers/inline_forms_helper.rb +15 -0
- data/app/views/devise/inline_forms/_content_for_devise_login.html.erb +3 -2
- data/app/views/devise/passwords/_top-bar-and-flash.html.erb +1 -1
- data/app/views/devise/sessions/_top-bar.html.erb +1 -1
- data/app/views/devise/sessions/new.html.erb +1 -1
- data/app/views/inline_forms/_generic_search.html.erb +3 -3
- data/app/views/inline_forms/_versions_file_entry.html.erb +1 -1
- data/app/views/inline_forms/_versions_list.html.erb +1 -1
- data/app/views/inline_forms/file_trash/index.html.erb +4 -4
- data/app/views/inline_forms/file_trash_panel.html.erb +3 -3
- data/app/views/layouts/application.html.erb +1 -1
- data/app/views/layouts/devise.html.erb +1 -1
- data/app/views/layouts/inline_forms.html.erb +9 -1
- data/lib/inline_forms/searchable.rb +6 -4
- data/lib/inline_forms/version.rb +1 -1
- data/lib/locales/inline_forms.en.yml +15 -4
- data/lib/locales/inline_forms.nl.yml +15 -4
- data/test/integration/locale_test.rb +56 -1
- data/test/integration/model_top_bar_test.rb +18 -0
- data/test/integration/turbo_menu_test.rb +46 -0
- metadata +3 -1
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 7d798e9e7a6d981b422009a82073f77675148cfcd17263b501067d7d6e0a3b57
|
|
4
|
+
data.tar.gz: 76876893fbad74dd364b02c21f927bc414cb86b2b6e7a89b1caa5a3a941bab4d
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: d1c4624d5315833d86d2578112d2f494b8f7cd32219698de060afbc7570a638fb98c5bf07fd376de87854b264c85521033da2c88920093c3552b5925c5062c8b
|
|
7
|
+
data.tar.gz: dab2278a87d37e9005baafc360503a0a767cbd95ce1e20ad0484463098d65763547daac3f25c6e16ca1707ca8ee92ef7e9f922ffdfce996c74670af849f028bc
|
data/CHANGELOG.md
CHANGED
|
@@ -4,6 +4,42 @@ All notable changes to this project are documented in this file.
|
|
|
4
4
|
|
|
5
5
|
## [Unreleased]
|
|
6
6
|
|
|
7
|
+
### Known issues
|
|
8
|
+
|
|
9
|
+
- Known issues, parked (local note `stuff/2026-10-31-dropdown-rule-3-parked.md`) with dropdown order rule 3: `includes`, `group` or `select` in `inline_forms_list` make `dropdown` and `dropdown_with_other` raise while `/index` works, so 8.1.65's fix for a list order on a joined table holds only for `joins`, `left_joins` and `eager_load`; a has_many join gives duplicate options. Present since 8.1.60. No host's list scope has these today. Parked; not changed.
|
|
10
|
+
|
|
11
|
+
## [8.1.66] - 2026-11-06
|
|
12
|
+
|
|
13
|
+
Requests from Papiamentu's move to the 8.1.65 admin (gem layout, engine model top bar, gem stylesheet); each was worked around in the apps.
|
|
14
|
+
|
|
15
|
+
### Fixed
|
|
16
|
+
|
|
17
|
+
- **Top-bar submenus no longer flash open after a Turbo Drive visit.** Turbo replaced `<body>` and re-ran the inline_forms bundle, which loaded a fresh jQuery; until it finished, the new body's "more..." and user submenus showed as plain lists. StProject, Scouting and Papiamentu each copied the admin layout to fix it. Now `layouts/inline_forms` starts as `<html class="no-js">`; `inline_forms.js` re-initialises Foundation on `turbo:load`, behind a once-guard (Turbo still re-runs the bundle on each visit, so a visit from a layout without it works; the guard stops the listeners piling up and a second `foundation()` from the fresh jQuery); and `inline_forms.scss` hides a submenu until Foundation has marked it `is-dropdown-submenu`.
|
|
18
|
+
- **No more 404 for `/vendor/assets/stylesheets/tippy.css`.** `foundation_and_overrides.scss` imported `tippy.css` with its extension, which Dart Sass keeps as a plain CSS `@import`; the browser resolved it next to the compiled stylesheet. The vendored file is now inlined with `meta.load-css` (no new `@import` deprecation), in admin and devise CSS. Apps can delete their `public/vendor/assets/stylesheets/tippy.css`.
|
|
19
|
+
- **Version and file timestamps show the year.** The versions list, the file entries in it and the file trash used `l(time, format: :short)`, and rails-i18n's `:short` has no year. They now use the gem's `time.formats.inline_forms` (`"%d %b %Y %H:%M"` in en and nl; a host can override it). The versions list and file entries render a `<time>` with the ISO 8601 time as `datetime` and `title` (new helper `inline_forms_time`).
|
|
20
|
+
- **The generic search box is translated.** `_generic_search` had "zoek op naam...", "zoek" and "reset" hard-coded; now `inline_forms.search.placeholder` / `.submit` / `.reset` (en: "search...", "search", "reset"; nl: "zoeken...", "zoek", "wissen").
|
|
21
|
+
- **No placeholder titles.** The gem's en and nl locales defined `general.application_title: Application Title`, `devise.title_for_devise: Title for Devise` and `devise.welcome: Welcome for Devise`, shown in any locale the host did not override (the installer only writes en). The keys are gone; the application and devise layouts and the devise top bars fall back to the host's `application_name` translation, else the Rails module name (new helper `inline_forms_application_title`), and the sign-in page to `inline_forms.devise.welcome_to` ("Welcome to %{name}!" / "Welkom bij %{name}!"). A host's own translations still win.
|
|
22
|
+
- **`inline_forms_search_on` no longer touches the database when the model loads.** It quoted the table and column names in the class body, so loading the model needed a connection; the clause is now built when the scope runs.
|
|
23
|
+
|
|
24
|
+
### Changed
|
|
25
|
+
|
|
26
|
+
- **Installer: the Dart Sass self-import guard no longer patches dartsass-rails.** The generated `config/initializers/inline_forms_dartsass_builds.rb` prepended the gem's stylesheet dir to `Dartsass::Runner.dartsass_load_paths`, an internal (a silent no-op if renamed; a boot error if `dartsass/runner` moved). The two `inline_forms_install/*_main.scss` entries now `@use "inline_forms/inline_forms.scss"` and `"inline_forms/devise.scss"` with the extension, which Dart Sass never resolves to a previous `app/assets/builds/*.css`, and which fails loudly if the gem renames the file. Existing apps keep their initializer until they change it.
|
|
27
|
+
|
|
28
|
+
### Not done
|
|
29
|
+
|
|
30
|
+
- **Autocomplete in the generic search** (a `rails-jquery-autocomplete` field when the controller declares `autocomplete :<model>, :name`): that gem needs jQuery UI, which inline_forms dropped in 8.1.26, and it is not an inline_forms dependency. An app that wants it keeps its own `_generic_search` override.
|
|
31
|
+
|
|
32
|
+
### Tests
|
|
33
|
+
|
|
34
|
+
- `turbo_menu_test` (new): the layout's `no-js`, the once-guarded `turbo:load` re-init, the submenu rule, tippy inlined.
|
|
35
|
+
- `locale_test`: timestamps with the year and an ISO `title`, the search box in en and nl, no placeholder titles (devise and application layouts, the devise top bar).
|
|
36
|
+
- `model_top_bar_test`: `inline_forms_search_on` declared on a model whose connection raises.
|
|
37
|
+
- Example app: `example_app_stylesheet_test` (new) checks the compiled CSS has no `@import`, inlines tippy and hides uninitialised submenus; `example_app_dartsass_build_test` checks the `.scss` extensions instead of the patched load path. `example_app_user_locale_test` and `example_app_layout_title_test` expect `<html lang="…" class="no-js">`.
|
|
38
|
+
|
|
39
|
+
### Lockstep
|
|
40
|
+
|
|
41
|
+
- validation_hints 8.1.66, inline_forms_installer 8.1.66, inline_forms_schema_edit 8.1.66.
|
|
42
|
+
|
|
7
43
|
## [8.1.65] - 2026-10-31
|
|
8
44
|
|
|
9
45
|
Fixes from a review of 8.1.60 to 8.1.64.
|
|
@@ -14,14 +14,30 @@
|
|
|
14
14
|
// dropdown_with_other combobox is an <input list> + <datalist>, and
|
|
15
15
|
// slider_with_values is a native <input type="range">. jQuery itself stays
|
|
16
16
|
// only because Foundation 6's JS requires it.
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
17
|
+
// Once per page load. Turbo Drive re-runs this bundle on every visit (it sits
|
|
18
|
+
// in <body>), and each run brings a fresh jQuery: without the guard each run
|
|
19
|
+
// added another turbo:load listener, and a ready-handler `foundation()` from
|
|
20
|
+
// the fresh copy (its own plugin data) would initialise the menus twice.
|
|
21
|
+
if (!window.inlineFormsBooted) {
|
|
22
|
+
window.inlineFormsBooted = true;
|
|
21
23
|
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
24
|
+
$(function(){
|
|
25
|
+
$(document).foundation();
|
|
26
|
+
initInlineFormsWidgets(document);
|
|
27
|
+
});
|
|
28
|
+
|
|
29
|
+
// Turbo Drive replaces <body> on a visit: the new top bar's DropdownMenu
|
|
30
|
+
// has to be initialised again, or its submenus show as plain lists.
|
|
31
|
+
// Foundation's reflow skips elements that already carry a plugin, so the
|
|
32
|
+
// call on the first page (turbo:load fires there too) is harmless. jQuery
|
|
33
|
+
// is looked up at call time.
|
|
34
|
+
document.addEventListener("turbo:load", function() {
|
|
35
|
+
if (window.jQuery && window.jQuery.fn.foundation) {
|
|
36
|
+
window.jQuery(document).foundation();
|
|
37
|
+
}
|
|
38
|
+
initInlineFormsWidgets(document);
|
|
39
|
+
});
|
|
40
|
+
}
|
|
25
41
|
|
|
26
42
|
// Widget init: one path for first paint, turbo:load and turbo:frame-load.
|
|
27
43
|
// Form element helpers emit class hooks only — no inline <script> tags.
|
|
@@ -1,5 +1,7 @@
|
|
|
1
1
|
@charset 'utf-8';
|
|
2
2
|
|
|
3
|
+
@use 'sass:meta';
|
|
4
|
+
|
|
3
5
|
@import 'settings/settings';
|
|
4
6
|
|
|
5
7
|
// Match F5 visual proportions: F5 `$row-width` was 62.5rem (1000px); F6
|
|
@@ -58,7 +60,12 @@ $body-font-family: 'Open Sans', 'Helvetica Neue', Helvetica, Roboto, Arial, sans
|
|
|
58
60
|
// Stays @import: see the module-system note in inline_forms.scss.
|
|
59
61
|
@import 'theme';
|
|
60
62
|
|
|
61
|
-
|
|
63
|
+
// Tippy's vendored CSS, inlined. It used to be `@import '…/tippy.css'`,
|
|
64
|
+
// which Dart Sass passes through as a plain CSS @import: the browser then
|
|
65
|
+
// fetched /vendor/assets/stylesheets/tippy.css (resolved next to the compiled
|
|
66
|
+
// stylesheet), a 404 on every page. load-css (not an extension-less @import,
|
|
67
|
+
// deprecated for Dart Sass 3) emits it here, in the same place.
|
|
68
|
+
@include meta.load-css('../../../vendor/assets/stylesheets/tippy');
|
|
62
69
|
|
|
63
70
|
.validation-hints-source[hidden] {
|
|
64
71
|
display: none !important;
|
|
@@ -113,6 +113,16 @@
|
|
|
113
113
|
}
|
|
114
114
|
}
|
|
115
115
|
|
|
116
|
+
// A top-bar submenu shows as a plain list until Foundation's DropdownMenu
|
|
117
|
+
// marks it `is-dropdown-submenu` (whose own rules then hide it). On the first
|
|
118
|
+
// page `html.no-js` covers that gap (layouts/inline_forms.html.erb), but
|
|
119
|
+
// Foundation removes the class and Turbo Drive never restores it, so after a
|
|
120
|
+
// visit the new body's submenus flashed open until inline_forms.js
|
|
121
|
+
// re-initialised them on turbo:load.
|
|
122
|
+
.dropdown.menu > li > ul.menu:not(.is-dropdown-submenu) {
|
|
123
|
+
display: none;
|
|
124
|
+
}
|
|
125
|
+
|
|
116
126
|
// Match: model top bar is the wider gold band (height 90px, padded so its
|
|
117
127
|
// usable area is in the lower 45px). Override Foundation 6's default
|
|
118
128
|
// `.top-bar { padding: 0.5rem }` so our 45/90px math holds.
|
|
@@ -394,6 +394,21 @@ module InlineFormsHelper
|
|
|
394
394
|
values
|
|
395
395
|
end
|
|
396
396
|
|
|
397
|
+
# A version or file timestamp: the date with its year
|
|
398
|
+
# (time.formats.inline_forms; rails-i18n's :short has none, so edits years
|
|
399
|
+
# apart read the same), the exact ISO 8601 time on hover.
|
|
400
|
+
def inline_forms_time(time)
|
|
401
|
+
tag.time(l(time, format: :inline_forms), datetime: time.iso8601, title: time.iso8601)
|
|
402
|
+
end
|
|
403
|
+
|
|
404
|
+
# The application's name for the gem's titles and the Devise pages: the
|
|
405
|
+
# host's `application_name` translation, else its Rails module name. The
|
|
406
|
+
# gem used to ship "Application Title" / "Title for Devise" placeholders,
|
|
407
|
+
# shown in every locale the host did not override (nl, in all of them).
|
|
408
|
+
def inline_forms_application_title
|
|
409
|
+
t("application_name", default: Rails.application.class.module_parent_name.titleize)
|
|
410
|
+
end
|
|
411
|
+
|
|
397
412
|
def version_modified_by(id)
|
|
398
413
|
return "Unknown" if id.blank?
|
|
399
414
|
|
|
@@ -6,7 +6,8 @@
|
|
|
6
6
|
and the contents will be displayed here.
|
|
7
7
|
</p>
|
|
8
8
|
<p>
|
|
9
|
-
|
|
10
|
-
|
|
9
|
+
The titles come from the <em>application_name</em> translation; override
|
|
10
|
+
inline_forms.general.application_title, inline_forms.devise.title_for_devise
|
|
11
|
+
or inline_forms.devise.welcome in <em>config/locales</em> to change one.<br />
|
|
11
12
|
</p>
|
|
12
13
|
|
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
<div class="top-bar-left">
|
|
4
4
|
<ul class="menu">
|
|
5
5
|
<li class="menu-text">
|
|
6
|
-
<h1 style="margin: 0; font-size: inherit;"><a href='/'><%= t('inline_forms.devise.title_for_devise') %> <%= InlineForms::VERSION %></a></h1>
|
|
6
|
+
<h1 style="margin: 0; font-size: inherit;"><a href='/'><%= t('inline_forms.devise.title_for_devise', default: inline_forms_application_title) %> <%= InlineForms::VERSION %></a></h1>
|
|
7
7
|
</li>
|
|
8
8
|
</ul>
|
|
9
9
|
</div>
|
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
<div class="top-bar-left">
|
|
4
4
|
<ul class="menu">
|
|
5
5
|
<li class="menu-text">
|
|
6
|
-
<h1 style="margin: 0; font-size: inherit;"><a href='/'><%= t('inline_forms.devise.title_for_devise') %> <%= InlineForms::VERSION %></a></h1>
|
|
6
|
+
<h1 style="margin: 0; font-size: inherit;"><a href='/'><%= t('inline_forms.devise.title_for_devise', default: inline_forms_application_title) %> <%= InlineForms::VERSION %></a></h1>
|
|
7
7
|
</li>
|
|
8
8
|
</ul>
|
|
9
9
|
</div>
|
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
|
|
5
5
|
<div class="row" id='inline_forms_devise_header_row'>
|
|
6
6
|
<div class="small-11 centered column">
|
|
7
|
-
<h1><%= t('inline_forms.devise.welcome') %></h1>
|
|
7
|
+
<h1><%= t('inline_forms.devise.welcome', default: t('inline_forms.devise.welcome_to', name: inline_forms_application_title)) %></h1>
|
|
8
8
|
</div>
|
|
9
9
|
</div>
|
|
10
10
|
|
|
@@ -1,13 +1,13 @@
|
|
|
1
1
|
<%= form_tag request.path, method: :get do %>
|
|
2
2
|
<div class="row collapse">
|
|
3
3
|
<div class="large-1 small-1 columns">
|
|
4
|
-
<%= link_to
|
|
4
|
+
<%= link_to tag.i(class: "fi-x"), request.path, title: t("inline_forms.search.reset"), class: "button expanded inline_forms_model_top_bar_buttons" %>
|
|
5
5
|
</div>
|
|
6
6
|
<div class="large-6 small-6 columns">
|
|
7
|
-
<%= text_field_tag :search, params[:search], placeholder: "
|
|
7
|
+
<%= text_field_tag :search, params[:search], placeholder: t("inline_forms.search.placeholder"), id: "input_search" %>
|
|
8
8
|
</div>
|
|
9
9
|
<div class="large-3 small-3 columns">
|
|
10
|
-
<%= submit_tag "
|
|
10
|
+
<%= submit_tag t("inline_forms.search.submit"), class: "button expanded inline_forms_model_top_bar_buttons" %>
|
|
11
11
|
</div>
|
|
12
12
|
<div class="large-2 small-2 columns">
|
|
13
13
|
|
|
@@ -23,7 +23,7 @@
|
|
|
23
23
|
<%= t("inline_forms.files.event.#{entry[:file_event]}") %>
|
|
24
24
|
</div>
|
|
25
25
|
<div class="small-2 column">
|
|
26
|
-
<%=
|
|
26
|
+
<%= inline_forms_time(entry[:at]) %>
|
|
27
27
|
</div>
|
|
28
28
|
<div class="small-2 column">
|
|
29
29
|
<%= inline_forms_file_user_name(entry[:by]) %>
|
|
@@ -95,7 +95,7 @@
|
|
|
95
95
|
<%= t("inline_forms.view.version_events.#{version.event}", default: version.event) -%><% if entry[:kind] == :rich_text %> <em>(<%= t("inline_forms.view.versions_rich_text") %>)</em><% end %>
|
|
96
96
|
</div>
|
|
97
97
|
<div class="small-2 column">
|
|
98
|
-
<%=
|
|
98
|
+
<%= inline_forms_time(version.created_at) -%>
|
|
99
99
|
</div>
|
|
100
100
|
<div class="small-2 column">
|
|
101
101
|
<%= version_modified_by version.whodunnit -%>
|
|
@@ -14,7 +14,7 @@
|
|
|
14
14
|
<% elsif @last_sweep.error.present? %>
|
|
15
15
|
<%= t("inline_forms.files.global.sweep_failed", error: @last_sweep.error) %>
|
|
16
16
|
<% else %>
|
|
17
|
-
<%= t("inline_forms.files.global.sweep_last", at: l(@last_sweep.ran_at, format: :
|
|
17
|
+
<%= t("inline_forms.files.global.sweep_last", at: l(@last_sweep.ran_at, format: :inline_forms), count: @last_sweep.purged_count) %>
|
|
18
18
|
<% end %>
|
|
19
19
|
</p>
|
|
20
20
|
|
|
@@ -78,10 +78,10 @@
|
|
|
78
78
|
<span class="inline_forms-file-name"><%= inline_forms_file_entry_name(entry) %></span>
|
|
79
79
|
<span class="inline_forms-file-meta"><%= inline_forms_file_size(entry.byte_size) %> · <%= t("inline_forms.files.reason.#{entry.reason}") %></span>
|
|
80
80
|
</td>
|
|
81
|
-
<td><%= t("inline_forms.files.by_on", user: inline_forms_file_user_name(entry.trashed_by_id), at: l(entry.trashed_at, format: :
|
|
81
|
+
<td><%= t("inline_forms.files.by_on", user: inline_forms_file_user_name(entry.trashed_by_id), at: l(entry.trashed_at, format: :inline_forms)) %></td>
|
|
82
82
|
<td>
|
|
83
|
-
<% if entry.state == :restored %><%= t("inline_forms.files.state_restored", user: inline_forms_file_user_name(entry.restored_by_id), at: l(entry.restored_at, format: :
|
|
84
|
-
<% elsif entry.state == :purged %><%= t("inline_forms.files.state_purged", user: inline_forms_file_user_name(entry.purged_by_id), at: l(entry.purged_at, format: :
|
|
83
|
+
<% if entry.state == :restored %><%= t("inline_forms.files.state_restored", user: inline_forms_file_user_name(entry.restored_by_id), at: l(entry.restored_at, format: :inline_forms)) %>
|
|
84
|
+
<% elsif entry.state == :purged %><%= t("inline_forms.files.state_purged", user: inline_forms_file_user_name(entry.purged_by_id), at: l(entry.purged_at, format: :inline_forms)) %>
|
|
85
85
|
<% elsif entry.state == :expired %><%= t("inline_forms.files.state_expired") %>
|
|
86
86
|
<% else %>
|
|
87
87
|
<% if entry.purge_after %><%= t("inline_forms.files.expires_in", count: ((entry.purge_after - Time.current) / 1.day).ceil, date: l(entry.purge_after.to_date)) %><% else %><%= t("inline_forms.files.global.states.trashed") %><% end %>
|
|
@@ -41,13 +41,13 @@
|
|
|
41
41
|
<span class="inline_forms-file-meta">
|
|
42
42
|
<%= inline_forms_file_size(entry.byte_size) %> ·
|
|
43
43
|
<%= t("inline_forms.files.reason.#{entry.reason}") %>
|
|
44
|
-
<%= t("inline_forms.files.by_on", user: inline_forms_file_user_name(entry.trashed_by_id), at: l(entry.trashed_at, format: :
|
|
44
|
+
<%= t("inline_forms.files.by_on", user: inline_forms_file_user_name(entry.trashed_by_id), at: l(entry.trashed_at, format: :inline_forms)) %>
|
|
45
45
|
</span>
|
|
46
46
|
<span class="inline_forms-file-meta">
|
|
47
47
|
<% if entry.state == :restored %>
|
|
48
|
-
<%= t("inline_forms.files.state_restored", user: inline_forms_file_user_name(entry.restored_by_id), at: l(entry.restored_at, format: :
|
|
48
|
+
<%= t("inline_forms.files.state_restored", user: inline_forms_file_user_name(entry.restored_by_id), at: l(entry.restored_at, format: :inline_forms)) %>
|
|
49
49
|
<% elsif entry.state == :purged %>
|
|
50
|
-
<%= t("inline_forms.files.state_purged", user: inline_forms_file_user_name(entry.purged_by_id), at: l(entry.purged_at, format: :
|
|
50
|
+
<%= t("inline_forms.files.state_purged", user: inline_forms_file_user_name(entry.purged_by_id), at: l(entry.purged_at, format: :inline_forms)) %>
|
|
51
51
|
<% elsif entry.state == :expired %>
|
|
52
52
|
<%= t("inline_forms.files.state_expired") %>
|
|
53
53
|
<% else %>
|
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
<meta charset="utf-8" />
|
|
5
5
|
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
|
6
6
|
<title>
|
|
7
|
-
<%= t('inline_forms.general.application_title') %> v<%= InlineForms::VERSION %>
|
|
7
|
+
<%= t('inline_forms.general.application_title', default: inline_forms_application_title) %> v<%= InlineForms::VERSION %>
|
|
8
8
|
</title>
|
|
9
9
|
<%= stylesheet_link_tag "application" %>
|
|
10
10
|
<%= stylesheet_link_tag "inline_forms_fonts" %>
|
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
<meta charset="utf-8" />
|
|
5
5
|
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
|
6
6
|
<title>
|
|
7
|
-
<%= t('inline_forms.general.application_title') %> v<%= InlineForms::VERSION %>
|
|
7
|
+
<%= t('inline_forms.general.application_title', default: inline_forms_application_title) %> v<%= InlineForms::VERSION %>
|
|
8
8
|
</title>
|
|
9
9
|
<%= stylesheet_link_tag "inline_forms_fonts" %>
|
|
10
10
|
<%= stylesheet_link_tag "inline_forms/devise" %>
|
|
@@ -1,5 +1,10 @@
|
|
|
1
1
|
<!DOCTYPE html>
|
|
2
|
-
|
|
2
|
+
<%# class="no-js": Foundation's CSS hides the top-bar submenus while it is set %>
|
|
3
|
+
<%# and its JS removes it on the first init, so the submenus do not show as %>
|
|
4
|
+
<%# plain lists before then. Turbo Drive never puts it back, so later visits %>
|
|
5
|
+
<%# rely on the `ul.menu:not(.is-dropdown-submenu)` rule in inline_forms.scss %>
|
|
6
|
+
<%# and on the turbo:load handler in inline_forms.js. %>
|
|
7
|
+
<html lang="<%= I18n.locale %>" class="no-js">
|
|
3
8
|
<head>
|
|
4
9
|
<meta charset="utf-8" />
|
|
5
10
|
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
|
@@ -37,6 +42,9 @@
|
|
|
37
42
|
</div>
|
|
38
43
|
<%# Tippy.js (v6) needs Popper.js as a global before its UMD factory runs %>
|
|
39
44
|
<%# (`tippy-bundle.umd.min` only bundles default themes, not Popper itself). %>
|
|
45
|
+
<%# Turbo Drive re-runs these on every visit (no data-turbo-eval="false": %>
|
|
46
|
+
<%# a visit from a page whose layout lacks them must still load them). %>
|
|
47
|
+
<%# inline_forms.js boots once and re-initialises Foundation on turbo:load. %>
|
|
40
48
|
<%= javascript_include_tag 'popper.min' %>
|
|
41
49
|
<%= javascript_include_tag 'tippy-bundle.umd.min' %>
|
|
42
50
|
<%= javascript_include_tag 'inline_forms/inline_forms' %>
|
|
@@ -19,11 +19,13 @@ module InlineForms
|
|
|
19
19
|
end
|
|
20
20
|
|
|
21
21
|
@inline_forms_searchable = true
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
}.join(" OR ")
|
|
22
|
+
# The clause is built when the scope runs, not here: quoting needs a
|
|
23
|
+
# database connection, and the class body runs whenever the model
|
|
24
|
+
# loads (eager loading, assets:precompile without a database).
|
|
26
25
|
scope :inline_forms_search, ->(q) {
|
|
26
|
+
clause = cols.map { |c|
|
|
27
|
+
"#{connection.quote_table_name(table_name)}.#{connection.quote_column_name(c)} LIKE :q"
|
|
28
|
+
}.join(" OR ")
|
|
27
29
|
where(clause, q: "%#{sanitize_sql_like(q.to_s)}%")
|
|
28
30
|
}
|
|
29
31
|
end
|
data/lib/inline_forms/version.rb
CHANGED
|
@@ -1,10 +1,16 @@
|
|
|
1
1
|
en:
|
|
2
2
|
inline_forms:
|
|
3
|
-
|
|
4
|
-
|
|
3
|
+
# The generic list search (inline_forms/_generic_search), shown for
|
|
4
|
+
# models that declare inline_forms_search_on.
|
|
5
|
+
search:
|
|
6
|
+
placeholder: "search..."
|
|
7
|
+
submit: search
|
|
8
|
+
reset: reset
|
|
9
|
+
# general.application_title, devise.title_for_devise and devise.welcome
|
|
10
|
+
# are not defined here: without a host override the views use the
|
|
11
|
+
# host's application_name translation (InlineFormsHelper#inline_forms_application_title).
|
|
5
12
|
devise:
|
|
6
|
-
|
|
7
|
-
welcome: Welcome for Devise
|
|
13
|
+
welcome_to: "Welcome to %{name}!"
|
|
8
14
|
passwords:
|
|
9
15
|
forgot_your_password: Forgot Your Password?
|
|
10
16
|
change_your_password: Change your password, if you want!
|
|
@@ -152,3 +158,8 @@ en:
|
|
|
152
158
|
messages:
|
|
153
159
|
inline_forms_file_too_large: "is too large (maximum %{count} MB)"
|
|
154
160
|
inline_forms_file_missing: "no file chosen"
|
|
161
|
+
# Version and file timestamps (InlineFormsHelper#inline_forms_time and the
|
|
162
|
+
# file trash): rails-i18n's :short has no year.
|
|
163
|
+
time:
|
|
164
|
+
formats:
|
|
165
|
+
inline_forms: "%d %b %Y %H:%M"
|
|
@@ -1,10 +1,16 @@
|
|
|
1
1
|
nl:
|
|
2
2
|
inline_forms:
|
|
3
|
-
|
|
4
|
-
|
|
3
|
+
# The generic list search (inline_forms/_generic_search), shown for
|
|
4
|
+
# models that declare inline_forms_search_on.
|
|
5
|
+
search:
|
|
6
|
+
placeholder: "zoeken..."
|
|
7
|
+
submit: zoek
|
|
8
|
+
reset: wissen
|
|
9
|
+
# general.application_title, devise.title_for_devise and devise.welcome
|
|
10
|
+
# are not defined here: without a host override the views use the
|
|
11
|
+
# host's application_name translation (InlineFormsHelper#inline_forms_application_title).
|
|
5
12
|
devise:
|
|
6
|
-
|
|
7
|
-
welcome: Welcome for Devise
|
|
13
|
+
welcome_to: "Welkom bij %{name}!"
|
|
8
14
|
passwords:
|
|
9
15
|
forgot_your_password: Wachtwoord Vergeten?
|
|
10
16
|
change_your_password: Verander wachtwoord, als u wilt!
|
|
@@ -152,3 +158,8 @@ nl:
|
|
|
152
158
|
messages:
|
|
153
159
|
inline_forms_file_too_large: "is te groot (maximaal %{count} MB)"
|
|
154
160
|
inline_forms_file_missing: "geen bestand gekozen"
|
|
161
|
+
# Version and file timestamps (InlineFormsHelper#inline_forms_time and the
|
|
162
|
+
# file trash): rails-i18n's :short has no year.
|
|
163
|
+
time:
|
|
164
|
+
formats:
|
|
165
|
+
inline_forms: "%d %b %Y %H:%M"
|
|
@@ -19,6 +19,8 @@ class LocaleTest < InlineFormsIntegrationTestCase
|
|
|
19
19
|
inline_forms.view.version_events.update inline_forms.view.version_events.destroy
|
|
20
20
|
inline_forms.devise.login common.more common.logout inline_forms.view.create_failed
|
|
21
21
|
inline_forms.files.preview inline_forms.files.download_pdf
|
|
22
|
+
inline_forms.search.placeholder inline_forms.search.submit inline_forms.search.reset
|
|
23
|
+
inline_forms.devise.welcome_to time.formats.inline_forms
|
|
22
24
|
].freeze
|
|
23
25
|
|
|
24
26
|
setup do
|
|
@@ -107,7 +109,60 @@ class LocaleTest < InlineFormsIntegrationTestCase
|
|
|
107
109
|
end
|
|
108
110
|
refute_includes body, "<strong>Event</strong>"
|
|
109
111
|
version = @machine.versions.last
|
|
110
|
-
assert_includes body, ERB::Util.h(I18n.l(version.created_at, format: :
|
|
112
|
+
assert_includes body, ERB::Util.h(I18n.l(version.created_at, format: :inline_forms, locale: :nl))
|
|
113
|
+
end
|
|
114
|
+
|
|
115
|
+
# rails-i18n's :short ("%d %b %H:%M") has no year: edits years apart read
|
|
116
|
+
# the same next to Restore.
|
|
117
|
+
test "version timestamps show the year, and the exact time on hover" do
|
|
118
|
+
version = @machine.versions.last
|
|
119
|
+
version.update_columns(created_at: Time.zone.local(2019, 3, 5, 14, 7))
|
|
120
|
+
row = "machine_#{@machine.id}"
|
|
121
|
+
strictly(:nl) do
|
|
122
|
+
get list_versions_machine_path(@machine, update: row), headers: frame_headers(row)
|
|
123
|
+
end
|
|
124
|
+
|
|
125
|
+
assert_response :success
|
|
126
|
+
iso = version.reload.created_at.iso8601
|
|
127
|
+
assert_includes response.body, %(<time datetime="#{iso}" title="#{iso}">05 mrt 2019 14:07</time>)
|
|
128
|
+
end
|
|
129
|
+
|
|
130
|
+
test "the generic search box is translated" do
|
|
131
|
+
I18n.backend.store_translations(:en, widgets: "widgets")
|
|
132
|
+
I18n.backend.store_translations(:nl, widgets: "widgets")
|
|
133
|
+
{ en: [ "search...", "search", "reset" ], nl: [ "zoeken...", "zoek", "wissen" ] }.each do |locale, (placeholder, submit, reset)|
|
|
134
|
+
strictly(locale) { get widgets_path }
|
|
135
|
+
|
|
136
|
+
assert_response :success
|
|
137
|
+
assert_includes response.body, %(placeholder="#{placeholder}")
|
|
138
|
+
assert_includes response.body, %(value="#{submit}")
|
|
139
|
+
assert_includes response.body, %(title="#{reset}")
|
|
140
|
+
refute_includes response.body, "zoek op naam" if locale == :en
|
|
141
|
+
end
|
|
142
|
+
end
|
|
143
|
+
|
|
144
|
+
# The gem shipped "Application Title" / "Title for Devise" / "Welcome for
|
|
145
|
+
# Devise" in en and nl; any locale the host did not override showed them.
|
|
146
|
+
test "no placeholder titles: the gem's titles use the host's application name" do
|
|
147
|
+
%i[en nl].each do |locale|
|
|
148
|
+
%w[inline_forms.general.application_title inline_forms.devise.title_for_devise inline_forms.devise.welcome].each do |key|
|
|
149
|
+
refute I18n.exists?(key, locale), "#{locale}.#{key} is a placeholder; the views fall back to application_name"
|
|
150
|
+
end
|
|
151
|
+
end
|
|
152
|
+
|
|
153
|
+
get widgets_path
|
|
154
|
+
I18n.backend.store_translations(:nl, application_name: "Werkplaats")
|
|
155
|
+
strictly(:nl) do
|
|
156
|
+
devise = controller.render_to_string(template: "stats/show", layout: "devise")
|
|
157
|
+
assert_includes devise, "Werkplaats v#{InlineForms::VERSION}"
|
|
158
|
+
top_bar = controller.render_to_string(partial: "devise/sessions/top-bar")
|
|
159
|
+
assert_includes top_bar, "Werkplaats #{InlineForms::VERSION}"
|
|
160
|
+
assert_equal "Welkom bij Werkplaats!",
|
|
161
|
+
I18n.t("inline_forms.devise.welcome", default: I18n.t("inline_forms.devise.welcome_to", name: "Werkplaats"))
|
|
162
|
+
end
|
|
163
|
+
I18n.with_locale(:en) do
|
|
164
|
+
assert_includes controller.render_to_string(template: "stats/show", layout: "application"), "Dummy v#{InlineForms::VERSION}"
|
|
165
|
+
end
|
|
111
166
|
end
|
|
112
167
|
|
|
113
168
|
test "the versions list in en keeps its English labels" do
|
|
@@ -62,4 +62,22 @@ class ModelTopBarTest < InlineFormsIntegrationTestCase
|
|
|
62
62
|
refute_includes response.body, "new_button"
|
|
63
63
|
refute_includes response.body, %(id="input_search")
|
|
64
64
|
end
|
|
65
|
+
|
|
66
|
+
# The class body runs whenever the model loads (eager loading,
|
|
67
|
+
# assets:precompile with no database); quoting there needed a connection.
|
|
68
|
+
test "inline_forms_search_on needs no database connection until the search runs" do
|
|
69
|
+
model = Class.new(ApplicationRecord) do
|
|
70
|
+
self.table_name = "widgets"
|
|
71
|
+
def self.name = "OfflineWidget"
|
|
72
|
+
end
|
|
73
|
+
no_db = ->(*) { raise "database touched at declaration" }
|
|
74
|
+
%i[connection lease_connection with_connection].each { |m| model.define_singleton_method(m, &no_db) }
|
|
75
|
+
|
|
76
|
+
model.inline_forms_search_on :name
|
|
77
|
+
|
|
78
|
+
assert model.inline_forms_searchable?
|
|
79
|
+
%i[connection lease_connection with_connection].each { |m| model.singleton_class.remove_method(m) }
|
|
80
|
+
Widget.create!(name: "Gamma")
|
|
81
|
+
assert_equal [ "Gamma" ], model.inline_forms_search("amm").pluck(:name)
|
|
82
|
+
end
|
|
65
83
|
end
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require_relative "../integration_test_helper"
|
|
4
|
+
|
|
5
|
+
# Top-bar submenus after a Turbo Drive visit. Turbo replaced <body> and re-ran
|
|
6
|
+
# the inline_forms bundle (a fresh jQuery, its own Foundation plugin data),
|
|
7
|
+
# and until that finished the new body's submenus showed as plain lists;
|
|
8
|
+
# StProject, Scouting and Papiamentu each carried the same layout copy to
|
|
9
|
+
# hide them (html.no-js, a turbo:load re-init, a CSS rule). Now the gem does.
|
|
10
|
+
class TurboMenuTest < InlineFormsIntegrationTestCase
|
|
11
|
+
ROOT = File.expand_path("../..", __dir__)
|
|
12
|
+
|
|
13
|
+
test "the layout starts as no-js" do
|
|
14
|
+
get widgets_path
|
|
15
|
+
|
|
16
|
+
assert_response :success
|
|
17
|
+
assert_match %r{<html lang="en" class="no-js">}, response.body
|
|
18
|
+
# Not data-turbo-eval="false": a Turbo visit from a page whose layout
|
|
19
|
+
# lacks the bundle has to run it.
|
|
20
|
+
assert_match %r{<script src="[^"]*/inline_forms/inline_forms[^"]*\.js"></script>}, response.body
|
|
21
|
+
end
|
|
22
|
+
|
|
23
|
+
test "inline_forms.js boots once and re-initialises Foundation on turbo:load" do
|
|
24
|
+
js = File.read(File.join(ROOT, "app/assets/javascripts/inline_forms/inline_forms.js"))
|
|
25
|
+
boot = js[/if \(!window\.inlineFormsBooted\) \{.*?\n\}\n/m]
|
|
26
|
+
|
|
27
|
+
assert boot, "the DOM-ready and turbo:load wiring sits behind the once-guard"
|
|
28
|
+
turbo_load = boot[/addEventListener\("turbo:load".*?\n \}\);/m]
|
|
29
|
+
assert_includes turbo_load, "window.jQuery(document).foundation();"
|
|
30
|
+
assert_includes turbo_load, "initInlineFormsWidgets(document);"
|
|
31
|
+
end
|
|
32
|
+
|
|
33
|
+
test "uninitialised submenus stay hidden after a visit" do
|
|
34
|
+
scss = File.read(File.join(ROOT, "app/assets/stylesheets/inline_forms/inline_forms.scss"))
|
|
35
|
+
|
|
36
|
+
assert_match(/^\.dropdown\.menu > li > ul\.menu:not\(\.is-dropdown-submenu\) \{\n display: none;\n\}/, scss)
|
|
37
|
+
end
|
|
38
|
+
|
|
39
|
+
# Compiled CSS is checked in the example app (example_app_stylesheet_test).
|
|
40
|
+
test "tippy.css is inlined, not left as a plain CSS @import the browser 404s on" do
|
|
41
|
+
scss = File.read(File.join(ROOT, "app/assets/stylesheets/inline_forms/foundation_and_overrides.scss"))
|
|
42
|
+
|
|
43
|
+
refute_match(/^\s*@import\s+['"][^'"]*tippy\.css['"]/, scss)
|
|
44
|
+
assert_includes scss, "@include meta.load-css('../../../vendor/assets/stylesheets/tippy');"
|
|
45
|
+
end
|
|
46
|
+
end
|
metadata
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: inline_forms
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 8.1.
|
|
4
|
+
version: 8.1.66
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Ace Suares
|
|
@@ -334,6 +334,7 @@ files:
|
|
|
334
334
|
- test/integration/schema_batch_pipeline_test.rb
|
|
335
335
|
- test/integration/schema_edit_test.rb
|
|
336
336
|
- test/integration/schema_preview_test.rb
|
|
337
|
+
- test/integration/turbo_menu_test.rb
|
|
337
338
|
- test/integration_test_helper.rb
|
|
338
339
|
- test/plain_text_configuration_test.rb
|
|
339
340
|
- test/schema_apply_test.rb
|
|
@@ -424,6 +425,7 @@ test_files:
|
|
|
424
425
|
- test/integration/schema_batch_pipeline_test.rb
|
|
425
426
|
- test/integration/schema_edit_test.rb
|
|
426
427
|
- test/integration/schema_preview_test.rb
|
|
428
|
+
- test/integration/turbo_menu_test.rb
|
|
427
429
|
- test/integration_test_helper.rb
|
|
428
430
|
- test/plain_text_configuration_test.rb
|
|
429
431
|
- test/schema_apply_test.rb
|