view_component-form 0.2.6 → 0.2.8

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 128d4c769f1fa3e25cf83830db0b8279413dd8da3e9c7e312aeee06f0456336a
4
- data.tar.gz: 63d592e4fa33d6bb77262bea0526761d5b251f26f37c6fac42a771f2506b3250
3
+ metadata.gz: ebfe18c2f32926684dc1bc0a96b4711bf56fe8e30ecf6a76cd51ee88228a33f9
4
+ data.tar.gz: 4220a56314d7955107d799b7fe40a364d9375c95d4b6f8cc90f4e3f985e11d26
5
5
  SHA512:
6
- metadata.gz: f584160cf5560bf80869e4b98ea86d4ac0ce35b25c97dcbb5f38d4fbbacddfce7c64734697327d500b36e537391ac5c34f81470491b2ed75a3347e6664b53b08
7
- data.tar.gz: 9fd72c0345d99a0207c006d916cf7e4f75224a88b4bc0175d3f56ffad491ebc42da14efa3619796896c12d37a8eef88dbd70911f47700eaa5fe520ea9f1277e0
6
+ metadata.gz: 311fd483187bebdb51d8f8a2f2358f5d9076332510cf608228eecd7988fea9c5676a3c605ed2857587682fac725036eab679e334f6c28b9059a66e7d0fb1cf3f
7
+ data.tar.gz: 1f498dc61138469a5e0797993db96f5ba32cd3bc6eec8403542cdd152b4261b371d04271dcba6675d424153eb14896044ace534bbb359ed764e4f640639f699a
data/CHANGELOG.md CHANGED
@@ -5,6 +5,21 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
5
5
  and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
6
6
 
7
7
  ## [Unreleased]
8
+ Nothing yet
9
+
10
+ ## [0.2.8] - 2024-08-20
11
+ ### Added
12
+ - Support for Rails 7.2 (#168)
13
+
14
+ ## [0.2.7] - 2024-07-18
15
+ ### Added
16
+ - Added parent_component configuration for field components (#160)
17
+ - Added Ruby 3.3 support (#164)
18
+ - Add `lookup_chain` customizability (#162)
19
+
20
+ ### Removed
21
+ - Drop Ruby 2.7 support (#164)
22
+ - Drop Rails 6.0 support (#164)
8
23
 
9
24
  ## [0.2.6] - 2023-10-11
10
25
  ### Added
@@ -103,7 +118,10 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
103
118
  - Add CHANGELOG (#50)
104
119
  - Add CI (#2)
105
120
 
106
- [Unreleased]: https://github.com/pantographe/view_component-form/compare/v0.2.5...HEAD
121
+ [Unreleased]: https://github.com/pantographe/view_component-form/compare/v0.2.8...HEAD
122
+ [0.2.8]: https://github.com/pantographe/view_component-form/compare/v0.2.7...v0.2.8
123
+ [0.2.7]: https://github.com/pantographe/view_component-form/compare/v0.2.6...v0.2.7
124
+ [0.2.6]: https://github.com/pantographe/view_component-form/compare/v0.2.5...v0.2.6
107
125
  [0.2.5]: https://github.com/pantographe/view_component-form/compare/v0.2.4...v0.2.5
108
126
  [0.2.4]: https://github.com/pantographe/view_component-form/compare/v0.2.3...v0.2.4
109
127
  [0.2.3]: https://github.com/pantographe/view_component-form/compare/v0.2.2...v0.2.3
data/README.md CHANGED
@@ -1,8 +1,6 @@
1
1
  # ViewComponent::Form
2
2
 
3
- **ViewComponent::Form** provides a `FormBuilder` with the same interface as [`ActionView::Helpers::FormBuilder`](https://api.rubyonrails.org/classes/ActionView/Helpers/FormBuilder.html), but using [ViewComponent](https://github.com/github/view_component)s for rendering the fields. It's a starting point for writing your own custom ViewComponents.
4
-
5
- :warning: **This is an early release: the API is subject to change until we reach `v1.0.0`.**
3
+ **`ViewComponent::Form`** is a customizable form builder using the same interface as [`ActionView::Helpers::FormBuilder`](https://api.rubyonrails.org/classes/ActionView/Helpers/FormBuilder.html) but with extensible [ViewComponent](https://github.com/github/view_component) components.
6
4
 
7
5
  Development of this gem is sponsored by:
8
6
 
@@ -10,34 +8,82 @@ Development of this gem is sponsored by:
10
8
 
11
9
  ## Compatibility
12
10
 
11
+ > [!WARNING]
12
+ > **This is an early release, and the API is subject to change until `v1.0.0`.**
13
+
13
14
  This gem is tested on:
14
- - Rails 6.0+ (with or without ActionText)
15
- - Ruby 2.7+
15
+
16
+ - Rails 6.1+ (with or without ActionText)
17
+ - Ruby 3.0+
16
18
 
17
19
  ## Installation
18
20
 
19
- Add this line to your application's Gemfile:
21
+ ```shell
22
+ bundle add view_component-form
23
+ ```
24
+
25
+ ### Configuration
20
26
 
21
27
  ```ruby
22
- gem 'view_component-form'
28
+ # config/initializers/vcf.rb
29
+
30
+ ViewComponent::Form.configure do |config|
31
+ config.parent_component = 'ApplicationFormComponent'
32
+ end
23
33
  ```
24
34
 
25
- And then execute:
35
+ | Attribute | Purpose | Default |
36
+ | --------------------------- | ----------------------------------------------------- | ----------------------- |
37
+ | `parent_component` (string) | Parent class for all `ViewComponent::Form` components | `"ViewComponent::Base"` |
38
+
39
+ #### Configuring component lookup
40
+
41
+ `ViewComponent::Form` will automatically infer the component class with a `Component` suffix. You can customize the lookup using the `lookup_chain`:
42
+
43
+ ```rb
44
+ # config/initializers/vcf.rb
45
+
46
+ ViewComponent::Form.configure do |config|
47
+ without_component_suffix = lambda do |component_name, namespaces: []|
48
+ namespaces.lazy.map do |namespace|
49
+ "#{namespace}::#{component_name.to_s.camelize}".safe_constantize
50
+ end.find(&:itself)
51
+ end
52
+
53
+ config.lookup_chain.prepend(without_component_suffix)
54
+ end
55
+ ```
26
56
 
27
- $ bundle install
57
+ `ViewComponent::Form` will iterate through the `lookup_chain` until a value is returned. By using `prepend` we can fallback on the default `ViewComponent::Form` lookup.
28
58
 
29
59
  ## Usage
30
60
 
31
- Add a `builder` param to your `form_for`, `form_with`, `fields_for` or `fields`:
61
+ Add your own form builder.
62
+
63
+ ```shell
64
+ bin/rails generate vcf:builder FormBuilder
65
+ create app/helpers/form_builder.rb
66
+ ```
67
+
68
+ To use the form builder:
69
+
70
+ - add a `builder` param to your `form_for`, `form_with`, `fields_for` or `fields`:
32
71
 
33
72
  ```diff
34
73
  - <%= form_for @user do |f| %>
35
- + <%= form_for @user, builder: ViewComponent::Form::Builder do |f| %>
74
+ + <%= form_for @user, builder: FormBuilder do |f| %>
36
75
  ```
37
76
 
38
- You can also define a default FormBuilder at the controller level using [default_form_builder](https://api.rubyonrails.org/classes/ActionController/FormBuilder.html#method-i-default_form_builder).
77
+ - or; set it as a default in your controller using [default_form_builder](https://api.rubyonrails.org/classes/ActionController/FormBuilder.html#method-i-default_form_builder).
78
+
79
+ ```ruby
80
+ # app/controllers/application_controller.rb
81
+ class ApplicationController < ActionController::Base
82
+ default_form_builder FormBuilder
83
+ end
84
+ ```
39
85
 
40
- Then call your helpers as usual:
86
+ Then use ActionView form builder helpers as you would normally:
41
87
 
42
88
  ```erb
43
89
  <%# app/views/users/_form.html.erb %>
@@ -62,141 +108,67 @@ Then call your helpers as usual:
62
108
  <% end %>
63
109
  ```
64
110
 
65
- It should work out of the box, but does nothing particularly interesting for now.
66
-
67
- ```html
68
- <form class="edit_user" id="edit_user_1" action="/users/1" accept-charset="UTF-8" method="post">
69
- <input type="hidden" name="_method" value="patch" />
70
- <input type="hidden" name="authenticity_token" value="[...]" />
71
-
72
- <label for="user_first_name">First name</label>
73
- <input type="text" value="John" name="user[first_name]" id="user_first_name" />
74
-
75
- <label for="user_last_name">Last name</label>
76
- <input type="text" value="Doe" name="user[last_name]" id="user_last_name" />
77
-
78
- <label for="user_email">E-mail</label>
79
- <input type="email" value="john.doe@example.com" name="user[email]" id="user_email" />
80
-
81
- <label for="user_password">Password</label>
82
- <input type="password" name="user[password]" id="user_password" aria-describedby="user_password_description" />
83
- <div id="user_password_description">
84
- <div>The password should be at least 8 characters long</div>
85
- </div>
86
- </form>
87
- ```
88
-
89
- The `ViewComponent::Form::*` components are included in the gem.
90
-
91
- ### Customizing the `FormBuilder` and the components
92
-
93
- First, generate your own `FormBuilder`:
94
-
95
- ```console
96
- bin/rails generate vcf:builder CustomFormBuilder
97
-
98
- create app/helpers/custom_form_builder.rb
99
- ```
111
+ ### Customizing built-in components
100
112
 
101
- This allows you to pick the namespace your components will be loaded from.
113
+ The `ViewComponent::Form::Builder` will use the provided `namespace` to find any components you've customized.
102
114
 
103
- ```rb
104
- # app/helpers/custom_form_builder.rb
105
- class CustomFormBuilder < ViewComponent::Form::Builder
106
- # Set the namespace you want to use for your own components
107
- namespace "Custom::Form"
115
+ ```ruby
116
+ # app/helpers/form_builder.rb
117
+ class FormBuilder < ViewComponent::Form::Builder
118
+ namespace Form
108
119
  end
109
120
  ```
110
121
 
111
- Use the generator options to change the default namespace or the path where the file will be created:
112
-
113
- ```console
114
- bin/rails generate vcf:builder AnotherCustomFormBuilder --namespace AnotherCustom::Form --path app/forms
115
-
116
- create app/forms/another_custom_form_builder.rb
117
- ```
122
+ Let's customize the `text_field` helper by generating a new [ViewComponent](https://github.com/github/view_component) in the namespace defined within the builder.
118
123
 
119
- ```rb
120
- # app/forms/another_custom_form_builder.rb
121
- class AnotherCustomFormBuilder < ViewComponent::Form::Builder
122
- # Set the namespace you want to use for your own components
123
- namespace "AnotherCustom::Form"
124
- end
124
+ ```shell
125
+ bin/rails generate component Form::TextField --parent ViewComponent::Form::TextFieldComponent --inline
125
126
  ```
126
127
 
127
- Another approach is to include only some modules instead of inheriting from the whole class:
128
-
129
- ```rb
130
- # app/forms/modular_custom_form_builder.rb
131
- class ModularCustomFormBuilder < ActionView::Helpers::FormBuilder
132
- # Provides `render_component` method and namespace management
133
- include ViewComponent::Form::Renderer
134
-
135
- # Exposes a `validation_context` to your components
136
- include ViewComponent::Form::ValidationContext
137
-
138
- # All standard Rails form helpers
139
- include ViewComponent::Form::Helpers::Rails
140
-
141
- # Backports of Rails 7 form helpers (can be removed if you're running Rails >= 7)
142
- # include ViewComponent::Form::Helpers::Rails7Backports
143
-
144
- # Additional form helpers provided by ViewComponent::Form
145
- # include ViewComponent::Form::Helpers::Custom
146
-
147
- # Set the namespace you want to use for your own components
148
- namespace "AnotherCustom::Form"
128
+ ```ruby
129
+ # app/components/form/text_field_component.rb
130
+ class Form::TextFieldComponent < ViewComponent::Form::TextFieldComponent
131
+ def html_class
132
+ class_names("custom-text-field", "has-error": method_errors?)
133
+ end
149
134
  end
150
135
  ```
151
136
 
152
- Now let's generate your own components to customize their rendering. We can use the standard view_component generator:
153
-
154
- ```console
155
- bin/rails generate component Custom::Form::TextField --inline --parent ViewComponent::Form::TextFieldComponent
156
-
157
- invoke test_unit
158
- create test/components/custom/form/text_field_component_test.rb
159
- create app/components/custom/form/text_field_component.rb
160
- ```
161
-
162
- :warning: The `--parent` option is available since ViewComponent [`v2.41.0`](https://viewcomponent.org/CHANGELOG.html#2410). If you're using a previous version, you can always edit the generated `Custom::Form::CustomTextFieldComponent` class to make it inherit from `ViewComponent::Form::TextFieldComponent`.
137
+ In this case we're leveraging the [`#class_names`](https://api.rubyonrails.org/classes/ActionView/Helpers/TagHelper.html#method-i-class_names) helper to:
163
138
 
164
- Change your forms to use your new builder:
139
+ - always add the `custom-text-field` class;
140
+ - add the `has-error` class if there is an error on the attribute (using `ViewComponent::Form::FieldComponent#method_errors?`).
165
141
 
166
- ```diff
167
- - <%= form_for @user, builder: ViewComponent::Form::Builder do |f| %>
168
- + <%= form_for @user, builder: CustomFormBuilder do |f| %>
169
- ```
142
+ ### Adding your own custom helpers and components
170
143
 
171
- You can then customize the behavior of your `Custom::Form::CustomTextFieldComponent`:
144
+ Add the helper method to your `ViewComponent::Form::Builder`
172
145
 
173
146
  ```rb
174
- # app/components/custom/form/text_field_component.rb
147
+ # app/helpers/form_builder.rb
148
+ class FormBuilder < ViewComponent::Form::Builder
149
+ def year_field(method, options = {})
150
+ render_component(:year_field, @object_name, method, objectify_options(options))
151
+ end
175
152
 
176
- class Admin::Form::TextFieldComponent < ViewComponent::Form::TextFieldComponent
177
- def html_class
178
- class_names("custom-text-field", "has-error": method_errors?)
153
+ def money_field(method, currencies = [], options = {})
154
+ render_component(:money_field, @object_name, method, currencies, objectify_options(options))
179
155
  end
180
156
  end
181
157
  ```
182
158
 
183
- In this case we leverage the [`#class_names`](https://api.rubyonrails.org/classes/ActionView/Helpers/TagHelper.html#method-i-class_names) helper to:
184
- - always add the `custom-text-field` class;
185
- - add the `has-error` class if there is an error on the attribute (using `ViewComponent::Form::FieldComponent#method_errors?`).
159
+ Add your component which can optionally inherit from:
186
160
 
187
- The rendered form field will now look like this:
161
+ - `ViewComponent::Form::FieldComponent` (suggested when adding a field because of helpers)
162
+ - `ViewComponent::Form::BaseComponent`
163
+ - or any of the `ViewComponent::Form::*Component` such as `ViewComponent::Form::TextFieldComponent`
188
164
 
189
- ```html
190
- <input class="custom-text-field" type="text" value="John" name="user[first_name]" id="user_first_name">
165
+ ```rb
166
+ # app/components/form/year_field_component.rb
167
+ class Form::YearFieldComponent < ViewComponent::Form::FieldComponent # or ViewComponent::Form::BaseComponent
168
+ end
191
169
  ```
192
170
 
193
- You can use the same approach to inject options, wrap the input in a `<div>`, etc.
194
-
195
- We'll add more use cases to the documentation soon.
196
-
197
- ### Building your own components
198
-
199
- When building your own ViewComponents for using in forms, it's recommended to inherit from `ViewComponent::Form::FieldComponent`, so you get access to the following helpers:
171
+ When inheriting from `ViewComponent::Form::FieldComponent`, you get access to the following helpers:
200
172
 
201
173
  #### `#label_text`
202
174
 
@@ -239,16 +211,27 @@ en:
239
211
  Renders:
240
212
 
241
213
  ```html
242
- <form class="edit_user" id="edit_user_1" action="/users/1" accept-charset="UTF-8" method="post">
214
+ <form
215
+ class="edit_user"
216
+ id="edit_user_1"
217
+ action="/users/1"
218
+ accept-charset="UTF-8"
219
+ method="post"
220
+ >
243
221
  <!-- ... -->
244
222
  <label>
245
223
  Your first name<br />
246
- <input type="text" value="John" name="user[first_name]" id="user_first_name" />
224
+ <input
225
+ type="text"
226
+ value="John"
227
+ name="user[first_name]"
228
+ id="user_first_name"
229
+ />
247
230
  </label>
248
231
  </form>
249
232
  ```
250
233
 
251
- #### Validations
234
+ ### Validations
252
235
 
253
236
  Let's consider the following model for the examples below.
254
237
 
@@ -339,6 +322,25 @@ def length_validator
339
322
  end
340
323
  ```
341
324
 
325
+ ### Setting up your own base component class
326
+
327
+ 1. Setup some base component from which the form components will inherit from
328
+
329
+ ```rb
330
+ class ApplicationFormComponent < ViewComponent::Base
331
+ end
332
+ ```
333
+
334
+ 2. Configure the parent component class
335
+
336
+ ```rb
337
+ # config/initializers/vcf.rb
338
+
339
+ ViewComponent::Form.configure do |config|
340
+ config.parent_component = 'ApplicationFormComponent'
341
+ end
342
+ ```
343
+
342
344
  ### Using your form components without a backing model
343
345
 
344
346
  If you want to ensure that your fields display consistently across your app, you'll need to lean on Rails' own helpers. You may be used to using form tag helpers such as `text_field_tag` to generate tags, or even writing out plain HTML tags. These can't be integrated with a form builder, so they won't offer you the benefits of this gem.
@@ -2,7 +2,7 @@
2
2
 
3
3
  module ViewComponent
4
4
  module Form
5
- class BaseComponent < ViewComponent::Base
5
+ class BaseComponent < ViewComponent::Form.configuration.parent_component.constantize
6
6
  class << self
7
7
  attr_accessor :default_options
8
8
  end
@@ -0,0 +1,20 @@
1
+ # frozen_string_literal: true
2
+
3
+ module ViewComponent
4
+ module Form
5
+ class Configuration
6
+ attr_accessor :parent_component, :lookup_chain
7
+
8
+ def initialize
9
+ @parent_component = "ViewComponent::Base"
10
+ @lookup_chain = [
11
+ lambda do |component_name, namespaces: []|
12
+ namespaces.lazy.map do |namespace|
13
+ "#{namespace}::#{component_name.to_s.camelize}Component".safe_constantize
14
+ end.find(&:itself)
15
+ end
16
+ ]
17
+ end
18
+ end
19
+ end
20
+ end
@@ -46,7 +46,7 @@ module ViewComponent
46
46
  :datetime_local_field, @object_name, method, objectify_options(options)
47
47
  )
48
48
  end
49
- alias datetime_locale_field datetime_field
49
+ alias datetime_local_field datetime_field
50
50
 
51
51
  def check_box(method, options = {}, checked_value = "1", unchecked_value = "0")
52
52
  render_component(
@@ -52,9 +52,9 @@ module ViewComponent
52
52
 
53
53
  def component_klass(component_name)
54
54
  @__component_klass_cache[component_name] ||= begin
55
- component_klass = self.class.lookup_namespaces.filter_map do |namespace|
56
- "#{namespace}::#{component_name.to_s.camelize}Component".safe_constantize || false
57
- end.first
55
+ component_klass = ViewComponent::Form.configuration.lookup_chain.lazy.map do |lookup|
56
+ lookup.call(component_name, namespaces: lookup_namespaces)
57
+ end.find(&:itself)
58
58
 
59
59
  unless component_klass.is_a?(Class) && component_klass < ViewComponent::Base
60
60
  raise NotImplementedComponentError, "Component named #{component_name} doesn't exist " \
@@ -2,6 +2,6 @@
2
2
 
3
3
  module ViewComponent
4
4
  module Form
5
- VERSION = "0.2.6"
5
+ VERSION = "0.2.8"
6
6
  end
7
7
  end
@@ -5,6 +5,15 @@ require "zeitwerk"
5
5
 
6
6
  module ViewComponent
7
7
  module Form
8
+ class << self
9
+ def configuration
10
+ @configuration ||= Configuration.new
11
+ end
12
+
13
+ def configure
14
+ yield configuration
15
+ end
16
+ end
8
17
  end
9
18
  end
10
19
 
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: view_component-form
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.2.6
4
+ version: 0.2.8
5
5
  platform: ruby
6
6
  authors:
7
7
  - Pantographe
8
- autorequire:
8
+ autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2023-10-11 00:00:00.000000000 Z
11
+ date: 2024-08-20 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: actionview
@@ -16,40 +16,40 @@ dependencies:
16
16
  requirements:
17
17
  - - ">="
18
18
  - !ruby/object:Gem::Version
19
- version: 6.0.0
19
+ version: 6.1.0
20
20
  - - "<"
21
21
  - !ruby/object:Gem::Version
22
- version: '7.2'
22
+ version: '8.0'
23
23
  type: :runtime
24
24
  prerelease: false
25
25
  version_requirements: !ruby/object:Gem::Requirement
26
26
  requirements:
27
27
  - - ">="
28
28
  - !ruby/object:Gem::Version
29
- version: 6.0.0
29
+ version: 6.1.0
30
30
  - - "<"
31
31
  - !ruby/object:Gem::Version
32
- version: '7.2'
32
+ version: '8.0'
33
33
  - !ruby/object:Gem::Dependency
34
34
  name: activesupport
35
35
  requirement: !ruby/object:Gem::Requirement
36
36
  requirements:
37
37
  - - ">="
38
38
  - !ruby/object:Gem::Version
39
- version: 6.0.0
39
+ version: 6.1.0
40
40
  - - "<"
41
41
  - !ruby/object:Gem::Version
42
- version: '7.2'
42
+ version: '8.0'
43
43
  type: :runtime
44
44
  prerelease: false
45
45
  version_requirements: !ruby/object:Gem::Requirement
46
46
  requirements:
47
47
  - - ">="
48
48
  - !ruby/object:Gem::Version
49
- version: 6.0.0
49
+ version: 6.1.0
50
50
  - - "<"
51
51
  - !ruby/object:Gem::Version
52
- version: '7.2'
52
+ version: '8.0'
53
53
  - !ruby/object:Gem::Dependency
54
54
  name: view_component
55
55
  requirement: !ruby/object:Gem::Requirement
@@ -136,6 +136,7 @@ files:
136
136
  - lib/view_component/form.rb
137
137
  - lib/view_component/form/builder.rb
138
138
  - lib/view_component/form/class_names_helper.rb
139
+ - lib/view_component/form/configuration.rb
139
140
  - lib/view_component/form/engine.rb
140
141
  - lib/view_component/form/helpers/custom.rb
141
142
  - lib/view_component/form/helpers/rails.rb
@@ -153,7 +154,7 @@ metadata:
153
154
  source_code_uri: https://github.com/pantographe/view_component-form
154
155
  bug_tracker_uri: https://github.com/pantographe/view_component-form/issues
155
156
  rubygems_mfa_required: 'true'
156
- post_install_message:
157
+ post_install_message:
157
158
  rdoc_options: []
158
159
  require_paths:
159
160
  - lib
@@ -161,15 +162,15 @@ required_ruby_version: !ruby/object:Gem::Requirement
161
162
  requirements:
162
163
  - - ">="
163
164
  - !ruby/object:Gem::Version
164
- version: 2.7.0
165
+ version: 3.0.0
165
166
  required_rubygems_version: !ruby/object:Gem::Requirement
166
167
  requirements:
167
168
  - - ">="
168
169
  - !ruby/object:Gem::Version
169
170
  version: '0'
170
171
  requirements: []
171
- rubygems_version: 3.4.10
172
- signing_key:
172
+ rubygems_version: 3.5.11
173
+ signing_key:
173
174
  specification_version: 4
174
175
  summary: Rails FormBuilder for ViewComponent
175
176
  test_files: []