superglue 1.0.0 → 1.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -8,7 +8,7 @@
8
8
  * these components to fit your design needs.
9
9
  */
10
10
 
11
- import React, { ReactNode, useContext, createContext, useMemo } from "react";
11
+ import React, { ReactNode, useContext, createContext, useMemo } from 'react'
12
12
  import {
13
13
  CheckboxField as RailsCheckboxField,
14
14
  CollectionCheckboxesField as RailsCollectionCheckboxesField,
@@ -31,25 +31,35 @@ import {
31
31
  UrlField as RailsUrlField,
32
32
  TextArea as RailsTextArea,
33
33
  ValidationErrors,
34
- SubmitProps as RailsSubmitButton
34
+ SubmitProps as RailsSubmitButton,
35
35
  } from '@thoughtbot/candy_wrapper'
36
36
 
37
- export const ValidationContext = createContext<ValidationErrors>({});
37
+ export const ValidationContext = createContext<ValidationErrors>({})
38
38
 
39
- export const useErrorKeyValidation = ({
40
- errorKey,
41
- }: {
42
- errorKey: string;
43
- name: string;
44
- }) => {
45
- const errors = useContext(ValidationContext);
39
+ export const useErrorMessage = (errorKey?: string) => {
40
+ const errors = useContext(ValidationContext)
46
41
 
47
42
  return useMemo(() => {
48
- return errors[errorKey];
49
- }, [errors, errorKey]);
50
- };
43
+ if (!errorKey) {
44
+ return null
45
+ }
46
+
47
+ const validationError = errors[errorKey]
48
+ const hasErrors = errorKey && validationError
49
+
50
+ if (!hasErrors) {
51
+ return null
52
+ }
53
+
54
+ const errorMessages = Array.isArray(validationError)
55
+ ? validationError
56
+ : [validationError]
51
57
 
52
- export type ExtrasProps = Record<string, RailsHiddenField>;
58
+ return errorMessages.join(' ')
59
+ }, [errors, errorKey])
60
+ }
61
+
62
+ export type ExtrasProps = Record<string, RailsHiddenField>
53
63
 
54
64
  /**
55
65
  * Extras renders the hidden inputs generated by form_props.
@@ -58,25 +68,24 @@ export type ExtrasProps = Record<string, RailsHiddenField>;
58
68
  * utf8, crsf_token, _method
59
69
  */
60
70
  export const Extras = (hiddenInputAttributes: ExtrasProps) => {
61
- const hiddenProps = Object.values(hiddenInputAttributes);
71
+ const hiddenProps = Object.values(hiddenInputAttributes)
62
72
  const hiddenInputs = hiddenProps.map((props: RailsHiddenField) => (
63
73
  <input {...props} type="hidden" key={props.name} />
64
- ));
74
+ ))
65
75
 
66
- return <>{hiddenInputs}</>;
67
- };
76
+ return <>{hiddenInputs}</>
77
+ }
68
78
 
69
- // TODO: Add this as a form props props??
70
- export interface FormProps<T={}> {
71
- extras: ExtrasProps;
72
- inputs: T;
73
- form: React.FormHTMLAttributes<HTMLFormElement>;
79
+ export interface FormProps<T = {}> {
80
+ extras: ExtrasProps
81
+ inputs: T
82
+ form: React.FormHTMLAttributes<HTMLFormElement>
74
83
  }
75
84
 
76
85
  type FormElementProps = React.FormHTMLAttributes<HTMLFormElement> & {
77
- extras: ExtrasProps;
78
- validationErrors?: ValidationErrors;
79
- };
86
+ extras: ExtrasProps
87
+ validationErrors?: ValidationErrors
88
+ }
80
89
  /**
81
90
  * A basic form component that supports inline errors.
82
91
  *
@@ -96,8 +105,8 @@ export const Form = ({
96
105
  {children}
97
106
  </ValidationContext.Provider>
98
107
  </form>
99
- );
100
- };
108
+ )
109
+ }
101
110
 
102
111
  /**
103
112
  * An inline error component.
@@ -106,31 +115,17 @@ export const Form = ({
106
115
  * Please modify this to your liking.
107
116
  */
108
117
  export const FieldError = ({ errorKey }: { errorKey: string | undefined }) => {
109
- const errors = useContext(ValidationContext);
110
- if (!errorKey || !errors) {
111
- return null;
112
- }
118
+ const errorMessage = useErrorMessage(errorKey)
113
119
 
114
- const validationError = errors[errorKey];
115
- const hasErrors = errorKey && validationError;
116
-
117
- if (!hasErrors) {
118
- return null;
119
- }
120
-
121
- const errorMessages = Array.isArray(validationError)
122
- ? validationError
123
- : [validationError];
124
-
125
- return <span>{errorMessages.join(" ")}</span>;
126
- };
120
+ return <span>{errorMessage}</span>
121
+ }
127
122
 
128
123
  export type FieldBaseProps = React.InputHTMLAttributes<HTMLInputElement> & {
129
- id?: string;
130
- label: string;
131
- errorKey?: string;
132
- children?: ReactNode;
133
- };
124
+ id?: string
125
+ label: string
126
+ errorKey?: string
127
+ children?: ReactNode
128
+ }
134
129
 
135
130
  /**
136
131
  * A Field component.
@@ -149,13 +144,13 @@ export const FieldBase = ({
149
144
  {children || <input {...props} />}
150
145
  <FieldError errorKey={errorKey} />
151
146
  </>
152
- );
153
- };
147
+ )
148
+ }
154
149
 
155
150
  type InputProps = {
156
- label: string;
157
- errorKey?: string;
158
- };
151
+ label: string
152
+ errorKey?: string
153
+ }
159
154
 
160
155
  /**
161
156
  * A checkbox component.
@@ -163,7 +158,7 @@ type InputProps = {
163
158
  * Designed to work with a payload form_props's [checkbox helper](https://github.com/thoughtbot/form_props?tab=readme-ov-file#checkbox-helper).
164
159
  * Mimics the rails equivalent. Please modify to your liking.
165
160
  */
166
- type CheckboxProps = RailsCheckboxField & InputProps;
161
+ type CheckboxProps = RailsCheckboxField & InputProps
167
162
  export const Checkbox = ({
168
163
  type: _type,
169
164
  includeHidden,
@@ -171,7 +166,7 @@ export const Checkbox = ({
171
166
  errorKey,
172
167
  ...rest
173
168
  }: CheckboxProps) => {
174
- const { name } = rest;
169
+ const { name } = rest
175
170
  return (
176
171
  <FieldBase {...rest} errorKey={errorKey}>
177
172
  {includeHidden && (
@@ -184,11 +179,11 @@ export const Checkbox = ({
184
179
  )}
185
180
  <input type="checkbox" {...rest}></input>
186
181
  </FieldBase>
187
- );
188
- };
182
+ )
183
+ }
189
184
 
190
185
  type CollectionCheckboxesFieldProps = RailsCollectionCheckboxesField &
191
- InputProps;
186
+ InputProps
192
187
 
193
188
  /**
194
189
  * A collection checkbox component.
@@ -203,29 +198,29 @@ export const CollectionCheckboxes = ({
203
198
  errorKey,
204
199
  }: CollectionCheckboxesFieldProps) => {
205
200
  if (collection.length == 0) {
206
- return null;
201
+ return null
207
202
  }
208
203
 
209
204
  const checkboxes = collection.map((options) => {
210
- return <Checkbox {...options} key={options.id} />;
211
- });
205
+ return <Checkbox {...options} key={options.id} />
206
+ })
212
207
 
213
- const { name } = collection[0];
208
+ const { name } = collection[0]
214
209
 
215
210
  return (
216
211
  <>
217
212
  {includeHidden && (
218
- <input type="hidden" name={name} defaultValue={""} autoComplete="off" />
213
+ <input type="hidden" name={name} defaultValue={''} autoComplete="off" />
219
214
  )}
220
215
  <label>{label}</label>
221
216
  {checkboxes}
222
217
  <FieldError errorKey={errorKey} />
223
218
  </>
224
- );
225
- };
219
+ )
220
+ }
226
221
 
227
222
  type CollectionRadioButtonsFieldProps = RailsCollectionRadioButtonsField &
228
- InputProps;
223
+ InputProps
229
224
 
230
225
  /**
231
226
  * A collection radio button component.
@@ -240,7 +235,7 @@ export const CollectionRadioButtons = ({
240
235
  errorKey,
241
236
  }: CollectionRadioButtonsFieldProps) => {
242
237
  if (collection.length == 0) {
243
- return null;
238
+ return null
244
239
  }
245
240
 
246
241
  const radioButtons = collection.map((options) => {
@@ -249,26 +244,26 @@ export const CollectionRadioButtons = ({
249
244
  <input {...options} type="radio" />
250
245
  <label htmlFor={options.id}>{options.label}</label>
251
246
  </div>
252
- );
253
- });
247
+ )
248
+ })
254
249
 
255
- const { name } = collection[0];
250
+ const { name } = collection[0]
256
251
 
257
252
  return (
258
253
  <>
259
254
  {includeHidden && (
260
- <input type="hidden" name={name} defaultValue={""} autoComplete="off" />
255
+ <input type="hidden" name={name} defaultValue={''} autoComplete="off" />
261
256
  )}
262
257
  <label>{label}</label>
263
258
  {radioButtons}
264
259
  <FieldError errorKey={errorKey} />
265
260
  </>
266
- );
267
- };
261
+ )
262
+ }
268
263
 
269
264
  export type TextFieldProps = React.InputHTMLAttributes<HTMLInputElement> &
270
265
  RailsTextField &
271
- InputProps;
266
+ InputProps
272
267
 
273
268
  /**
274
269
  * A text field component.
@@ -277,12 +272,12 @@ export type TextFieldProps = React.InputHTMLAttributes<HTMLInputElement> &
277
272
  * Mimics the rails equivalent. Please modify to your liking.
278
273
  */
279
274
  export const TextField = ({ type: _type, ...rest }: TextFieldProps) => {
280
- return <FieldBase {...rest} type="text" />;
281
- };
275
+ return <FieldBase {...rest} type="text" />
276
+ }
282
277
 
283
278
  export type EmailFieldProps = React.InputHTMLAttributes<HTMLInputElement> &
284
279
  RailsEmailField &
285
- InputProps;
280
+ InputProps
286
281
 
287
282
  /**
288
283
  * A email field component.
@@ -291,12 +286,12 @@ export type EmailFieldProps = React.InputHTMLAttributes<HTMLInputElement> &
291
286
  * Mimics the rails equivalent. Please modify to your liking.
292
287
  */
293
288
  export const EmailField = ({ type: _type, ...rest }: EmailFieldProps) => {
294
- return <FieldBase {...rest} type="email" />;
295
- };
289
+ return <FieldBase {...rest} type="email" />
290
+ }
296
291
 
297
292
  export type ColorFieldProps = React.InputHTMLAttributes<HTMLInputElement> &
298
293
  RailsColorField &
299
- InputProps;
294
+ InputProps
300
295
 
301
296
  /**
302
297
  * A color field component.
@@ -305,12 +300,12 @@ export type ColorFieldProps = React.InputHTMLAttributes<HTMLInputElement> &
305
300
  * Mimics the rails equivalent. Please modify to your liking.
306
301
  */
307
302
  export const ColorField = ({ type: _type, ...rest }: ColorFieldProps) => {
308
- return <FieldBase {...rest} type="color" />;
309
- };
303
+ return <FieldBase {...rest} type="color" />
304
+ }
310
305
 
311
306
  export type DateFieldProps = React.InputHTMLAttributes<HTMLInputElement> &
312
307
  RailsDateField &
313
- InputProps;
308
+ InputProps
314
309
 
315
310
  /**
316
311
  * A date field component.
@@ -319,13 +314,13 @@ export type DateFieldProps = React.InputHTMLAttributes<HTMLInputElement> &
319
314
  * Mimics the rails equivalent. Please modify to your liking.
320
315
  */
321
316
  export const DateField = ({ type: _type, ...rest }: DateFieldProps) => {
322
- return <FieldBase {...rest} type="date" />;
323
- };
317
+ return <FieldBase {...rest} type="date" />
318
+ }
324
319
 
325
320
  export type DateTimeLocalFieldProps =
326
321
  React.InputHTMLAttributes<HTMLInputElement> &
327
322
  RailsDateTimeLocalField &
328
- InputProps;
323
+ InputProps
329
324
 
330
325
  /**
331
326
  * A date field component.
@@ -337,12 +332,12 @@ export const DateTimeLocalField = ({
337
332
  type: _type,
338
333
  ...rest
339
334
  }: DateTimeLocalFieldProps) => {
340
- return <FieldBase {...rest} type="datetime-local" />;
341
- };
335
+ return <FieldBase {...rest} type="datetime-local" />
336
+ }
342
337
 
343
338
  export type SearchFieldProps = React.InputHTMLAttributes<HTMLInputElement> &
344
339
  RailsSearchField &
345
- InputProps;
340
+ InputProps
346
341
 
347
342
  /**
348
343
  * A search field component.
@@ -351,12 +346,12 @@ export type SearchFieldProps = React.InputHTMLAttributes<HTMLInputElement> &
351
346
  * Mimics the rails equivalent. Please modify to your liking.
352
347
  */
353
348
  export const SearchField = ({ type: _type, ...rest }: SearchFieldProps) => {
354
- return <FieldBase {...rest} type="search" />;
355
- };
349
+ return <FieldBase {...rest} type="search" />
350
+ }
356
351
 
357
352
  export type TelFieldProps = React.InputHTMLAttributes<HTMLInputElement> &
358
353
  RailsTelField &
359
- InputProps;
354
+ InputProps
360
355
 
361
356
  /**
362
357
  * A tel field component.
@@ -365,12 +360,12 @@ export type TelFieldProps = React.InputHTMLAttributes<HTMLInputElement> &
365
360
  * Mimics the rails equivalent. Please modify to your liking.
366
361
  */
367
362
  export const TelField = ({ type: _type, ...rest }: TelFieldProps) => {
368
- return <FieldBase {...rest} type="tel" />;
369
- };
363
+ return <FieldBase {...rest} type="tel" />
364
+ }
370
365
 
371
366
  export type UrlFieldProps = React.InputHTMLAttributes<HTMLInputElement> &
372
367
  RailsUrlField &
373
- InputProps;
368
+ InputProps
374
369
 
375
370
  /**
376
371
  * A url field component.
@@ -379,12 +374,12 @@ export type UrlFieldProps = React.InputHTMLAttributes<HTMLInputElement> &
379
374
  * Mimics the rails equivalent. Please modify to your liking.
380
375
  */
381
376
  export const UrlField = ({ type: _type, ...rest }: UrlFieldProps) => {
382
- return <FieldBase {...rest} type="url" />;
383
- };
377
+ return <FieldBase {...rest} type="url" />
378
+ }
384
379
 
385
380
  export type MonthFieldProps = React.InputHTMLAttributes<HTMLInputElement> &
386
381
  RailsMonthField &
387
- InputProps;
382
+ InputProps
388
383
 
389
384
  /**
390
385
  * A month field component.
@@ -393,26 +388,26 @@ export type MonthFieldProps = React.InputHTMLAttributes<HTMLInputElement> &
393
388
  * Mimics the rails equivalent. Please modify to your liking.
394
389
  */
395
390
  export const MonthField = ({ type: _type, ...rest }: MonthFieldProps) => {
396
- return <FieldBase {...rest} type="month" />;
397
- };
391
+ return <FieldBase {...rest} type="month" />
392
+ }
398
393
 
399
394
  export type TimeFieldProps = React.InputHTMLAttributes<HTMLInputElement> &
400
395
  RailsTimeField &
401
- InputProps;
396
+ InputProps
402
397
 
403
398
  /**
404
- * A month field component.
399
+ * A time field component.
405
400
  *
406
401
  * Designed to work with a payload form_props's [month_field helper](https://github.com/thoughtbot/form_props?tab=readme-ov-file#date-helpers).
407
402
  * Mimics the rails equivalent. Please modify to your liking.
408
403
  */
409
404
  export const TimeField = ({ type: _type, ...rest }: TimeFieldProps) => {
410
- return <FieldBase {...rest} type="time" />;
411
- };
405
+ return <FieldBase {...rest} type="time" />
406
+ }
412
407
 
413
408
  export type NumberFieldProps = React.InputHTMLAttributes<HTMLInputElement> &
414
409
  RailsNumberField &
415
- InputProps;
410
+ InputProps
416
411
  /**
417
412
  * A number field component.
418
413
  *
@@ -420,12 +415,12 @@ export type NumberFieldProps = React.InputHTMLAttributes<HTMLInputElement> &
420
415
  * Mimics the rails equivalent. Please modify to your liking.
421
416
  */
422
417
  export const NumberField = ({ type: _type, ...rest }: NumberFieldProps) => {
423
- return <FieldBase {...rest} type="number" />;
424
- };
418
+ return <FieldBase {...rest} type="number" />
419
+ }
425
420
 
426
421
  export type RangeFieldProps = React.InputHTMLAttributes<HTMLInputElement> &
427
422
  RailsRangeField &
428
- InputProps;
423
+ InputProps
429
424
  /**
430
425
  * A range field component.
431
426
  *
@@ -433,12 +428,12 @@ export type RangeFieldProps = React.InputHTMLAttributes<HTMLInputElement> &
433
428
  * Mimics the rails equivalent. Please modify to your liking.
434
429
  */
435
430
  export const RangeField = ({ type: _type, ...rest }: RangeFieldProps) => {
436
- return <FieldBase {...rest} type="range" />;
437
- };
431
+ return <FieldBase {...rest} type="range" />
432
+ }
438
433
 
439
434
  export type PasswordFieldProps = React.InputHTMLAttributes<HTMLInputElement> &
440
435
  RailsPasswordField &
441
- InputProps;
436
+ InputProps
442
437
  /**
443
438
  * A password field component.
444
439
  *
@@ -446,14 +441,12 @@ export type PasswordFieldProps = React.InputHTMLAttributes<HTMLInputElement> &
446
441
  * Mimics the rails equivalent. Please modify to your liking.
447
442
  */
448
443
  export const PasswordField = ({ type: _type, ...rest }: PasswordFieldProps) => {
449
- return <FieldBase {...rest} type="password" />;
450
- };
444
+ return <FieldBase {...rest} type="password" />
445
+ }
451
446
 
452
447
  export type SelectProps = React.SelectHTMLAttributes<HTMLSelectElement> &
453
- RailsSelect & {
454
- label?: string;
455
- errorKey?: string;
456
- };
448
+ RailsSelect &
449
+ InputProps
457
450
  /**
458
451
  * A select component.
459
452
  *
@@ -468,42 +461,46 @@ export const Select = ({
468
461
  id,
469
462
  children,
470
463
  options,
464
+ label,
465
+ errorKey,
471
466
  multiple,
472
467
  type: _type,
473
468
  ...rest
474
469
  }: SelectProps) => {
475
- const addHidden = includeHidden && multiple;
470
+ const addHidden = includeHidden && multiple
476
471
 
477
472
  const optionElements = options.map((item) => {
478
- if ("options" in item) {
473
+ if ('options' in item) {
479
474
  return (
480
475
  <optgroup label={item.label} key={item.label}>
481
476
  {item.options.map((opt) => (
482
477
  <option key={opt.label} {...opt} />
483
478
  ))}
484
479
  </optgroup>
485
- );
480
+ )
486
481
  } else {
487
- return <option key={item.label} {...item} />;
482
+ return <option key={item.label} {...item} />
488
483
  }
489
- });
484
+ })
490
485
 
491
486
  return (
492
487
  <>
493
488
  {addHidden && (
494
- <input type="hidden" name={name} value={""} autoComplete="off" />
489
+ <input type="hidden" name={name} value={''} autoComplete="off" />
495
490
  )}
496
- <select name={name} id={id} multiple={multiple} {...rest}>
497
- {children}
498
- {optionElements}
499
- </select>
491
+ <FieldBase label={label} errorKey={errorKey} id={id}>
492
+ <select name={name} id={id} multiple={multiple} {...rest}>
493
+ {children}
494
+ {optionElements}
495
+ </select>
496
+ </FieldBase>
500
497
  </>
501
- );
502
- };
498
+ )
499
+ }
503
500
 
504
501
  export type TextAreaProps = React.InputHTMLAttributes<HTMLTextAreaElement> &
505
502
  RailsTextArea &
506
- InputProps;
503
+ InputProps
507
504
  /**
508
505
  * A text area component.
509
506
  *
@@ -511,18 +508,18 @@ export type TextAreaProps = React.InputHTMLAttributes<HTMLTextAreaElement> &
511
508
  * Mimics the rails equivalent. Please modify to your liking.
512
509
  */
513
510
  export const TextArea = ({ type: _type, errorKey, ...rest }: TextAreaProps) => {
514
- const { label } = rest;
511
+ const { label } = rest
515
512
 
516
513
  return (
517
514
  <FieldBase label={label} errorKey={errorKey} id={rest.id}>
518
515
  <textarea {...rest} />
519
516
  </FieldBase>
520
- );
521
- };
517
+ )
518
+ }
522
519
 
523
520
  export type FileFieldProps = React.InputHTMLAttributes<HTMLInputElement> &
524
521
  RailsFileField &
525
- InputProps;
522
+ InputProps
526
523
 
527
524
  /**
528
525
  * A file field component.
@@ -531,17 +528,27 @@ export type FileFieldProps = React.InputHTMLAttributes<HTMLInputElement> &
531
528
  * Mimics the rails equivalent. Please modify to your liking.
532
529
  */
533
530
  export const FileField = ({ type: _type, ...rest }: FileFieldProps) => {
534
- return <FieldBase {...rest} type="file" />;
535
- };
531
+ return <FieldBase {...rest} type="file" />
532
+ }
536
533
 
537
534
  export type SubmitButtonProps = React.ButtonHTMLAttributes<HTMLButtonElement> &
538
535
  RailsSubmitButton
536
+
539
537
  /**
540
538
  * A SubmitButton component.
541
539
  *
542
540
  * Designed to work with a payload form_props's [submit helper](https://github.com/thoughtbot/form_props?tab=readme-ov-file#form-helpers).
543
541
  * Mimics the rails equivalent. Please modify to your liking.
544
542
  */
545
- export const SubmitButton = ({ type: _type, text, ...rest }: SubmitButtonProps) => {
546
- return <button {...rest} type="submit"> {text} </button>
547
- };
543
+ export const SubmitButton = ({
544
+ type: _type,
545
+ text,
546
+ ...rest
547
+ }: SubmitButtonProps) => {
548
+ return (
549
+ <button {...rest} type="submit">
550
+ {' '}
551
+ {text}{' '}
552
+ </button>
553
+ )
554
+ }
@@ -25,7 +25,11 @@ module Superglue
25
25
  argument :attributes, type: :array, default: [], banner: "field:type field:type"
26
26
 
27
27
  def create_controller_files
28
- template "controller.rb", File.join("app/controllers", controller_class_path, "#{controller_file_name}_controller.rb")
28
+ controller_file = File.join("app/controllers", controller_class_path, "#{controller_file_name}_controller.rb")
29
+ template "controller.rb", controller_file
30
+ inject_into_file controller_file, after: /ApplicationController$/ do
31
+ "\n before_action :use_jsx_rendering_defaults"
32
+ end
29
33
  end
30
34
 
31
35
  # Replaces template_engine (and its default erb), with view_collection
@@ -21,14 +21,6 @@ module Superglue
21
21
  empty_directory path unless File.directory?(path)
22
22
  end
23
23
 
24
- def copy_erb_files
25
- available_views.each do |view|
26
- @action_name = view
27
- filename = filename_with_html_extensions(view)
28
- template "erb/" + filename, File.join("app/views", controller_file_path, filename)
29
- end
30
- end
31
-
32
24
  def copy_prop_files
33
25
  available_views.each do |view|
34
26
  @action_name = view