yummy-guide-generic-administrate 0.8.18 → 0.8.19
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/app/assets/javascripts/yummy_guide_administrate/tooltips.js +114 -10
- data/app/assets/stylesheets/yummy_guide_administrate/_tooltips.scss +67 -0
- data/app/helpers/yummy_guide/administrate/integration_transition_helper.rb +37 -0
- data/lib/yummy_guide/administrate/engine.rb +1 -0
- data/lib/yummy_guide/administrate/version.rb +1 -1
- data/spec/yummy_guide/administrate/integration_transition_helper_spec.rb +28 -0
- data/spec/yummy_guide/administrate/tooltip_asset_spec.rb +49 -4
- 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: c85a364e43e99ea2fcb9440143675804196ced1b1d46b211cd202e1664232581
|
|
4
|
+
data.tar.gz: e6974ae9c103179acfc9fe77347336dac3feae4e305f1008dbc2ebd18eb1bd26
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: bddc130440fc835aabeca11cc79dafcaaecfcd6f06ab4c37a18318b618a4d20b835436e5f766adb4f31a4e25f391a447dbdce40af0e2694b2101f426f2153109
|
|
7
|
+
data.tar.gz: 87927272596c5bbb430f78982a5ba8d4e20626c88b907fa14613708a49de8d4dc357d8fe4c8160719f00a8e3d159aaea74dee74678aa1c22f5c41430fbadb117
|
|
@@ -6,10 +6,13 @@
|
|
|
6
6
|
var MOBILE_MEDIA_QUERY = "(max-width: 767px)";
|
|
7
7
|
var EDGE_GAP = 8;
|
|
8
8
|
var OFFSET = 8;
|
|
9
|
+
var WARNING_HOVER_HIDE_DELAY = 300;
|
|
9
10
|
var activeTrigger = null;
|
|
10
11
|
var hoveredTrigger = null;
|
|
11
12
|
var focusedTrigger = null;
|
|
13
|
+
var pinnedTrigger = null;
|
|
12
14
|
var tooltipElement = null;
|
|
15
|
+
var warningHideTimer = null;
|
|
13
16
|
|
|
14
17
|
function isMobile() {
|
|
15
18
|
return window.matchMedia && window.matchMedia(MOBILE_MEDIA_QUERY).matches;
|
|
@@ -21,6 +24,39 @@
|
|
|
21
24
|
return target.closest(TRIGGER_SELECTOR);
|
|
22
25
|
}
|
|
23
26
|
|
|
27
|
+
function warningTrigger(trigger) {
|
|
28
|
+
return trigger && trigger.getAttribute("data-admin-tooltip-variant") === "warning";
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
function interactiveTooltipTarget(target) {
|
|
32
|
+
return tooltipElement &&
|
|
33
|
+
tooltipElement.classList.contains("admin-tooltip--warning") &&
|
|
34
|
+
target &&
|
|
35
|
+
tooltipElement.contains(target);
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
function clearWarningHideTimer() {
|
|
39
|
+
if (!warningHideTimer) return;
|
|
40
|
+
|
|
41
|
+
window.clearTimeout(warningHideTimer);
|
|
42
|
+
warningHideTimer = null;
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
function hideTooltipAfterPointerLeave(trigger) {
|
|
46
|
+
if (!warningTrigger(trigger)) {
|
|
47
|
+
hideTooltip(trigger);
|
|
48
|
+
return;
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
clearWarningHideTimer();
|
|
52
|
+
warningHideTimer = window.setTimeout(function() {
|
|
53
|
+
warningHideTimer = null;
|
|
54
|
+
if (activeTrigger !== trigger || focusedTrigger === trigger || pinnedTrigger === trigger) return;
|
|
55
|
+
|
|
56
|
+
hideTooltip(trigger);
|
|
57
|
+
}, WARNING_HOVER_HIDE_DELAY);
|
|
58
|
+
}
|
|
59
|
+
|
|
24
60
|
function clamp(value, min, max) {
|
|
25
61
|
return Math.min(Math.max(value, min), max);
|
|
26
62
|
}
|
|
@@ -37,12 +73,23 @@
|
|
|
37
73
|
return tooltipElement;
|
|
38
74
|
}
|
|
39
75
|
|
|
76
|
+
function templateContentHtml(template) {
|
|
77
|
+
var html = template.innerHTML;
|
|
78
|
+
if (html) return html;
|
|
79
|
+
|
|
80
|
+
// React は <template> の子要素を template.content ではなく直接配置するため、
|
|
81
|
+
// innerHTML が空の場合は直接の子ノードから本文を復元する。
|
|
82
|
+
return Array.prototype.map.call(template.childNodes, function(node) {
|
|
83
|
+
return node.outerHTML || node.textContent || "";
|
|
84
|
+
}).join("");
|
|
85
|
+
}
|
|
86
|
+
|
|
40
87
|
function tooltipContent(trigger) {
|
|
41
88
|
var contentId = trigger.getAttribute("data-admin-tooltip-content-id");
|
|
42
89
|
var template = contentId ? document.getElementById(contentId) : null;
|
|
43
90
|
if (template) {
|
|
44
91
|
return {
|
|
45
|
-
html: template
|
|
92
|
+
html: templateContentHtml(template),
|
|
46
93
|
text: ""
|
|
47
94
|
};
|
|
48
95
|
}
|
|
@@ -59,7 +106,14 @@
|
|
|
59
106
|
|
|
60
107
|
trigger.setAttribute("aria-expanded", expanded ? "true" : "false");
|
|
61
108
|
|
|
62
|
-
if (
|
|
109
|
+
if (warningTrigger(trigger)) {
|
|
110
|
+
trigger.removeAttribute("aria-describedby");
|
|
111
|
+
if (expanded) {
|
|
112
|
+
trigger.setAttribute("aria-controls", TOOLTIP_ID);
|
|
113
|
+
} else {
|
|
114
|
+
trigger.removeAttribute("aria-controls");
|
|
115
|
+
}
|
|
116
|
+
} else if (expanded) {
|
|
63
117
|
trigger.setAttribute("aria-describedby", TOOLTIP_ID);
|
|
64
118
|
} else {
|
|
65
119
|
trigger.removeAttribute("aria-describedby");
|
|
@@ -106,8 +160,19 @@
|
|
|
106
160
|
hideTooltip();
|
|
107
161
|
}
|
|
108
162
|
|
|
163
|
+
clearWarningHideTimer();
|
|
109
164
|
activeTrigger = trigger;
|
|
110
165
|
tooltipElement = getTooltipElement();
|
|
166
|
+
tooltipElement.classList.toggle(
|
|
167
|
+
"admin-tooltip--warning",
|
|
168
|
+
warningTrigger(trigger)
|
|
169
|
+
);
|
|
170
|
+
tooltipElement.setAttribute("role", warningTrigger(trigger) ? "dialog" : "tooltip");
|
|
171
|
+
if (warningTrigger(trigger)) {
|
|
172
|
+
tooltipElement.setAttribute("aria-label", trigger.getAttribute("aria-label") || "Details");
|
|
173
|
+
} else {
|
|
174
|
+
tooltipElement.removeAttribute("aria-label");
|
|
175
|
+
}
|
|
111
176
|
if (content.html) {
|
|
112
177
|
tooltipElement.innerHTML = content.html;
|
|
113
178
|
} else {
|
|
@@ -126,6 +191,7 @@
|
|
|
126
191
|
function hideTooltip(trigger) {
|
|
127
192
|
if (trigger && activeTrigger !== trigger) return;
|
|
128
193
|
|
|
194
|
+
clearWarningHideTimer();
|
|
129
195
|
setTriggerState(activeTrigger, false);
|
|
130
196
|
|
|
131
197
|
if (tooltipElement) {
|
|
@@ -133,14 +199,16 @@
|
|
|
133
199
|
}
|
|
134
200
|
|
|
135
201
|
activeTrigger = null;
|
|
202
|
+
pinnedTrigger = null;
|
|
136
203
|
}
|
|
137
204
|
|
|
138
205
|
function toggleTooltip(trigger) {
|
|
139
|
-
if (
|
|
206
|
+
if (pinnedTrigger === trigger && tooltipElement && tooltipElement.classList.contains(VISIBLE_CLASS)) {
|
|
140
207
|
hideTooltip(trigger);
|
|
141
208
|
return;
|
|
142
209
|
}
|
|
143
210
|
|
|
211
|
+
pinnedTrigger = trigger;
|
|
144
212
|
showTooltip(trigger);
|
|
145
213
|
}
|
|
146
214
|
|
|
@@ -148,23 +216,47 @@
|
|
|
148
216
|
var trigger = closestTrigger(event.target);
|
|
149
217
|
if (!trigger || isMobile() || (event.relatedTarget && trigger.contains(event.relatedTarget))) return;
|
|
150
218
|
|
|
219
|
+
clearWarningHideTimer();
|
|
151
220
|
hoveredTrigger = trigger;
|
|
152
221
|
showTooltip(trigger);
|
|
153
222
|
});
|
|
154
223
|
|
|
224
|
+
document.addEventListener("mouseover", function(event) {
|
|
225
|
+
if (interactiveTooltipTarget(event.target)) {
|
|
226
|
+
clearWarningHideTimer();
|
|
227
|
+
}
|
|
228
|
+
});
|
|
229
|
+
|
|
155
230
|
document.addEventListener("mouseout", function(event) {
|
|
156
231
|
var trigger = closestTrigger(event.target);
|
|
157
|
-
if (!trigger || isMobile() || (event.relatedTarget && trigger.contains(event.relatedTarget))) return;
|
|
232
|
+
if (!trigger || isMobile() || (event.relatedTarget && (trigger.contains(event.relatedTarget) || interactiveTooltipTarget(event.relatedTarget)))) return;
|
|
158
233
|
|
|
159
234
|
if (hoveredTrigger === trigger) {
|
|
160
235
|
hoveredTrigger = null;
|
|
161
236
|
}
|
|
162
|
-
if (focusedTrigger === trigger) return;
|
|
237
|
+
if (focusedTrigger === trigger || pinnedTrigger === trigger) return;
|
|
163
238
|
|
|
164
|
-
|
|
239
|
+
hideTooltipAfterPointerLeave(trigger);
|
|
240
|
+
});
|
|
241
|
+
|
|
242
|
+
document.addEventListener("mouseout", function(event) {
|
|
243
|
+
if (!interactiveTooltipTarget(event.target) || interactiveTooltipTarget(event.relatedTarget)) return;
|
|
244
|
+
if (activeTrigger && event.relatedTarget && activeTrigger.contains(event.relatedTarget)) return;
|
|
245
|
+
|
|
246
|
+
if (hoveredTrigger === activeTrigger) {
|
|
247
|
+
hoveredTrigger = null;
|
|
248
|
+
}
|
|
249
|
+
if (focusedTrigger === activeTrigger || pinnedTrigger === activeTrigger) return;
|
|
250
|
+
|
|
251
|
+
hideTooltipAfterPointerLeave(activeTrigger);
|
|
165
252
|
});
|
|
166
253
|
|
|
167
254
|
document.addEventListener("focusin", function(event) {
|
|
255
|
+
if (interactiveTooltipTarget(event.target)) {
|
|
256
|
+
clearWarningHideTimer();
|
|
257
|
+
return;
|
|
258
|
+
}
|
|
259
|
+
|
|
168
260
|
var trigger = closestTrigger(event.target);
|
|
169
261
|
if (!trigger || isMobile()) return;
|
|
170
262
|
|
|
@@ -179,24 +271,36 @@
|
|
|
179
271
|
if (focusedTrigger === trigger) {
|
|
180
272
|
focusedTrigger = null;
|
|
181
273
|
}
|
|
182
|
-
if (
|
|
274
|
+
if (warningTrigger(trigger) && interactiveTooltipTarget(event.relatedTarget)) {
|
|
275
|
+
clearWarningHideTimer();
|
|
276
|
+
return;
|
|
277
|
+
}
|
|
278
|
+
if (hoveredTrigger === trigger || pinnedTrigger === trigger) return;
|
|
183
279
|
|
|
184
280
|
hideTooltip(trigger);
|
|
185
281
|
});
|
|
186
282
|
|
|
283
|
+
document.addEventListener("focusout", function(event) {
|
|
284
|
+
if (!interactiveTooltipTarget(event.target)) return;
|
|
285
|
+
if (interactiveTooltipTarget(event.relatedTarget)) return;
|
|
286
|
+
if (activeTrigger && event.relatedTarget && (activeTrigger === event.relatedTarget || activeTrigger.contains(event.relatedTarget))) return;
|
|
287
|
+
|
|
288
|
+
hideTooltip();
|
|
289
|
+
});
|
|
290
|
+
|
|
187
291
|
document.addEventListener("click", function(event) {
|
|
188
292
|
var trigger = closestTrigger(event.target);
|
|
189
293
|
|
|
190
294
|
if (trigger) {
|
|
191
|
-
if (!isMobile()) return;
|
|
192
|
-
|
|
193
295
|
event.preventDefault();
|
|
194
296
|
event.stopPropagation();
|
|
195
297
|
toggleTooltip(trigger);
|
|
196
298
|
return;
|
|
197
299
|
}
|
|
198
300
|
|
|
199
|
-
if (
|
|
301
|
+
if (interactiveTooltipTarget(event.target)) return;
|
|
302
|
+
|
|
303
|
+
if (activeTrigger) {
|
|
200
304
|
hideTooltip();
|
|
201
305
|
}
|
|
202
306
|
});
|
|
@@ -55,6 +55,73 @@
|
|
|
55
55
|
transform: translateY(0);
|
|
56
56
|
}
|
|
57
57
|
|
|
58
|
+
.admin-tooltip--warning {
|
|
59
|
+
max-width: 360px;
|
|
60
|
+
padding: 0;
|
|
61
|
+
border: 1px solid #fedf89;
|
|
62
|
+
border-left: 4px solid #f79009;
|
|
63
|
+
background: #fff;
|
|
64
|
+
color: #344054;
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
.admin-tooltip--warning.admin-tooltip--visible {
|
|
68
|
+
pointer-events: auto;
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
.admin-tooltip--warning::before {
|
|
72
|
+
background: #fff;
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
.admin-tooltip__warning-card {
|
|
76
|
+
padding: 0.75rem;
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
.admin-tooltip__warning-title {
|
|
80
|
+
display: block;
|
|
81
|
+
color: #7a2e0e;
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
.admin-tooltip__warning-message {
|
|
85
|
+
margin: 0.35rem 0 0.75rem;
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
.admin-tooltip__warning-details {
|
|
89
|
+
display: grid;
|
|
90
|
+
gap: 0.5rem;
|
|
91
|
+
margin: 0;
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
.admin-tooltip__warning-details > div {
|
|
95
|
+
display: grid;
|
|
96
|
+
grid-template-columns: minmax(0, 1fr) auto;
|
|
97
|
+
align-items: baseline;
|
|
98
|
+
gap: 0.75rem;
|
|
99
|
+
padding-top: 0.5rem;
|
|
100
|
+
border-top: 1px solid #eaecf0;
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
.admin-tooltip__warning-details dt {
|
|
104
|
+
color: #667085;
|
|
105
|
+
font-weight: 600;
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
.admin-tooltip__warning-details dd {
|
|
109
|
+
margin: 0;
|
|
110
|
+
color: #344054;
|
|
111
|
+
text-align: right;
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
.admin-tooltip__warning-details dt,
|
|
115
|
+
.admin-tooltip__warning-details dd {
|
|
116
|
+
line-height: 1.45;
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
.admin-tooltip__warning-actions {
|
|
120
|
+
display: flex;
|
|
121
|
+
justify-content: flex-end;
|
|
122
|
+
margin-top: 0.75rem;
|
|
123
|
+
}
|
|
124
|
+
|
|
58
125
|
.admin-tooltip::before {
|
|
59
126
|
content: "";
|
|
60
127
|
position: absolute;
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module YummyGuide
|
|
4
|
+
module Administrate
|
|
5
|
+
module IntegrationTransitionHelper
|
|
6
|
+
# 外部連携の非同期遷移を、製品固有の文言を渡して共通表示する。
|
|
7
|
+
def integration_transition_notice(message:, state:, details: nil)
|
|
8
|
+
warning = state.to_s == 'warning'
|
|
9
|
+
color = (warning || state.to_s == 'failed') ? '#b42318' : '#175cd3'
|
|
10
|
+
background = warning ? '#fef3f2' : '#f8fafc'
|
|
11
|
+
border = warning ? '#fecdca' : color
|
|
12
|
+
icon = if warning
|
|
13
|
+
content_tag(
|
|
14
|
+
:span,
|
|
15
|
+
'⚠',
|
|
16
|
+
class: 'integration-transition-notice__caution-icon',
|
|
17
|
+
aria: { hidden: true },
|
|
18
|
+
style: "color: #{color}; font-size: 20px; line-height: 1;",
|
|
19
|
+
)
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
content_tag(
|
|
23
|
+
:div,
|
|
24
|
+
role: 'status',
|
|
25
|
+
class: "integration-transition-notice integration-transition-notice--#{state}",
|
|
26
|
+
data: { integration_transition_state: state },
|
|
27
|
+
style: "display: flex; gap: 8px; margin: 12px 0; padding: 12px; border: 1px solid #{border}; border-left: 4px solid #{color}; background: #{background}; color: #{color};",
|
|
28
|
+
) do
|
|
29
|
+
safe_join([
|
|
30
|
+
icon,
|
|
31
|
+
content_tag(:div, safe_join([content_tag(:strong, message), details.present? ? content_tag(:div, details, style: 'margin-top: 4px;') : nil].compact)),
|
|
32
|
+
].compact)
|
|
33
|
+
end
|
|
34
|
+
end
|
|
35
|
+
end
|
|
36
|
+
end
|
|
37
|
+
end
|
|
@@ -27,6 +27,7 @@ module YummyGuide
|
|
|
27
27
|
::Administrate::ApplicationController.helper YummyGuide::Administrate::FilterFormHelper
|
|
28
28
|
::Administrate::ApplicationController.helper YummyGuide::Administrate::FilterControlsHelper
|
|
29
29
|
::Administrate::ApplicationController.helper YummyGuide::Administrate::TooltipHelper
|
|
30
|
+
::Administrate::ApplicationController.helper YummyGuide::Administrate::IntegrationTransitionHelper
|
|
30
31
|
end
|
|
31
32
|
end
|
|
32
33
|
end
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require "spec_helper"
|
|
4
|
+
|
|
5
|
+
RSpec.describe YummyGuide::Administrate::IntegrationTransitionHelper do
|
|
6
|
+
let(:view) { ActionView::Base.empty }
|
|
7
|
+
|
|
8
|
+
before { view.extend(described_class) }
|
|
9
|
+
|
|
10
|
+
# 製品固有の非同期連携状態を共通のアクセシブルな通知として表示すること
|
|
11
|
+
it "renders a status notice with the supplied message and details" do
|
|
12
|
+
html = view.integration_transition_notice(message: "処理中です", state: "processing", details: "対象 Assign: 2件")
|
|
13
|
+
|
|
14
|
+
expect(html).to include('role="status"')
|
|
15
|
+
expect(html).to include("処理中です")
|
|
16
|
+
expect(html).to include("対象 Assign: 2件")
|
|
17
|
+
end
|
|
18
|
+
|
|
19
|
+
# 紐付け設定が必要な警告は赤色カードとcautionアイコンで強調すること
|
|
20
|
+
it "renders a warning notice as a caution card" do
|
|
21
|
+
html = view.integration_transition_notice(message: "JobOption の紐付け設定が必要", state: "warning")
|
|
22
|
+
|
|
23
|
+
expect(html).to include("integration-transition-notice--warning")
|
|
24
|
+
expect(html).to include('data-integration-transition-state="warning"')
|
|
25
|
+
expect(html).to include("integration-transition-notice__caution-icon")
|
|
26
|
+
expect(html).to include("#fef3f2")
|
|
27
|
+
end
|
|
28
|
+
end
|
|
@@ -22,31 +22,76 @@ RSpec.describe "tooltip assets" do
|
|
|
22
22
|
expect(javascript_source).to include("showTooltip(trigger)")
|
|
23
23
|
end
|
|
24
24
|
|
|
25
|
-
#
|
|
26
|
-
it "supports
|
|
25
|
+
# 画面幅にかかわらずクリックで tooltip 表示を固定・トグルできることを静的に確認する
|
|
26
|
+
it "supports click toggling on every screen size" do
|
|
27
27
|
expect(javascript_source).to include('MOBILE_MEDIA_QUERY = "(max-width: 767px)"')
|
|
28
28
|
expect(javascript_source).to include("window.matchMedia")
|
|
29
29
|
expect(javascript_source).to include('document.addEventListener("click"')
|
|
30
30
|
expect(javascript_source).to include("toggleTooltip(trigger)")
|
|
31
|
+
expect(javascript_source).to include("pinnedTrigger = trigger")
|
|
31
32
|
expect(javascript_source).to include("event.stopPropagation()")
|
|
32
33
|
end
|
|
33
34
|
|
|
34
35
|
# Esc / 外側クリック / resize で表示中 tooltip を閉じられることを静的に確認する
|
|
35
36
|
it "closes the active tooltip from common dismissal events" do
|
|
36
37
|
expect(javascript_source).to include('event.key !== "Escape"')
|
|
37
|
-
expect(javascript_source).to include("if (activeTrigger
|
|
38
|
+
expect(javascript_source).to include("if (activeTrigger)")
|
|
38
39
|
expect(javascript_source).to include('window.addEventListener("resize"')
|
|
39
40
|
expect(javascript_source).to include("hideTooltip()")
|
|
40
41
|
end
|
|
41
42
|
|
|
42
|
-
#
|
|
43
|
+
# ERBのtemplate本文とReactが直接配置する子要素の両方をHTML本文としてtooltipへ描画することを静的に確認する
|
|
43
44
|
it "renders template content for block tooltip bodies" do
|
|
44
45
|
expect(javascript_source).to include('getAttribute("data-admin-tooltip-content-id")')
|
|
45
46
|
expect(javascript_source).to include("template.innerHTML")
|
|
47
|
+
expect(javascript_source).to include("templateContentHtml")
|
|
48
|
+
expect(javascript_source).to include("Array.prototype.map.call(template.childNodes")
|
|
49
|
+
expect(javascript_source).to include("node.outerHTML || node.textContent")
|
|
46
50
|
expect(javascript_source).to include("tooltipElement.innerHTML = content.html")
|
|
47
51
|
expect(javascript_source).to include("tooltipElement.textContent = content.text")
|
|
48
52
|
end
|
|
49
53
|
|
|
54
|
+
# warning variantでは白背景の項目別ポップオーバーを表示できることを静的に確認する
|
|
55
|
+
it "supports warning card tooltip variants" do
|
|
56
|
+
expect(javascript_source).to include('data-admin-tooltip-variant')
|
|
57
|
+
expect(javascript_source).to include('"admin-tooltip--warning"')
|
|
58
|
+
expect(stylesheet_source).to include('.admin-tooltip--warning')
|
|
59
|
+
expect(stylesheet_source).to include('background: #fff')
|
|
60
|
+
expect(stylesheet_source).to include('.admin-tooltip__warning-details')
|
|
61
|
+
expect(stylesheet_source).to include('align-items: baseline')
|
|
62
|
+
expect(stylesheet_source).to match(/\.admin-tooltip--warning\.admin-tooltip--visible\s*\{[^}]*pointer-events: auto;/m)
|
|
63
|
+
end
|
|
64
|
+
|
|
65
|
+
# warning cardはポインターをカード上へ移動しても閉じず、操作要素をクリックできることを静的に確認する
|
|
66
|
+
it "keeps interactive warning cards open while they are hovered" do
|
|
67
|
+
expect(javascript_source).to include('interactiveTooltipTarget(event.relatedTarget)')
|
|
68
|
+
expect(javascript_source).to include('if (interactiveTooltipTarget(event.target)) return')
|
|
69
|
+
expect(javascript_source).to include('WARNING_HOVER_HIDE_DELAY = 300')
|
|
70
|
+
expect(javascript_source).to include('clearWarningHideTimer()')
|
|
71
|
+
expect(javascript_source).to include('hideTooltipAfterPointerLeave(trigger)')
|
|
72
|
+
expect(javascript_source).to include('"dialog"')
|
|
73
|
+
end
|
|
74
|
+
|
|
75
|
+
# warning cardを閉じた後は透明な要素が背後の操作を妨げないことを静的に確認する
|
|
76
|
+
it "enables pointer events only while warning cards are visible" do
|
|
77
|
+
expect(stylesheet_source).to include('.admin-tooltip--warning.admin-tooltip--visible')
|
|
78
|
+
expect(stylesheet_source).to include('pointer-events: none')
|
|
79
|
+
end
|
|
80
|
+
|
|
81
|
+
# triggerとwarning card間のキーボードフォーカス移動では閉じず、外部への移動で閉じることを静的に確認する
|
|
82
|
+
it "keeps warning cards open while focus moves within the interactive card" do
|
|
83
|
+
expect(javascript_source).to include('warningTrigger(trigger) && interactiveTooltipTarget(event.relatedTarget)')
|
|
84
|
+
expect(javascript_source).to include('activeTrigger === event.relatedTarget || activeTrigger.contains(event.relatedTarget)')
|
|
85
|
+
expect(javascript_source).to include('if (!interactiveTooltipTarget(event.target)) return;')
|
|
86
|
+
expect(javascript_source).to include('hideTooltip();')
|
|
87
|
+
end
|
|
88
|
+
|
|
89
|
+
# warning cardの確認操作を詳細項目の右下へ配置するスタイルを持つことを確認する
|
|
90
|
+
it "right-aligns warning card actions" do
|
|
91
|
+
expect(stylesheet_source).to include('.admin-tooltip__warning-actions')
|
|
92
|
+
expect(stylesheet_source).to include('justify-content: flex-end')
|
|
93
|
+
end
|
|
94
|
+
|
|
50
95
|
# tooltip の吹き出しと表示状態に必要な CSS class が定義されることを静的に確認する
|
|
51
96
|
it "defines tooltip trigger and bubble styles" do
|
|
52
97
|
expect(stylesheet_source).to include(".admin-tooltip-trigger")
|
metadata
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: yummy-guide-generic-administrate
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 0.8.
|
|
4
|
+
version: 0.8.19
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- akatsuki-kk
|
|
@@ -118,6 +118,7 @@ files:
|
|
|
118
118
|
- app/helpers/yummy_guide/administrate/datetime_input_helper.rb
|
|
119
119
|
- app/helpers/yummy_guide/administrate/filter_controls_helper.rb
|
|
120
120
|
- app/helpers/yummy_guide/administrate/filter_form_helper.rb
|
|
121
|
+
- app/helpers/yummy_guide/administrate/integration_transition_helper.rb
|
|
121
122
|
- app/helpers/yummy_guide/administrate/number_input_helper.rb
|
|
122
123
|
- app/helpers/yummy_guide/administrate/tooltip_helper.rb
|
|
123
124
|
- app/views/administrate/application/_header_pagination.html.erb
|
|
@@ -161,6 +162,7 @@ files:
|
|
|
161
162
|
- spec/yummy_guide/administrate/filter_form_asset_spec.rb
|
|
162
163
|
- spec/yummy_guide/administrate/filter_form_helper_spec.rb
|
|
163
164
|
- spec/yummy_guide/administrate/filters_spec.rb
|
|
165
|
+
- spec/yummy_guide/administrate/integration_transition_helper_spec.rb
|
|
164
166
|
- spec/yummy_guide/administrate/number_input_helper_spec.rb
|
|
165
167
|
- spec/yummy_guide/administrate/tooltip_asset_spec.rb
|
|
166
168
|
- spec/yummy_guide/administrate/tooltip_helper_spec.rb
|