Skip to content

Commit

Permalink
[#58] Added Field as a molecule-level component. (#182)
Browse files Browse the repository at this point in the history
Co-authored-by: Joshua Fernandes <“joshua.1234511@yahoo.in”>
  • Loading branch information
joshua-salsadigital and Joshua Fernandes committed Jun 4, 2024
1 parent d0311f5 commit 665b8ad
Show file tree
Hide file tree
Showing 9 changed files with 214 additions and 280 deletions.
26 changes: 13 additions & 13 deletions components/00-base/base.utils.js
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ import CivicThemeInput from '../01-atoms/input/input.twig';
import CivicThemeSelect from '../01-atoms/select/select.twig';
import CivicThemeCheckbox from '../01-atoms/checkbox/checkbox.twig';
import CivicThemeRadio from '../01-atoms/radio/radio.twig';
import CivicThemeFormElement from '../02-molecules/form-element/form-element.twig';
import CivicThemeField from '../02-molecules/field/field.twig';
import CivicThemeLabel from '../01-atoms/label/label.twig';

// =============================================================================
Expand Down Expand Up @@ -122,10 +122,10 @@ export const randomDropdownFilter = () => {
return filters[Math.floor(Math.random() * filters.length)];
};

export const randomFormElement = (inputType, options, theme, rand, itr) => {
export const randomField = (inputType, options, theme, rand, itr) => {
const isCheckboxOrRadio = inputType === 'checkbox' || inputType === 'radio';

const formElementOptions = {
const FieldOptions = {
theme,
type: inputType,
label: CivicThemeLabel({
Expand All @@ -139,7 +139,7 @@ export const randomFormElement = (inputType, options, theme, rand, itr) => {
description: {
content: options.description ? `Input description ${itr + 1}${rand ? ` ${randomText(randomInt(4, 10))}` : ''}` : '',
},
children: [],
control: [],
attributes: options.form_element_attributes,
};
let attributes = `id="form-element-${itr}"`;
Expand All @@ -157,25 +157,25 @@ export const randomFormElement = (inputType, options, theme, rand, itr) => {

switch (inputType) {
case 'radio':
formElementOptions.children.push(CivicThemeRadio(inputOptions));
FieldOptions.control.push(CivicThemeRadio(inputOptions));
break;
case 'checkbox':
formElementOptions.children.push(CivicThemeCheckbox(inputOptions));
FieldOptions.control.push(CivicThemeCheckbox(inputOptions));
break;
case 'select':
formElementOptions.children.push(CivicThemeSelect({
FieldOptions.control.push(CivicThemeSelect({
...inputOptions,
options: inputOptions.value,
}));
break;
default:
formElementOptions.children.push(CivicThemeInput(inputOptions));
FieldOptions.control.push(CivicThemeInput(inputOptions));
}

return CivicThemeFormElement(formElementOptions);
return CivicThemeField(FieldOptions);
};

export const randomFormElements = (count, theme, rand) => {
export const randomFields = (count, theme, rand) => {
rand = rand || false;

const inputTypes = [
Expand All @@ -190,12 +190,12 @@ export const randomFormElements = (count, theme, rand) => {

const requiredOptions = ['required', ''];

const formElements = [];
const Fields = [];
for (let i = 0; i < count; i++) {
const inputType = inputTypes[Math.floor(Math.random() * inputTypes.length)];
const required = [Math.floor(Math.random() * requiredOptions.length)];

formElements.push(randomFormElement(
Fields.push(randomField(
inputType,
{
required,
Expand All @@ -206,7 +206,7 @@ export const randomFormElements = (count, theme, rand) => {
));
}

return formElements;
return Fields;
};

export const randomLinks = (count, length, domain, prefix) => {
Expand Down
4 changes: 2 additions & 2 deletions components/01-atoms/fieldset/fieldset.stories.js
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { knobBoolean, knobNumber, knobRadios, knobText, randomFormElements, shouldRender } from '../../00-base/base.utils';
import { knobBoolean, knobNumber, knobRadios, knobText, randomFields, shouldRender } from '../../00-base/base.utils';
import CivicThemeFieldset from './fieldset.twig';

export default {
Expand Down Expand Up @@ -38,7 +38,7 @@ export const Fieldset = (props = {}) => {

const combinedKnobs = {
...knobs,
children: randomFormElements(numOfElements, knobs.theme, true).join(''),
children: randomFields(numOfElements, knobs.theme, true).join(''),
};

return shouldRender(props)
Expand Down
File renamed without changes.
93 changes: 93 additions & 0 deletions components/02-molecules/field/field.stories.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,93 @@
import { knobBoolean, knobRadios, knobText, shouldRender } from '../../00-base/base.utils';
import CivicThemeField from './field.twig';
import { Select } from '../../01-atoms/select/select.stories';
import { Textfield } from '../../01-atoms/textfield/textfield.stories';
import { Textarea } from '../../01-atoms/textarea/textarea.stories';
import { Checkbox } from '../../01-atoms/checkbox/checkbox.stories';

export default {
title: 'Molecules/Field',
parameters: {
layout: 'centered',
},
};

export const Field = (props = {}) => {
const knobs = {
theme: knobRadios(
'Theme',
{
Light: 'light',
Dark: 'dark',
},
'light',
props.theme,
props.knobTab,
),
type: knobRadios(
'Type',
{
Textfield: 'textfield',
Textarea: 'textarea',
Select: 'select',
Checkbox: 'checkbox',
Hidden: 'hidden',
Other: 'other',
},
'textfield',
props.type,
props.knobTab,
),
direction: knobRadios(
'Direction',
{
Horizontal: 'horizontal',
Vertical: 'vertical',
},
'vertical',
props.direction,
props.knobTab,
),
control_direction: knobRadios(
'Control direction (for group controls)',
{
Horizontal: 'horizontal',
Vertical: 'vertical',
},
'vertical',
props.control_direction,
props.knobTab,
),
label: knobText('Label', 'Field label', props.label, props.knobTab),
description: knobText('Description', 'Content sample with long text that spans on the multiple lines to test text vertical spacing', props.description, props.knobTab),
is_required: knobBoolean('Required', false, props.is_required, props.knobTab),
is_disabled: knobBoolean('Disabled', false, props.is_disabled, props.knobTab),
is_invalid: knobBoolean('Invalid', false, props.is_invalid, props.knobTab),
message: knobText('Message', 'Content sample with long text that spans on the multiple lines to test text vertical spacing', props.message, props.knobTab),
modifier_class: `story-wrapper-size--medium ${knobText('Additional class', '', props.modifier_class, props.knobTab)}`,
attributes: knobText('Additional attributes', '', props.attributes, props.knobTab),
};

switch (knobs.type) {
case 'textfield':
knobs.control = Textfield(knobs);
break;

case 'textarea':
knobs.control = Textarea(knobs);
break;

case 'select':
knobs.control = Select(knobs);
break;

case 'checkbox':
knobs.control = Checkbox(knobs);
break;

default:
knobs.control = Textfield(knobs);
}

return shouldRender(props) ? CivicThemeField(knobs) : knobs;
};
102 changes: 102 additions & 0 deletions components/02-molecules/field/field.twig
Original file line number Diff line number Diff line change
@@ -0,0 +1,102 @@
{#
/**
* @file
* Field component.
*
* Used as a main entry point to build any form fields.
*
* Variables:
* - theme: [string] Theme: light, dark.
* - type: [string] Type defined by a control type.
* - direction: [string] Elements direction: vertical or horizontal.
* - control_direction: [string] Elements direction within a control (if the control supports it): vertical or horizontal.
* - label: [string] Label.
* - description: [string] Description.
* - is_required: [boolean] Denote if the field is required.
* - is_invalid: [boolean] Denote if the field is invalid.
* - is_disabled: [boolean] Denote if the field is disabled.
* - control: [object] Object with control's properties:
* - name: [string] DOM name.
* - value: [string] DOM value.
* - for: [string] Which component this label belongs to.
* - attributes: [string] Additional attributes.
* - modifier_class: [string] Additional classes.
* - items: [array] An array of items for group controls. Each item consists of:
* - label: [string] Child label.
* - value: [string] Child DOM value.
* - for: [string] Which component this label belongs to.
* - is_checked: [boolean] Denote if the component is checked.
* - is_disabled: [boolean] Denote if the component is disabled.
* - is_invalid: [boolean] Denote if the component is invalid.
* - attributes: [string] Additional attributes.
* - modifier_class: [string] Additional classes.
*/
#}

{% set type = type not in ['textfield', 'textarea', 'select', 'radio-group', 'checkbox', 'checkbox-group', 'hidden'] ? 'hidden' : type %}
{% set theme_class = 'ct-theme-%s'|format(theme|default('light')) %}
{% set type_class = 'ct-field--%s'|format(type|default('input')) %}
{% set direction_class = 'ct-field--%s'|format(direction|default('horizontal')) %}
{% set is_required_class = is_required ? 'ct-field--required' : '' %}
{% set is_disabled_class = is_disabled ? 'ct-field--disabled' : '' %}
{% set modifier_class = '%s %s %s %s %s %s'|format(theme_class, type_class, direction_class, is_required_class, is_disabled_class, modifier_class|default('')) %}

{% set control = control|merge({
theme: theme,
is_disabled: is_disabled,
is_invalid: is_invalid,
is_required: is_required,
modifier_class: 'ct-field__control ' ~ control.modifier_class,
}) %}

<div class="ct-field {{ modifier_class }}" {% if attributes %}{{ attributes|raw }}{% endif %}>
{% set label_padded_class = type in ['textfield', 'textarea', 'select'] and direction == 'horizontal' ? 'ct-field__label--padded' : '' %}
{% if label is not empty and type != 'hidden' %}
{% include '@atoms/label/label.twig' with {
theme: theme,
content: label,
is_required: is_required,
for: control.for ? control.for : null,
modifier_class: 'ct-field__label ' ~ label_padded_class,
} only %}
{% endif %}

<div class="ct-field__wrapper">
{% if type == 'textfield' %}
{% include '@atoms/textfield/textfield.twig' with control only %}
{% elseif type == 'textarea' %}
{% include '@atoms/textarea/textarea.twig' with control only %}
{% elseif type == 'select' %}
{% include '@atoms/select/select.twig' with control only %}
{% elseif type == 'checkbox' %}
{% include '@atoms/checkbox/checkbox.twig' with control only %}
{% else %}
<input
type="{{ type }}"
{# name="{{ control.name }}" #}
{% if control.modifier_class %}class="{{ control.modifier_class }}"{% endif %}
{% if control.value %}value="{{ control.value }}"{% endif %}
{% if control.for %}id="{{ control.for }}"{% endif %}
{% if control.attributes %}{{ control.attributes|raw }}{% endif %}
/>
{% endif %}

{% if description is not empty and type != 'hidden' %}
{% include '@atoms/field-description/field-description.twig' with {
theme: theme,
size: 'regular',
content: description,
modifier_class: 'ct-field__description',
} only %}
{% endif %}

{% if (message or is_invalid) and type != 'hidden' %}
{% include '@atoms/field-message/field-message.twig' with {
theme: theme,
type: is_invalid ? 'error' : 'information',
content: message ? message : (is_invalid ? (label is not empty ? 'Field <em>' ~ label ~ '</em> has an error' : 'Field has an error') : null),
modifier_class: 'ct-field__message',
} only %}
{% endif %}
</div>
</div>
Loading

1 comment on commit 665b8ad

@github-actions
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please sign in to comment.