refactor: form package cleanup (#135)
* refactor: form package cleanup * refactor: organize components folder
This commit is contained in:
parent
b9995b8a63
commit
3d361fcb5b
18 changed files with 287 additions and 234 deletions
|
@ -34,8 +34,8 @@ const form = new FormGroup([
|
|||
label: "Agreement",
|
||||
type: "radio",
|
||||
value: [
|
||||
{ label: "Agree", value: "yes" },
|
||||
{ label: "Disagree", value: "no", labelPosition: "right" },
|
||||
{ label: "Agree", value: "yes", checked: true },
|
||||
{ label: "Disagree", value: "no" },
|
||||
],
|
||||
},
|
||||
]);
|
||||
|
@ -61,9 +61,12 @@ form.get("is-awesome")?.setValue("checked");
|
|||
|
||||
// setting an invalid value will cause errors as server-rendered
|
||||
form.get("email")?.setValue("invalid-email");
|
||||
|
||||
// switch between light and dark mode
|
||||
const theme = "dark";
|
||||
---
|
||||
|
||||
<html lang="en">
|
||||
<html lang="en" class={theme}>
|
||||
<head>
|
||||
<meta charset="utf-8" />
|
||||
<link rel="icon" type="image/svg+xml" href="/favicon.svg" />
|
||||
|
@ -71,8 +74,15 @@ form.get("email")?.setValue("invalid-email");
|
|||
<meta name="generator" content={Astro.generator} />
|
||||
<title>Astro</title>
|
||||
</head>
|
||||
<body>
|
||||
<body class={theme}>
|
||||
<nav>Examples: <a href="/pizza-delivery">Pizza Delivery</a></nav>
|
||||
<h1>Astro Reactive Form</h1>
|
||||
<Form showValidationHints={true} formGroups={form} />
|
||||
<Form showValidationHints={true} formGroups={form} theme={theme} />
|
||||
<style>
|
||||
html.dark,
|
||||
body.dark {
|
||||
color-scheme: dark;
|
||||
}
|
||||
</style>
|
||||
</body>
|
||||
</html>
|
||||
|
|
|
@ -98,6 +98,7 @@ infoForm.name = "Customer Info";
|
|||
<title>Astro</title>
|
||||
</head>
|
||||
<body>
|
||||
<nav><a href="/">Home</a></nav>
|
||||
<h1>Pizza Form Demo</h1>
|
||||
<Form
|
||||
showValidationHints={true}
|
||||
|
|
|
@ -46,7 +46,7 @@ export interface Radio extends Omit<ControlBase, "value"> {
|
|||
value: string[] | RadioOption[];
|
||||
}
|
||||
|
||||
export interface RadioOption extends Omit<ControlBase, "name"> {
|
||||
export interface RadioOption {
|
||||
label: string;
|
||||
value: string;
|
||||
checked?: boolean;
|
||||
|
|
|
@ -1,84 +0,0 @@
|
|||
---
|
||||
import type { Submit, Radio, RadioOption } from 'common/types';
|
||||
import { FormGroup, FormControl } from './core';
|
||||
import FieldSet from './components/FieldSet.astro';
|
||||
import Field from './components/Field.astro';
|
||||
|
||||
export interface Props {
|
||||
formGroups: FormGroup | FormGroup[];
|
||||
submitControl?: Submit;
|
||||
theme?: 'light' | 'dark';
|
||||
showValidationHints?: boolean;
|
||||
}
|
||||
|
||||
const { submitControl, formGroups: form, theme, showValidationHints = false } = Astro.props;
|
||||
|
||||
const formTheme = theme ?? 'light';
|
||||
const formName = Array.isArray(form) ? null : form?.name || null;
|
||||
---
|
||||
|
||||
<form class={formTheme} name={formName} id={formName}>
|
||||
{
|
||||
Array.isArray(form)
|
||||
? form?.map((group) => <FieldSet showValidationHints={showValidationHints} group={group} />)
|
||||
: form?.controls.map((control) =>
|
||||
control.type === 'radio' ? (
|
||||
[
|
||||
<Field
|
||||
showValidationHints={showValidationHints}
|
||||
control={control}
|
||||
showOnlyLabel={true}
|
||||
/>,
|
||||
...(control as Radio)?.value?.map((v: string | RadioOption) => (
|
||||
<Field
|
||||
showValidationHints={showValidationHints}
|
||||
control={
|
||||
typeof v === 'string'
|
||||
? new FormControl({
|
||||
name: control.name,
|
||||
type: 'radio',
|
||||
id: control.name + v,
|
||||
label: v,
|
||||
value: v,
|
||||
})
|
||||
: new FormControl({
|
||||
name: control.name,
|
||||
type: 'radio',
|
||||
id: control.name + v.label,
|
||||
...(v as RadioOption),
|
||||
})
|
||||
}
|
||||
/>
|
||||
)),
|
||||
]
|
||||
) : (
|
||||
<Field showValidationHints={showValidationHints} control={control} />
|
||||
)
|
||||
)
|
||||
}
|
||||
{
|
||||
submitControl && (
|
||||
<Field showValidationHints={showValidationHints} control={new FormControl(submitControl)} />
|
||||
)
|
||||
}
|
||||
</form>
|
||||
|
||||
<style>
|
||||
.light {
|
||||
/**
|
||||
* run dev server with: "npm start",
|
||||
* then open browser to "localhost:3000"
|
||||
* add a class="light" to the <form> element above to test changes
|
||||
* INSERT STYLES FOR LIGHT MODE BELOW: */
|
||||
}
|
||||
|
||||
.dark {
|
||||
/**
|
||||
* run dev server with: "npm start",
|
||||
* then open browser to "localhost:3000"
|
||||
* add a class="dark" to the <form> element above to test changes
|
||||
* INSERT STYLES FOR DARK MODE BELOW: */
|
||||
background-color: #333;
|
||||
color: white;
|
||||
}
|
||||
</style>
|
17
packages/form/components/Errors.astro
Normal file
17
packages/form/components/Errors.astro
Normal file
|
@ -0,0 +1,17 @@
|
|||
---
|
||||
import type { ValidationError } from 'common/types';
|
||||
|
||||
export interface Props {
|
||||
errors: ValidationError[];
|
||||
}
|
||||
|
||||
const { errors = [] } = Astro.props;
|
||||
---
|
||||
|
||||
{
|
||||
errors.map((error) => (
|
||||
<li>
|
||||
{error.error} {error.limit}
|
||||
</li>
|
||||
))
|
||||
}
|
|
@ -1,98 +1,34 @@
|
|||
---
|
||||
/**
|
||||
* DEFAULT CONTROL COMPONENT
|
||||
*/
|
||||
import type { Radio } from 'common/types';
|
||||
import type { FormControl } from '../core/form-control';
|
||||
import Input from './controls/Input.astro';
|
||||
import RadioGroup from './controls/RadioGroup.astro';
|
||||
import Errors from './Errors.astro';
|
||||
import Label from './Label.astro';
|
||||
|
||||
export interface Props {
|
||||
control: FormControl;
|
||||
showValidationHints: boolean;
|
||||
showErrors?: boolean;
|
||||
showOnlyLabel?: boolean;
|
||||
showErrors?: boolean; // feature flag for showing validation errors
|
||||
}
|
||||
|
||||
const { control, showValidationHints, showErrors = false, showOnlyLabel = false } = Astro.props;
|
||||
const { control, showValidationHints, showErrors = false } = Astro.props;
|
||||
|
||||
const { validators = [] } = control;
|
||||
|
||||
const isRequired: boolean = showValidationHints && validators.includes('validator-required');
|
||||
|
||||
const hasErrors: boolean = showValidationHints && !!control.errors?.length;
|
||||
|
||||
const validatorAttributes: Record<string, string> = 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,
|
||||
};
|
||||
},
|
||||
|
||||
{}
|
||||
);
|
||||
const hasErrors: boolean | null = !!control.errors?.length;
|
||||
---
|
||||
|
||||
<div
|
||||
class="field"
|
||||
data-validator-hints={showValidationHints?.toString()}
|
||||
data-validator-haserrors={hasErrors ? hasErrors?.toString() : null}
|
||||
>
|
||||
{
|
||||
control.label && control.labelPosition === 'left' && (
|
||||
<label for={control?.id ?? control.name}>
|
||||
{isRequired && <span>*</span>}
|
||||
{control.label}
|
||||
</label>
|
||||
)
|
||||
}
|
||||
|
||||
{
|
||||
!showOnlyLabel && (
|
||||
<input
|
||||
name={control.name}
|
||||
id={control?.id ?? control.name}
|
||||
type={control.type}
|
||||
value={control.value?.toString()}
|
||||
checked={control.value === 'checked'}
|
||||
placeholder={control.placeholder}
|
||||
data-label={control.label}
|
||||
data-label-position={control.labelPosition}
|
||||
data-validator-haserrors={hasErrors ? hasErrors?.toString() : null}
|
||||
class:list={[{ 'has-errors': hasErrors }]}
|
||||
{...validatorAttributes}
|
||||
/>
|
||||
)
|
||||
}
|
||||
|
||||
{
|
||||
showErrors &&
|
||||
hasErrors &&
|
||||
control.errors.map((error) => (
|
||||
<li>
|
||||
{error.error} {error.limit}
|
||||
</li>
|
||||
))
|
||||
}
|
||||
|
||||
{
|
||||
control.label && control.labelPosition === 'right' && (
|
||||
<label for={control?.id ?? control.name}>
|
||||
{isRequired && <span>*</span>}
|
||||
{control.label}
|
||||
</label>
|
||||
)
|
||||
}
|
||||
<div class="field" data-validator-haserrors={hasErrors ? hasErrors.toString() : null}>
|
||||
<Label control={control} showValidationHints={showValidationHints}>
|
||||
{
|
||||
control.type === 'radio' ? (
|
||||
<RadioGroup control={control as Radio} />
|
||||
) : (
|
||||
<Input control={control} />
|
||||
)
|
||||
}
|
||||
{showErrors && <Errors errors={control.errors} />}
|
||||
</Label>
|
||||
</div>
|
||||
|
||||
<style>
|
||||
/**
|
||||
TODO: remove usage of data-validator-haserrors,
|
||||
class has-errors is sufficient
|
||||
*/
|
||||
[data-validator-hints='true'][data-validator-haserrors='true'],
|
||||
[data-validator-hints='true'] [data-validator-haserrors='true'],
|
||||
.has-errors {
|
||||
color: red;
|
||||
border-color: red;
|
||||
}
|
||||
</style>
|
||||
|
|
58
packages/form/components/Form.astro
Normal file
58
packages/form/components/Form.astro
Normal file
|
@ -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;
|
||||
---
|
||||
|
||||
<form
|
||||
class={formTheme}
|
||||
name={formName}
|
||||
id={formName}
|
||||
data-validator-hints={showValidationHints.toString()}
|
||||
>
|
||||
{
|
||||
Array.isArray(formGroups)
|
||||
? formGroups?.map((group) => (
|
||||
<FieldSet showValidationHints={showValidationHints} group={group} />
|
||||
))
|
||||
: formGroups?.controls.map((control) => (
|
||||
<Field showValidationHints={showValidationHints} control={control} />
|
||||
))
|
||||
}
|
||||
{
|
||||
submitControl && (
|
||||
<Field showValidationHints={showValidationHints} control={new FormControl(submitControl)} />
|
||||
)
|
||||
}
|
||||
</form>
|
||||
|
||||
<style>
|
||||
.light {
|
||||
color-scheme: light;
|
||||
}
|
||||
|
||||
.dark {
|
||||
color-scheme: dark;
|
||||
}
|
||||
</style>
|
||||
|
||||
<style is:global>
|
||||
[data-validator-hints='true'][data-validator-haserrors='true'],
|
||||
[data-validator-hints='true'] [data-validator-haserrors='true'] {
|
||||
color: red;
|
||||
border-color: red;
|
||||
}
|
||||
</style>
|
36
packages/form/components/Label.astro
Normal file
36
packages/form/components/Label.astro
Normal file
|
@ -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' && (
|
||||
<label for={control.name}>
|
||||
{isRequired && <span>*</span>}
|
||||
{control.label}
|
||||
</label>
|
||||
)
|
||||
}
|
||||
|
||||
<slot />
|
||||
|
||||
{
|
||||
control.label && control.labelPosition === 'right' && (
|
||||
<label for={control.name}>
|
||||
{isRequired && <span>*</span>}
|
||||
{control.label}
|
||||
</label>
|
||||
)
|
||||
}
|
||||
|
40
packages/form/components/controls/Input.astro
Normal file
40
packages/form/components/controls/Input.astro
Normal file
|
@ -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<string, string> = 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,
|
||||
};
|
||||
}, {});
|
||||
---
|
||||
|
||||
<input
|
||||
name={control.name}
|
||||
id={control.name}
|
||||
type={control.type}
|
||||
value={control.value?.toString()}
|
||||
checked={control.value === 'checked'}
|
||||
placeholder={control.placeholder}
|
||||
data-label={control.label}
|
||||
data-label-position={control.labelPosition}
|
||||
data-validator-haserrors={hasErrors.toString()}
|
||||
{...validatorAttributes}
|
||||
/>
|
31
packages/form/components/controls/RadioGroup.astro
Normal file
31
packages/form/components/controls/RadioGroup.astro
Normal file
|
@ -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) => (
|
||||
<div class="radio-option">
|
||||
<input type="radio" name={control.name} value={option.value} checked={option.checked} />{' '}
|
||||
{option.label}
|
||||
</div>
|
||||
))
|
||||
}
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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';
|
||||
|
|
|
@ -16,7 +16,6 @@
|
|||
"files": [
|
||||
"core/",
|
||||
"components/",
|
||||
"Form.astro",
|
||||
"index.ts"
|
||||
],
|
||||
"keywords": [
|
||||
|
|
|
@ -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, '');
|
||||
}
|
||||
|
|
|
@ -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(`<legend>${expectedName}</legend>`);
|
||||
});
|
||||
});
|
||||
|
||||
function cleanString(str) {
|
||||
return str.replace(/\s/g, '');
|
||||
}
|
||||
|
|
|
@ -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 = /<fieldset>/g;
|
||||
const props = { formGroups: undefined };
|
||||
component = await getComponentOutput('./Form.astro', props);
|
||||
component = await getComponentOutput('./components/Form.astro', props);
|
||||
|
||||
// act
|
||||
const actualResult = cleanString(component.html);
|
||||
|
@ -30,7 +31,7 @@ describe('Form.astro test', () => {
|
|||
const expectedCount = 0;
|
||||
const element = /<fieldset>/g;
|
||||
const props = { formGroups: [] };
|
||||
component = await getComponentOutput('./Form.astro', props);
|
||||
component = await getComponentOutput('./components/Form.astro', props);
|
||||
|
||||
// act
|
||||
const actualResult = cleanString(component.html);
|
||||
|
@ -54,7 +55,7 @@ describe('Form.astro test', () => {
|
|||
],
|
||||
};
|
||||
const props = { formGroups: Array(expectedCount).fill(fakeFormGroup) };
|
||||
component = await getComponentOutput('./Form.astro', props);
|
||||
component = await getComponentOutput('./components/Form.astro', props);
|
||||
|
||||
// act
|
||||
const actualResult = cleanString(component.html);
|
||||
|
@ -78,7 +79,7 @@ describe('Form.astro test', () => {
|
|||
],
|
||||
};
|
||||
const props = { formGroups: fakeFormGroup };
|
||||
component = await getComponentOutput('./Form.astro', props);
|
||||
component = await getComponentOutput('./components/Form.astro', props);
|
||||
|
||||
// act
|
||||
const actualResult = cleanString(component.html);
|
||||
|
@ -89,7 +90,3 @@ describe('Form.astro test', () => {
|
|||
});
|
||||
});
|
||||
});
|
||||
|
||||
function cleanString(str) {
|
||||
return str.replace(/\s/g, '');
|
||||
}
|
||||
|
|
34
packages/form/test/RadioGroup.astro.test.js
Normal file
34
packages/form/test/RadioGroup.astro.test.js
Normal file
|
@ -0,0 +1,34 @@
|
|||
import { expect } from 'chai';
|
||||
import { describe, beforeEach, it } from 'mocha';
|
||||
import { getComponentOutput } from 'astro-component-tester';
|
||||
import { cleanString } from './utils/index.js';
|
||||
|
||||
describe('RadioGroup.astro test', () => {
|
||||
let component;
|
||||
|
||||
beforeEach(() => {
|
||||
component = undefined;
|
||||
});
|
||||
it('Should render all radio options', async () => {
|
||||
// arrange
|
||||
const expectedOptions = 3;
|
||||
const element = /radio-option/g;
|
||||
const props = {
|
||||
control: {
|
||||
label: 'FAKE LABEL',
|
||||
name: 'FAKE NAME',
|
||||
type: 'radio',
|
||||
value: ['one', 'two', 'three'],
|
||||
},
|
||||
showValidationHints: true,
|
||||
};
|
||||
|
||||
// act
|
||||
component = await getComponentOutput('./components/controls/RadioGroup.astro', props);
|
||||
const actualResult = cleanString(component.html);
|
||||
const matches = actualResult.match(element) || [];
|
||||
|
||||
// assert
|
||||
expect(matches.length).to.equal(expectedOptions);
|
||||
});
|
||||
});
|
3
packages/form/test/utils/index.js
Normal file
3
packages/form/test/utils/index.js
Normal file
|
@ -0,0 +1,3 @@
|
|||
export function cleanString(str) {
|
||||
return str.replace(/\s/g, '');
|
||||
}
|
Loading…
Reference in a new issue