- {
- control.label && control.labelPosition === 'left' && (
-
- )
- }
-
- {
- !showOnlyLabel && (
-
- )
- }
-
- {
- showErrors &&
- hasErrors &&
- control.errors.map((error) => (
-
- {error.error} {error.limit}
-
- ))
- }
-
- {
- control.label && control.labelPosition === 'right' && (
-
- )
- }
+
+
-
-
diff --git a/packages/form/components/Form.astro b/packages/form/components/Form.astro
new file mode 100644
index 0000000..8f57b57
--- /dev/null
+++ b/packages/form/components/Form.astro
@@ -0,0 +1,58 @@
+---
+import type { Submit } from 'common/types';
+import { FormGroup, FormControl } from '../core';
+import FieldSet from './FieldSet.astro';
+import Field from './Field.astro';
+
+export interface Props {
+ formGroups: FormGroup | FormGroup[];
+ submitControl?: Submit;
+ theme?: 'light' | 'dark';
+ showValidationHints?: boolean;
+}
+
+const { submitControl, formGroups = [], theme, showValidationHints = false } = Astro.props;
+
+const formTheme = theme ?? 'light';
+const formName = Array.isArray(formGroups) ? null : formGroups?.name || null;
+---
+
+
+
+
+
+
diff --git a/packages/form/components/Label.astro b/packages/form/components/Label.astro
new file mode 100644
index 0000000..be6f703
--- /dev/null
+++ b/packages/form/components/Label.astro
@@ -0,0 +1,36 @@
+---
+import type { FormControl } from '../core';
+
+export interface Props {
+ control: FormControl;
+ showValidationHints: boolean;
+ showErrors?: boolean; // feature flag for showing validation errors
+}
+
+const { control, showValidationHints } = Astro.props;
+
+const { validators = [] } = control;
+
+const isRequired: boolean = showValidationHints && validators.includes('validator-required');
+---
+
+ {
+ control.label && control.labelPosition === 'left' && (
+
+ )
+ }
+
+
+
+ {
+ control.label && control.labelPosition === 'right' && (
+
+ )
+ }
+
diff --git a/packages/form/components/controls/Input.astro b/packages/form/components/controls/Input.astro
new file mode 100644
index 0000000..db834fe
--- /dev/null
+++ b/packages/form/components/controls/Input.astro
@@ -0,0 +1,40 @@
+---
+/**
+ * DEFAULT INPUT COMPONENT
+ */
+import type { FormControl } from '../../core/form-control';
+
+export interface Props {
+ control: FormControl;
+}
+
+const { control } = Astro.props;
+
+const { validators = [] } = control;
+
+const hasErrors: boolean = !!control.errors?.length;
+
+const validatorAttributes: Record
= validators?.reduce((prev, validator) => {
+ const split: string[] = validator.split(':');
+ const label: string = `data-${split[0]}` || 'invalid';
+ const value: string | null = split.length > 1 ? split[1] ?? null : 'true';
+
+ return {
+ ...prev,
+ [label]: value,
+ };
+}, {});
+---
+
+
diff --git a/packages/form/components/controls/RadioGroup.astro b/packages/form/components/controls/RadioGroup.astro
new file mode 100644
index 0000000..1961b18
--- /dev/null
+++ b/packages/form/components/controls/RadioGroup.astro
@@ -0,0 +1,31 @@
+---
+/**
+ * RADIO GROUP COMPONENT
+ */
+import type { Radio } from 'common/types';
+
+export interface Props {
+ control: Radio;
+}
+
+const { control } = Astro.props;
+
+const options = control.value.map((option) => {
+ if (typeof option === 'string') {
+ return {
+ label: option,
+ value: option,
+ };
+ }
+ return option;
+});
+---
+
+{
+ options.map((option) => (
+
+ {' '}
+ {option.label}
+
+ ))
+}
diff --git a/packages/form/core/form-control.ts b/packages/form/core/form-control.ts
index 638b36e..5360f69 100644
--- a/packages/form/core/form-control.ts
+++ b/packages/form/core/form-control.ts
@@ -13,15 +13,14 @@ export type ControlConfig = ControlBase | Checkbox | Radio | Submit | Button;
export class FormControl {
private _name = '';
- private _id = '';
private _type: ControlType = 'text';
private _value?: string | number | null | string[] | RadioOption[];
- private _label?: string;
+ private _label = '';
private _labelPosition?: 'right' | 'left' = 'left';
private _isValid = true;
private _isPristine = true;
- private _placeholder?: string;
- private _validators?: string[];
+ private _placeholder: string | null = null;
+ private _validators: string[] = [];
private _errors: ValidationError[] = [];
private validate: (value: string, validators: string[]) => ValidationError[] = (
@@ -34,18 +33,25 @@ export class FormControl {
};
constructor(private config: ControlConfig) {
- const { name, id, type, value, label, labelPosition, placeholder, validators = [] } = config;
+ const {
+ name,
+ type = 'text',
+ value = null,
+ label = '',
+ labelPosition = 'left',
+ placeholder = null,
+ validators = [],
+ } = config;
+
this._name = name;
- this._id = id ?? name;
- this._type = type ?? 'text';
- this._value = value ?? null;
- this._label = label ?? '';
- this._labelPosition = labelPosition ?? 'left';
- this._placeholder = placeholder ?? '';
+ this._type = type;
+ this._value = value;
+ this._label = label;
+ this._labelPosition = labelPosition;
+ this._placeholder = placeholder;
this._validators = validators;
// dynamic import of the validator package
- // if user did not install the validator, then errors should be empty
import('@astro-reactive/validator').then((validator) => {
if (validator) {
this.validate = validator.validate;
@@ -53,6 +59,7 @@ export class FormControl {
const valueStr: string = this._value?.toString() || '';
this._errors = this.validate(valueStr, validators);
} else {
+ // if user did not install the validator, then errors should be empty
this._errors = [];
}
});
@@ -62,10 +69,6 @@ export class FormControl {
return this._name;
}
- get id() {
- return this._id;
- }
-
get type() {
return this._type;
}
diff --git a/packages/form/index.ts b/packages/form/index.ts
index 0b332f4..9b8a12b 100644
--- a/packages/form/index.ts
+++ b/packages/form/index.ts
@@ -1,5 +1,5 @@
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
// @ts-ignore
-import Form from './Form.astro';
+import Form from './components/Form.astro';
export default Form;
export * from './core';
diff --git a/packages/form/package.json b/packages/form/package.json
index 33fe571..8459e1a 100644
--- a/packages/form/package.json
+++ b/packages/form/package.json
@@ -16,7 +16,6 @@
"files": [
"core/",
"components/",
- "Form.astro",
"index.ts"
],
"keywords": [
diff --git a/packages/form/test/Field.astro.test.js b/packages/form/test/Field.astro.test.js
index fe31c18..5ff26b2 100644
--- a/packages/form/test/Field.astro.test.js
+++ b/packages/form/test/Field.astro.test.js
@@ -1,6 +1,7 @@
import { expect } from 'chai';
import { beforeEach, describe, it } from 'mocha';
import { getComponentOutput } from 'astro-component-tester';
+import { cleanString } from './utils/index.js';
describe('Field.astro test', () => {
let component;
@@ -29,28 +30,6 @@ describe('Field.astro test', () => {
expect(actualResult).to.contain(expectedLabel);
});
- it('Should render only label', async () => {
- // arrange
- const expectedLabel = 'TestLabel';
- const props = {
- control: {
- label: expectedLabel,
- name: 'username',
- labelPosition: 'left',
- },
- showValidationHints: false,
- showOnlyLabel: true,
- };
-
- // act
- component = await getComponentOutput('./components/Field.astro', props);
- const actualResult = cleanString(component.html);
-
- // assert
- expect(actualResult).to.contain(expectedLabel);
- expect(actualResult).to.not.contain('input');
- });
-
it('Should render required fields with asterisk in label when showValidationHints is true', async () => {
// arrange
const expectedLabel = 'TestLabel';
@@ -75,7 +54,7 @@ describe('Field.astro test', () => {
it('Should server-render validation error attributes', async () => {
// arrange
- const expectedClass = 'has-errors';
+ const expectedResult = 'data-validator-haserrors="true"';
const props = {
control: {
label: 'FAKE LABEL',
@@ -97,10 +76,6 @@ describe('Field.astro test', () => {
const actualResult = cleanString(component.html);
// assert
- expect(actualResult).to.contain(expectedClass);
+ expect(actualResult).to.contain(expectedResult);
});
});
-
-function cleanString(str) {
- return str.replace(/\s/g, '');
-}
diff --git a/packages/form/test/FieldSet.astro.test.js b/packages/form/test/FieldSet.astro.test.js
index 2bbcd78..879ee39 100644
--- a/packages/form/test/FieldSet.astro.test.js
+++ b/packages/form/test/FieldSet.astro.test.js
@@ -1,6 +1,7 @@
import { expect } from 'chai';
import { beforeEach, describe, it } from 'mocha';
import { getComponentOutput } from 'astro-component-tester';
+import { cleanString } from './utils/index.js';
describe('FieldSet.astro test', () => {
let component;
@@ -36,7 +37,3 @@ describe('FieldSet.astro test', () => {
expect(actualResult).to.contain(``);
});
});
-
-function cleanString(str) {
- return str.replace(/\s/g, '');
-}
diff --git a/packages/form/test/Form.astro.test.js b/packages/form/test/Form.astro.test.js
index 6e390c6..d514485 100644
--- a/packages/form/test/Form.astro.test.js
+++ b/packages/form/test/Form.astro.test.js
@@ -1,6 +1,7 @@
import { expect } from 'chai';
import { describe, beforeEach, it } from 'mocha';
import { getComponentOutput } from 'astro-component-tester';
+import { cleanString } from './utils/index.js';
describe('Form.astro test', () => {
let component;
@@ -15,7 +16,7 @@ describe('Form.astro test', () => {
const expectedCount = 0;
const element = /