feat(form): implement unique IDs (#182)

* Added Short Unique ID npm library

* Added unique ID to form-control

* Added unique ID to form-group

* Unique ID added to <form>

* Added unique IDs to control components

* Added IDs to label and fieldset

* Update Form.astro with requested changes

Co-authored-by: Ayo Ayco <ayo@ayco.io>

* Adjustments for requested changes.

Co-authored-by: Ayo Ayco <ayo@ayco.io>
This commit is contained in:
Alexander Samaniego 2022-11-08 02:53:22 -05:00 committed by GitHub
parent d02c1e4081
commit 93a8d49f0a
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
11 changed files with 41 additions and 9 deletions

17
package-lock.json generated
View file

@ -17,7 +17,8 @@
"packages/common" "packages/common"
], ],
"dependencies": { "dependencies": {
"prettier-plugin-astro": "^0.7.0" "prettier-plugin-astro": "^0.7.0",
"short-unique-id": "^4.4.4"
} }
}, },
"apps/demo": { "apps/demo": {
@ -7578,6 +7579,15 @@
"resolved": "https://registry.npmjs.org/jsonc-parser/-/jsonc-parser-3.2.0.tgz", "resolved": "https://registry.npmjs.org/jsonc-parser/-/jsonc-parser-3.2.0.tgz",
"integrity": "sha512-gfFQZrcTc8CnKXp6Y4/CBT3fTc0OVuDofpre4aEeEpSBPV5X5v4+Vmx+8snU7RLPrNHPKSgLxGo9YuQzz20o+w==" "integrity": "sha512-gfFQZrcTc8CnKXp6Y4/CBT3fTc0OVuDofpre4aEeEpSBPV5X5v4+Vmx+8snU7RLPrNHPKSgLxGo9YuQzz20o+w=="
}, },
"node_modules/short-unique-id": {
"version": "4.4.4",
"resolved": "https://registry.npmjs.org/short-unique-id/-/short-unique-id-4.4.4.tgz",
"integrity": "sha512-oLF1NCmtbiTWl2SqdXZQbo5KM1b7axdp0RgQLq8qCBBLoq+o3A5wmLrNM6bZIh54/a8BJ3l69kTXuxwZ+XCYuw==",
"bin": {
"short-unique-id": "bin/short-unique-id",
"suid": "bin/short-unique-id"
}
},
"node_modules/signal-exit": { "node_modules/signal-exit": {
"version": "3.0.7", "version": "3.0.7",
"resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.7.tgz", "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.7.tgz",
@ -14421,6 +14431,11 @@
} }
} }
}, },
"short-unique-id": {
"version": "4.4.4",
"resolved": "https://registry.npmjs.org/short-unique-id/-/short-unique-id-4.4.4.tgz",
"integrity": "sha512-oLF1NCmtbiTWl2SqdXZQbo5KM1b7axdp0RgQLq8qCBBLoq+o3A5wmLrNM6bZIh54/a8BJ3l69kTXuxwZ+XCYuw=="
},
"signal-exit": { "signal-exit": {
"version": "3.0.7", "version": "3.0.7",
"resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.7.tgz", "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.7.tgz",

View file

@ -38,6 +38,7 @@
"packages/common" "packages/common"
], ],
"dependencies": { "dependencies": {
"prettier-plugin-astro": "^0.7.0" "prettier-plugin-astro": "^0.7.0",
"short-unique-id": "^4.4.4"
} }
} }

View file

@ -11,7 +11,7 @@ export interface Props {
const { group, showValidationHints, readOnly = false } = Astro.props; const { group, showValidationHints, readOnly = false } = Astro.props;
--- ---
<fieldset id={group.name} name={group.name}> <fieldset id={group.id} name={group.name}>
{group.name && <legend>{group.name}</legend>} {group.name && <legend>{group.name}</legend>}
{ {
group?.controls?.map((control) => ( group?.controls?.map((control) => (

View file

@ -3,6 +3,7 @@ import type { Submit } from '@astro-reactive/common';
import { FormGroup, FormControl } from '../core'; import { FormGroup, FormControl } from '../core';
import FieldSet from './FieldSet.astro'; import FieldSet from './FieldSet.astro';
import Field from './Field.astro'; import Field from './Field.astro';
import ShortUniqueId from 'short-unique-id';
export interface Props { export interface Props {
formGroups: FormGroup | FormGroup[]; formGroups: FormGroup | FormGroup[];
@ -20,14 +21,16 @@ const {
readOnly = false, readOnly = false,
} = Astro.props; } = Astro.props;
const uid = new ShortUniqueId({ length: 9 });
const formTheme = theme ?? 'light'; const formTheme = theme ?? 'light';
const formName = Array.isArray(formGroups) ? null : formGroups?.name || null; const formName = Array.isArray(formGroups) ? null : formGroups?.name || null;
const formId = Array.isArray(formGroups) ? uid() : formGroups?.id || null;
--- ---
<form <form
class={formTheme} class={formTheme}
name={formName} name={formName}
id={formName} id={formId}
data-validator-hints={showValidationHints.toString()} data-validator-hints={showValidationHints.toString()}
> >
{ {

View file

@ -16,7 +16,7 @@ const isRequired: boolean = showValidationHints && validators.includes('validato
{ {
control.label && control.type !== "checkbox" && ( control.label && control.type !== "checkbox" && (
<label for={control.name} data-validation-required={isRequired ? "true" : null}> <label for={control.id} data-validation-required={isRequired ? "true" : null}>
{control.label} {control.label}
</label> </label>
) )
@ -26,7 +26,7 @@ const isRequired: boolean = showValidationHints && validators.includes('validato
{ {
control.label && control.type === "checkbox" && ( control.label && control.type === "checkbox" && (
<label for={control.name} data-validation-required={isRequired ? "true" : null}> <label for={control.id} data-validation-required={isRequired ? "true" : null}>
{control.label} {control.label}
</label> </label>
) )

View file

@ -24,7 +24,7 @@ const options = control.options.map((option: string | ControlOption) => {
<select <select
name={control.name} name={control.name}
id={control.name} id={control.id}
disabled={readOnly || null} disabled={readOnly || null}
> >
{ {

View file

@ -30,7 +30,7 @@ const validatorAttributes: Record<string, string> = validators?.reduce((prev, va
<input <input
name={control.name} name={control.name}
id={control.name} id={control.id}
type={control.type as InputType} type={control.type as InputType}
value={control.value?.toString()} value={control.value?.toString()}
checked={control.value === 'checked'} checked={control.value === 'checked'}

View file

@ -27,6 +27,7 @@ const options = control.options.map((option: string | ControlOption) => {
<div class="radio-option"> <div class="radio-option">
<input <input
type="radio" type="radio"
id={control.id}
name={control.name} name={control.name}
value={option.value} value={option.value}
checked={option.value === control.value} checked={option.value === control.value}

View file

@ -27,7 +27,7 @@ const validatorAttributes: Record<string, string> = validators?.reduce((prev, va
<textarea <textarea
name={control.name} name={control.name}
id={control.name} id={control.id}
placeholder={control?.placeholder} placeholder={control?.placeholder}
rows={control?.rows ?? 3} rows={control?.rows ?? 3}
cols={control?.cols ?? 21} cols={control?.cols ?? 21}

View file

@ -10,10 +10,12 @@ import type {
TextArea, TextArea,
ControlBase, ControlBase,
} from '@astro-reactive/common'; } from '@astro-reactive/common';
import ShortUniqueId from 'short-unique-id';
export type ControlConfig = ControlBase | Checkbox | Radio | Submit | Button | Dropdown | TextArea; export type ControlConfig = ControlBase | Checkbox | Radio | Submit | Button | Dropdown | TextArea;
export class FormControl { export class FormControl {
private _id = '';
private _name = ''; private _name = '';
private _type: ControlType = 'text'; private _type: ControlType = 'text';
private _value?: string | number | null | string[] | ControlOption[]; private _value?: string | number | null | string[] | ControlOption[];
@ -46,6 +48,8 @@ export class FormControl {
validators = [], validators = [],
} = config; } = config;
const uid = new ShortUniqueId({ length: 9 });
this._id = 'arl-' + uid();
this._name = name; this._name = name;
this._type = type; this._type = type;
this._value = value; this._value = value;
@ -80,6 +84,10 @@ export class FormControl {
}); });
} }
get id() {
return this._id;
}
get name() { get name() {
return this._name; return this._name;
} }

View file

@ -1,11 +1,15 @@
import { ControlConfig, FormControl } from './form-control'; import { ControlConfig, FormControl } from './form-control';
import ShortUniqueId from 'short-unique-id';
export class FormGroup { export class FormGroup {
controls: FormControl[]; controls: FormControl[];
name?: string; name?: string;
id?: string;
constructor(controls: ControlConfig[], name = '') { constructor(controls: ControlConfig[], name = '') {
const uid = new ShortUniqueId({ length: 9 });
this.name = name; this.name = name;
this.id = 'arl-' + uid();
this.controls = controls this.controls = controls
.filter((control) => control.type !== 'submit') .filter((control) => control.type !== 'submit')
.map((control) => new FormControl(control)); .map((control) => new FormControl(control));