bard-attachment_field 0.2.4 → 0.2.5
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/input-attachment.js +44 -74
- data/input-attachment/package.json +8 -2
- data/input-attachment/src/components/input-attachment/form-controller.tsx +30 -17
- data/lib/bard/attachment_field/cucumber.rb +16 -0
- data/lib/bard/attachment_field/version.rb +1 -1
- metadata +2 -2
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: e7ee47362d9328e10ce601c919f111a0e9ea5fe99545b699dd4bebac3afaeb62
|
|
4
|
+
data.tar.gz: '0803e538c1fdbb231dee995f69d0bcb071c765e6dfcd52d5e38f237a353bacd1'
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 7f285040a99291e3224ba2c3c8f8259e4a67ff8c6901f15356517752363706271d4f12765680151f523070ebd967429c4940f43d99a04b9d9b09fbf9e7d7be70
|
|
7
|
+
data.tar.gz: 6f0e372c7eb699efb282e71de7ff8156f7f7615d5cd82bba896697f961a78beeb5b72cf24adaf47ce58598d306879c2ce6c6cf1ca1cf619df16c14299bf2aacf
|
|
@@ -5070,9 +5070,6 @@ var FetchResponse = class {
|
|
|
5070
5070
|
get isTurboStream() {
|
|
5071
5071
|
return this.contentType.match(/^text\/vnd\.turbo-stream\.html/);
|
|
5072
5072
|
}
|
|
5073
|
-
get isScript() {
|
|
5074
|
-
return this.contentType.match(/\b(?:java|ecma)script\b/);
|
|
5075
|
-
}
|
|
5076
5073
|
async renderTurboStream() {
|
|
5077
5074
|
if (this.isTurboStream) {
|
|
5078
5075
|
if (window.Turbo) {
|
|
@@ -5084,22 +5081,6 @@ var FetchResponse = class {
|
|
|
5084
5081
|
return Promise.reject(new Error(`Expected a Turbo Stream response but got "${this.contentType}" instead`));
|
|
5085
5082
|
}
|
|
5086
5083
|
}
|
|
5087
|
-
async activeScript() {
|
|
5088
|
-
if (this.isScript) {
|
|
5089
|
-
const script = document.createElement("script");
|
|
5090
|
-
const metaTag = document.querySelector("meta[name=csp-nonce]");
|
|
5091
|
-
if (metaTag) {
|
|
5092
|
-
const nonce = metaTag.nonce === "" ? metaTag.content : metaTag.nonce;
|
|
5093
|
-
if (nonce) {
|
|
5094
|
-
script.setAttribute("nonce", nonce);
|
|
5095
|
-
}
|
|
5096
|
-
}
|
|
5097
|
-
script.innerHTML = await this.text;
|
|
5098
|
-
document.body.appendChild(script);
|
|
5099
|
-
} else {
|
|
5100
|
-
return Promise.reject(new Error(`Expected a Script response but got "${this.contentType}" instead`));
|
|
5101
|
-
}
|
|
5102
|
-
}
|
|
5103
5084
|
};
|
|
5104
5085
|
var RequestInterceptor = class {
|
|
5105
5086
|
static register(interceptor) {
|
|
@@ -5145,7 +5126,7 @@ function stringEntriesFromFormData(formData) {
|
|
|
5145
5126
|
function mergeEntries(searchParams, entries) {
|
|
5146
5127
|
for (const [name, value] of entries) {
|
|
5147
5128
|
if (value instanceof window.File) continue;
|
|
5148
|
-
if (searchParams.has(name)
|
|
5129
|
+
if (searchParams.has(name)) {
|
|
5149
5130
|
searchParams.delete(name);
|
|
5150
5131
|
searchParams.set(name, value);
|
|
5151
5132
|
} else {
|
|
@@ -5168,16 +5149,11 @@ var FetchRequest = class {
|
|
|
5168
5149
|
} catch (error) {
|
|
5169
5150
|
console.error(error);
|
|
5170
5151
|
}
|
|
5171
|
-
const
|
|
5172
|
-
const response = new FetchResponse(await fetch(this.url, this.fetchOptions));
|
|
5152
|
+
const response = new FetchResponse(await window.fetch(this.url, this.fetchOptions));
|
|
5173
5153
|
if (response.unauthenticated && response.authenticationURL) {
|
|
5174
5154
|
return Promise.reject(window.location.href = response.authenticationURL);
|
|
5175
5155
|
}
|
|
5176
|
-
if (response.
|
|
5177
|
-
await response.activeScript();
|
|
5178
|
-
}
|
|
5179
|
-
const responseStatusIsTurboStreamable = response.ok || response.unprocessableEntity;
|
|
5180
|
-
if (responseStatusIsTurboStreamable && response.isTurboStream) {
|
|
5156
|
+
if (response.ok && response.isTurboStream) {
|
|
5181
5157
|
await response.renderTurboStream();
|
|
5182
5158
|
}
|
|
5183
5159
|
return response;
|
|
@@ -5187,38 +5163,27 @@ var FetchRequest = class {
|
|
|
5187
5163
|
headers[key] = value;
|
|
5188
5164
|
this.options.headers = headers;
|
|
5189
5165
|
}
|
|
5190
|
-
sameHostname() {
|
|
5191
|
-
if (!this.originalUrl.startsWith("http:") && !this.originalUrl.startsWith("https:")) {
|
|
5192
|
-
return true;
|
|
5193
|
-
}
|
|
5194
|
-
try {
|
|
5195
|
-
return new URL(this.originalUrl).hostname === window.location.hostname;
|
|
5196
|
-
} catch (_) {
|
|
5197
|
-
return true;
|
|
5198
|
-
}
|
|
5199
|
-
}
|
|
5200
5166
|
get fetchOptions() {
|
|
5201
5167
|
return {
|
|
5202
5168
|
method: this.method.toUpperCase(),
|
|
5203
5169
|
headers: this.headers,
|
|
5204
5170
|
body: this.formattedBody,
|
|
5205
5171
|
signal: this.signal,
|
|
5206
|
-
credentials:
|
|
5207
|
-
redirect: this.redirect
|
|
5208
|
-
keepalive: this.keepalive
|
|
5172
|
+
credentials: "same-origin",
|
|
5173
|
+
redirect: this.redirect
|
|
5209
5174
|
};
|
|
5210
5175
|
}
|
|
5211
5176
|
get headers() {
|
|
5212
|
-
const baseHeaders = {
|
|
5213
|
-
"X-Requested-With": "XMLHttpRequest",
|
|
5214
|
-
"Content-Type": this.contentType,
|
|
5215
|
-
Accept: this.accept
|
|
5216
|
-
};
|
|
5217
|
-
if (this.sameHostname()) {
|
|
5218
|
-
baseHeaders["X-CSRF-Token"] = this.csrfToken;
|
|
5219
|
-
}
|
|
5220
5177
|
return compact(
|
|
5221
|
-
Object.assign(
|
|
5178
|
+
Object.assign(
|
|
5179
|
+
{
|
|
5180
|
+
"X-Requested-With": "XMLHttpRequest",
|
|
5181
|
+
"X-CSRF-Token": this.csrfToken,
|
|
5182
|
+
"Content-Type": this.contentType,
|
|
5183
|
+
Accept: this.accept
|
|
5184
|
+
},
|
|
5185
|
+
this.additionalHeaders
|
|
5186
|
+
)
|
|
5222
5187
|
);
|
|
5223
5188
|
}
|
|
5224
5189
|
get csrfToken() {
|
|
@@ -5242,8 +5207,6 @@ var FetchRequest = class {
|
|
|
5242
5207
|
return "text/vnd.turbo-stream.html, text/html, application/xhtml+xml";
|
|
5243
5208
|
case "json":
|
|
5244
5209
|
return "application/json, application/vnd.api+json";
|
|
5245
|
-
case "script":
|
|
5246
|
-
return "text/javascript, application/javascript";
|
|
5247
5210
|
default:
|
|
5248
5211
|
return "*/*";
|
|
5249
5212
|
}
|
|
@@ -5278,12 +5241,6 @@ var FetchRequest = class {
|
|
|
5278
5241
|
get redirect() {
|
|
5279
5242
|
return this.options.redirect || "follow";
|
|
5280
5243
|
}
|
|
5281
|
-
get credentials() {
|
|
5282
|
-
return this.options.credentials || "same-origin";
|
|
5283
|
-
}
|
|
5284
|
-
get keepalive() {
|
|
5285
|
-
return this.options.keepalive || false;
|
|
5286
|
-
}
|
|
5287
5244
|
get additionalHeaders() {
|
|
5288
5245
|
return this.options.headers || {};
|
|
5289
5246
|
}
|
|
@@ -5494,9 +5451,7 @@ var FormController = class _FormController {
|
|
|
5494
5451
|
</dialog>`);
|
|
5495
5452
|
this.dialog = this.element.querySelector("#form-controller-dialog");
|
|
5496
5453
|
this.progressContainerTarget = this.dialog.querySelector("#progress-container");
|
|
5497
|
-
|
|
5498
|
-
this.element.addEventListener("submit", (event) => this.submit(event));
|
|
5499
|
-
}
|
|
5454
|
+
this.element.addEventListener("submit", (event) => this.submit(event));
|
|
5500
5455
|
window.addEventListener("beforeunload", (event) => this.beforeUnload(event));
|
|
5501
5456
|
this.element.addEventListener("direct-upload:initialize", (event) => this.init(event));
|
|
5502
5457
|
this.element.addEventListener("direct-upload:start", (event) => this.start(event));
|
|
@@ -5512,10 +5467,11 @@ var FormController = class _FormController {
|
|
|
5512
5467
|
}
|
|
5513
5468
|
}
|
|
5514
5469
|
submit(event) {
|
|
5515
|
-
if (this.controllers.length === 0 && !this.hasUploadErrors())
|
|
5470
|
+
if (this.controllers.length === 0 && !this.hasUploadErrors() && !this.processing)
|
|
5516
5471
|
return;
|
|
5517
5472
|
event.preventDefault();
|
|
5518
5473
|
this.submitted = true;
|
|
5474
|
+
this.setInputAttachmentsDisabled(true);
|
|
5519
5475
|
this.startNextController();
|
|
5520
5476
|
if (this.processing) {
|
|
5521
5477
|
this.dialog.showModal();
|
|
@@ -5527,10 +5483,18 @@ var FormController = class _FormController {
|
|
|
5527
5483
|
const controller = this.controllers.shift();
|
|
5528
5484
|
if (controller) {
|
|
5529
5485
|
this.processing = true;
|
|
5530
|
-
this.
|
|
5486
|
+
if (this.submitted) {
|
|
5487
|
+
this.setInputAttachmentsDisabled(true);
|
|
5488
|
+
} else {
|
|
5489
|
+
this.setControllerInputDisabled(controller, true);
|
|
5490
|
+
}
|
|
5531
5491
|
controller.start((error) => {
|
|
5532
|
-
if (
|
|
5533
|
-
|
|
5492
|
+
if (this.submitted) {
|
|
5493
|
+
if (error) {
|
|
5494
|
+
this.setInputAttachmentsDisabled(false);
|
|
5495
|
+
}
|
|
5496
|
+
} else {
|
|
5497
|
+
this.setControllerInputDisabled(controller, false);
|
|
5534
5498
|
}
|
|
5535
5499
|
this.processing = false;
|
|
5536
5500
|
this.startNextController();
|
|
@@ -5543,16 +5507,22 @@ var FormController = class _FormController {
|
|
|
5543
5507
|
return Array.from(this.element.querySelectorAll("attachment-file")).some((el) => el.state === "error");
|
|
5544
5508
|
}
|
|
5545
5509
|
submitForm() {
|
|
5546
|
-
if (this.submitted)
|
|
5547
|
-
|
|
5548
|
-
|
|
5549
|
-
|
|
5550
|
-
|
|
5551
|
-
|
|
5552
|
-
|
|
5553
|
-
|
|
5554
|
-
|
|
5555
|
-
|
|
5510
|
+
if (!this.submitted)
|
|
5511
|
+
return;
|
|
5512
|
+
if (this.hasUploadErrors()) {
|
|
5513
|
+
this.dialog.close();
|
|
5514
|
+
this.setInputAttachmentsDisabled(false);
|
|
5515
|
+
return;
|
|
5516
|
+
}
|
|
5517
|
+
this.setInputAttachmentsDisabled(true);
|
|
5518
|
+
requestAnimationFrame(() => {
|
|
5519
|
+
this.element.submit();
|
|
5520
|
+
});
|
|
5521
|
+
}
|
|
5522
|
+
setControllerInputDisabled(controller, disabled) {
|
|
5523
|
+
const inputAttachment = controller.uploadedFile.closest("input-attachment");
|
|
5524
|
+
if (inputAttachment) {
|
|
5525
|
+
inputAttachment.disabled = disabled;
|
|
5556
5526
|
}
|
|
5557
5527
|
}
|
|
5558
5528
|
setInputAttachmentsDisabled(disabled) {
|
|
@@ -21,7 +21,10 @@
|
|
|
21
21
|
},
|
|
22
22
|
"jest": {
|
|
23
23
|
"testEnvironment": "jsdom",
|
|
24
|
-
"extensionsToTreatAsEsm": [
|
|
24
|
+
"extensionsToTreatAsEsm": [
|
|
25
|
+
".ts",
|
|
26
|
+
".tsx"
|
|
27
|
+
],
|
|
25
28
|
"globals": {
|
|
26
29
|
"ts-jest": {
|
|
27
30
|
"useESM": true
|
|
@@ -53,5 +56,8 @@
|
|
|
53
56
|
"import": "./dist/components/index.js"
|
|
54
57
|
}
|
|
55
58
|
},
|
|
56
|
-
"license": "MIT"
|
|
59
|
+
"license": "MIT",
|
|
60
|
+
"volta": {
|
|
61
|
+
"node": "22.22.1"
|
|
62
|
+
}
|
|
57
63
|
}
|
|
@@ -34,9 +34,7 @@ export default class FormController {
|
|
|
34
34
|
this.dialog = this.element.querySelector("#form-controller-dialog")
|
|
35
35
|
this.progressContainerTarget = this.dialog.querySelector("#progress-container")
|
|
36
36
|
|
|
37
|
-
|
|
38
|
-
this.element.addEventListener("submit", event => this.submit(event))
|
|
39
|
-
}
|
|
37
|
+
this.element.addEventListener("submit", event => this.submit(event))
|
|
40
38
|
window.addEventListener("beforeunload", event => this.beforeUnload(event))
|
|
41
39
|
|
|
42
40
|
this.element.addEventListener("direct-upload:initialize", event => this.init(event))
|
|
@@ -56,9 +54,10 @@ export default class FormController {
|
|
|
56
54
|
}
|
|
57
55
|
|
|
58
56
|
submit(event) {
|
|
59
|
-
if(this.controllers.length === 0 && !this.hasUploadErrors()) return
|
|
57
|
+
if(this.controllers.length === 0 && !this.hasUploadErrors() && !this.processing) return
|
|
60
58
|
event.preventDefault()
|
|
61
59
|
this.submitted = true
|
|
60
|
+
this.setInputAttachmentsDisabled(true)
|
|
62
61
|
this.startNextController()
|
|
63
62
|
if(this.processing) {
|
|
64
63
|
this.dialog.showModal()
|
|
@@ -71,10 +70,18 @@ export default class FormController {
|
|
|
71
70
|
const controller = this.controllers.shift()
|
|
72
71
|
if(controller) {
|
|
73
72
|
this.processing = true
|
|
74
|
-
this.
|
|
73
|
+
if (this.submitted) {
|
|
74
|
+
this.setInputAttachmentsDisabled(true)
|
|
75
|
+
} else {
|
|
76
|
+
this.setControllerInputDisabled(controller, true)
|
|
77
|
+
}
|
|
75
78
|
controller.start(error => {
|
|
76
|
-
if(
|
|
77
|
-
|
|
79
|
+
if (this.submitted) {
|
|
80
|
+
if(error) {
|
|
81
|
+
this.setInputAttachmentsDisabled(false)
|
|
82
|
+
}
|
|
83
|
+
} else {
|
|
84
|
+
this.setControllerInputDisabled(controller, false)
|
|
78
85
|
}
|
|
79
86
|
this.processing = false
|
|
80
87
|
this.startNextController()
|
|
@@ -90,16 +97,22 @@ export default class FormController {
|
|
|
90
97
|
}
|
|
91
98
|
|
|
92
99
|
submitForm() {
|
|
93
|
-
if(this.submitted)
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
100
|
+
if(!this.submitted) return
|
|
101
|
+
if(this.hasUploadErrors()) {
|
|
102
|
+
this.dialog.close()
|
|
103
|
+
this.setInputAttachmentsDisabled(false)
|
|
104
|
+
return
|
|
105
|
+
}
|
|
106
|
+
this.setInputAttachmentsDisabled(true)
|
|
107
|
+
requestAnimationFrame(() => { // run after pending rAF callbacks (e.g. updateFormValue)
|
|
108
|
+
this.element.submit()
|
|
109
|
+
})
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
setControllerInputDisabled(controller: DirectUploadController, disabled: boolean) {
|
|
113
|
+
const inputAttachment = (controller.uploadedFile as any).closest('input-attachment')
|
|
114
|
+
if (inputAttachment) {
|
|
115
|
+
inputAttachment.disabled = disabled
|
|
103
116
|
}
|
|
104
117
|
}
|
|
105
118
|
|
|
@@ -309,6 +309,22 @@ Then "the {string} attachment field should be disabled" do |field|
|
|
|
309
309
|
expect(is_disabled).to be true
|
|
310
310
|
end
|
|
311
311
|
|
|
312
|
+
Then "the {string} attachment field should not be disabled" do |field|
|
|
313
|
+
element = Bard::AttachmentField::TestHelper.find_field(page, field)
|
|
314
|
+
|
|
315
|
+
is_disabled = page.evaluate_script(<<~JS)
|
|
316
|
+
(function() {
|
|
317
|
+
const element = document.getElementById('#{element[:id]}');
|
|
318
|
+
if (element.hasAttribute('disabled')) return true;
|
|
319
|
+
if (element.closest('fieldset[disabled]')) return true;
|
|
320
|
+
const fileInput = element.shadowRoot?.querySelector('input[type="file"]');
|
|
321
|
+
return fileInput?.disabled || false;
|
|
322
|
+
})()
|
|
323
|
+
JS
|
|
324
|
+
|
|
325
|
+
expect(is_disabled).to be false
|
|
326
|
+
end
|
|
327
|
+
|
|
312
328
|
Then "the {string} attachment field should have a validation error containing {string}" do |field, message|
|
|
313
329
|
element = Bard::AttachmentField::TestHelper.find_field(page, field)
|
|
314
330
|
messages = Bard::AttachmentField::TestHelper.validation_messages(page, element)
|
metadata
CHANGED
|
@@ -1,14 +1,14 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: bard-attachment_field
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 0.2.
|
|
4
|
+
version: 0.2.5
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Micah Geisel
|
|
8
8
|
autorequire:
|
|
9
9
|
bindir: exe
|
|
10
10
|
cert_chain: []
|
|
11
|
-
date: 2026-03-
|
|
11
|
+
date: 2026-03-14 00:00:00.000000000 Z
|
|
12
12
|
dependencies:
|
|
13
13
|
- !ruby/object:Gem::Dependency
|
|
14
14
|
name: activestorage
|