Skip to content

Commit

Permalink
Implemented dnn-fieldset component
Browse files Browse the repository at this point in the history
This externalizes the field wrappers into a reusable fieldset component (that potentially could also be used to wrap anything else).

After this PR is merged I'll refactor the exiting fields to consume this component, reduce code and style duplications.

Closes Implement a dnn-fieldset #994
  • Loading branch information
valadas committed Mar 9, 2024
1 parent dc3044a commit 2d1e792
Show file tree
Hide file tree
Showing 11 changed files with 407 additions and 24 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ export const DnnColorInput = /*@__PURE__*/createReactComponent<JSX.DnnColorInput
export const DnnColorPicker = /*@__PURE__*/createReactComponent<JSX.DnnColorPicker, HTMLDnnColorPickerElement>('dnn-color-picker');
export const DnnDropzone = /*@__PURE__*/createReactComponent<JSX.DnnDropzone, HTMLDnnDropzoneElement>('dnn-dropzone');
export const DnnExampleForm = /*@__PURE__*/createReactComponent<JSX.DnnExampleForm, HTMLDnnExampleFormElement>('dnn-example-form');
export const DnnFieldset = /*@__PURE__*/createReactComponent<JSX.DnnFieldset, HTMLDnnFieldsetElement>('dnn-fieldset');
export const DnnImageCropper = /*@__PURE__*/createReactComponent<JSX.DnnImageCropper, HTMLDnnImageCropperElement>('dnn-image-cropper');
export const DnnInput = /*@__PURE__*/createReactComponent<JSX.DnnInput, HTMLDnnInputElement>('dnn-input');
export const DnnModal = /*@__PURE__*/createReactComponent<JSX.DnnModal, HTMLDnnModalElement>('dnn-modal');
Expand Down
150 changes: 149 additions & 1 deletion packages/stencil-library/custom-elements.json
Original file line number Diff line number Diff line change
Expand Up @@ -659,6 +659,146 @@
}
]
},
{
"kind": "javascript-module",
"path": "src/components/dnn-fieldset/dnn-fieldset.tsx",
"declarations": [
{
"kind": "class",
"name": "dnn-fieldset.tsx",
"tagName": "dnn-fieldset",
"description": "A custom input component that wraps the html input element is a mobile friendly component that supports a label, some help text and other features.",
"attributes": [
{
"name": "disabled",
"type": {
"text": "boolean"
},
"description": "If true, the fieldset will display as disabled.",
"required": false
},
{
"name": "float-label",
"type": {
"text": "boolean"
},
"description": "If true, the label will float in the container, set false to show it on top.",
"required": false
},
{
"name": "focused",
"type": {
"text": "boolean"
},
"description": "If true the fieldset will display as focused.",
"required": false
},
{
"name": "help-text",
"type": {
"text": "string"
},
"description": "Can be used to show some help text about this field.",
"required": false
},
{
"name": "invalid",
"type": {
"text": "boolean"
},
"description": "If true, the fieldset will display as invalid.",
"required": false
},
{
"name": "label",
"type": {
"text": "string"
},
"description": "Sets the text of the fieldset label (caption).",
"required": false
}
],
"members": [
{
"kind": "method",
"name": "disable",
"description": "Sets the fieldset to a disabled state.",
"signature": "disable() => Promise<void>"
},
{
"kind": "method",
"name": "enable",
"description": "Sets the fieldset to an enabled state.",
"signature": "enable() => Promise<void>"
},
{
"kind": "method",
"name": "pinLabel",
"description": "Places the label on the top of the container.",
"signature": "pinLabel() => Promise<void>"
},
{
"kind": "method",
"name": "setBlurred",
"description": "Unsets the fieldset focused state.",
"signature": "setBlurred() => Promise<void>"
},
{
"kind": "method",
"name": "setFocused",
"description": "Sets the fieldset to the focused state.",
"signature": "setFocused() => Promise<void>"
},
{
"kind": "method",
"name": "setValidity",
"description": "Sets the validity of the field.",
"signature": "setValidity(valid: boolean, message?: string) => Promise<void>"
},
{
"kind": "method",
"name": "unpinLabel",
"description": "Places the label in the vertical middle of the container.",
"signature": "unpinLabel() => Promise<void>"
}
],
"events": [],
"slots": [
{
"name": "label-prefix",
"description": "Can be used to inject content before the labe."
},
{
"name": "label-suffix",
"description": "Can be used to inject content after the label."
}
],
"cssProperties": [
{
"name": "--background",
"description": "Defines the background color."
},
{
"name": "--control-radius",
"description": "Defines the radius for the control corners."
},
{
"name": "--danger-color",
"description": "Defines the danger color used for invalid data."
},
{
"name": "--focus-color",
"description": "Defines the color when the component is focused."
},
{
"name": "--foreground",
"description": "Defines the foreground color."
}
],
"cssParts": []
}
]
},
{
"kind": "javascript-module",
"path": "src/components/dnn-image-cropper/dnn-image-cropper.tsx",
Expand Down Expand Up @@ -911,7 +1051,7 @@
{
"kind": "method",
"name": "setCustomValidity",
"description": "",
"description": "Can be used to set a custom validity message.",
"signature": "setCustomValidity(message: string) => Promise<void>"
}
],
Expand Down Expand Up @@ -1291,6 +1431,14 @@
"tagName": "dnn-richtext",
"description": "",
"attributes": [
{
"name": "name",
"type": {
"text": "string"
},
"description": "Name of the field when used in a form.",
"required": false
},
{
"name": "value",
"type": {
Expand Down
28 changes: 22 additions & 6 deletions packages/stencil-library/src/components.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -220,6 +220,9 @@ export namespace Components {
*/
interface DnnExampleForm {
}
/**
* A custom input component that wraps the html input element is a mobile friendly component that supports a label, some help text and other features.
*/
interface DnnFieldset {
/**
* Sets the fieldset to a disabled state.
Expand All @@ -241,6 +244,10 @@ export namespace Components {
* If true the fieldset will display as focused.
*/
"focused": boolean;
/**
* Can be used to show some help text about this field.
*/
"helpText": string;
/**
* If true, the fieldset will display as invalid.
*/
Expand All @@ -262,13 +269,9 @@ export namespace Components {
*/
"setFocused": () => Promise<void>;
/**
* Sets the fieldset to an invalid state.
*/
"setInvalid": () => Promise<void>;
/**
* Sets the fieldset to a valid state.
* Sets the validity of the field.
*/
"setValid": () => Promise<void>;
"setValidity": (valid: boolean, message?: string) => Promise<void>;
/**
* Places the label in the vertical middle of the container.
*/
Expand Down Expand Up @@ -818,6 +821,9 @@ declare global {
prototype: HTMLDnnExampleFormElement;
new (): HTMLDnnExampleFormElement;
};
/**
* A custom input component that wraps the html input element is a mobile friendly component that supports a label, some help text and other features.
*/
interface HTMLDnnFieldsetElement extends Components.DnnFieldset, HTMLStencilElement {
}
var HTMLDnnFieldsetElement: {
Expand Down Expand Up @@ -1322,6 +1328,9 @@ declare namespace LocalJSX {
*/
interface DnnExampleForm {
}
/**
* A custom input component that wraps the html input element is a mobile friendly component that supports a label, some help text and other features.
*/
interface DnnFieldset {
/**
* If true, the fieldset will display as disabled.
Expand All @@ -1335,6 +1344,10 @@ declare namespace LocalJSX {
* If true the fieldset will display as focused.
*/
"focused"?: boolean;
/**
* Can be used to show some help text about this field.
*/
"helpText"?: string;
/**
* If true, the fieldset will display as invalid.
*/
Expand Down Expand Up @@ -1751,6 +1764,9 @@ declare module "@stencil/core" {
* Do not use this component in production, it is meant for testing purposes only and is not distributed in the production package.
*/
"dnn-example-form": LocalJSX.DnnExampleForm & JSXBase.HTMLAttributes<HTMLDnnExampleFormElement>;
/**
* A custom input component that wraps the html input element is a mobile friendly component that supports a label, some help text and other features.
*/
"dnn-fieldset": LocalJSX.DnnFieldset & JSXBase.HTMLAttributes<HTMLDnnFieldsetElement>;
/**
* Allows cropping an image in-browser with the option to enforce a specific final size.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -70,4 +70,16 @@
&.invalid{
border-color: var(--danger-color);
}
}

.help-text, .error-message{
font-style: italic;
opacity: 0.7;
font-size: 0.8em;
margin: 0.25em;
}
.error-message{
color: var(--danger-color);
font-style: normal;
font-weight: bold;
}
Original file line number Diff line number Diff line change
@@ -1,5 +1,9 @@
import { Component, Host, h, Prop, Method } from '@stencil/core';
import { Component, Host, h, Prop, Method, State } from '@stencil/core';

/** A custom input component that wraps the html input element is a mobile friendly component that supports a label, some help text and other features.
* @slot label-prefix - Can be used to inject content before the labe.
* @slot label-suffix - Can be used to inject content after the label.
*/
@Component({
tag: 'dnn-fieldset',
styleUrl: 'dnn-fieldset.scss',
Expand All @@ -22,6 +26,9 @@ export class DnnFieldset {
/** If true, the label will float in the container, set false to show it on top. */
@Prop({mutable: true, reflect: true}) floatLabel: boolean;

/** Can be used to show some help text about this field. */
@Prop() helpText: string;

/** Sets the fieldset to the focused state. */
@Method()
async setFocused() {
Expand All @@ -46,18 +53,6 @@ export class DnnFieldset {
this.disabled = false;
}

/** Sets the fieldset to an invalid state. */
@Method()
async setInvalid() {
this.invalid = true;
}

/** Sets the fieldset to a valid state. */
@Method()
async setValid() {
this.invalid = false;
}

/** Places the label on the top of the container. */
@Method()
async pinLabel() {
Expand All @@ -70,6 +65,16 @@ export class DnnFieldset {
this.floatLabel = true;
}

/** Sets the validity of the field. */
@Method()
async setValidity(valid: boolean, message?: string) {
this.valid = valid;
this.customValidityMessage = message;
}

@State() valid: boolean = true;
@State() customValidityMessage: string;

private getContainerClasses() {
const classes: string[] = ["container"];
if (this.focused) {
Expand Down Expand Up @@ -108,6 +113,14 @@ export class DnnFieldset {
<slot></slot>
</div>
</div>
{!this.valid && this.customValidityMessage &&
<div class="error-message">
{this.customValidityMessage}
</div>
}
{this.valid &&
<div class="help-text">{this.helpText}</div>
}
</Host>
);
}
Expand Down
Loading

0 comments on commit 2d1e792

Please sign in to comment.