Skip to content

Commit

Permalink
feat(ui-library): introduce property sanitizer using preact and contr…
Browse files Browse the repository at this point in the history
…oller (#1175)
  • Loading branch information
ashk1996 authored Dec 3, 2024
1 parent 3240589 commit 21856a3
Show file tree
Hide file tree
Showing 6 changed files with 259 additions and 64 deletions.
56 changes: 41 additions & 15 deletions packages/ui-library/src/components/button-text/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -30,30 +30,55 @@ import {
} from '../../globals/events.js';
import { LitElementCustom, ElementInterface } from '../../utils/lit/element.js';
import { ifDefined } from 'lit/directives/if-defined.js';
import { makeSanitizer } from '../../utils/lit/sanitize.js';
import { SanitizationController } from '../../utils/lit/sanitization-controller.js';

export type BlrButtonTextEventHandlers = {
blrFocus?: (event: BlrFocusEvent) => void;
blrBlur?: (event: BlrBlurEvent) => void;
blrClick?: (event: BlrClickEvent) => void;
};

const propertySanitizer = makeSanitizer((unsanitized: BlrButtonTextType) => ({
iconPosition: unsanitized.iconPosition ?? 'leading',
sizeVariant: unsanitized.sizeVariant ?? 'md',
buttonDisplay: unsanitized.buttonDisplay ?? 'inline-block',
}));

/**
* @fires blrFocus Button received focus
* @fires blrBlur Button lost focus
* @fires blrClick Button was clicked
*/
export class BlrButtonText extends LitElementCustom {
private sanitizedController: SanitizationController<
BlrButtonTextType,
// eslint-disable-next-line @typescript-eslint/no-explicit-any
any
>;

constructor() {
super();
this.sanitizedController = new SanitizationController<
BlrButtonTextType,
// eslint-disable-next-line @typescript-eslint/no-explicit-any
any
>({
host: this,
sanitize: propertySanitizer,
});
}
static styles = [styleCustom, staticActionStyles];

@property() accessor label = 'Button Label';
@property() accessor icon: SizelessIconType | undefined = undefined;
@property() accessor iconPosition: IconPositionVariant | undefined = 'leading';
@property() accessor icon: SizelessIconType | undefined;
@property() accessor iconPosition: IconPositionVariant | undefined;
@property({ type: Boolean }) accessor loading!: boolean;
@property({ type: Boolean }) accessor disabled!: boolean;
@property() accessor buttonTextId: string | undefined;
@property() accessor variant: ActionVariantType = 'primary';
@property() accessor sizeVariant: ActionSizesType | undefined = 'md';
@property() accessor buttonDisplay: DisplayType | undefined = 'inline-block';
@property() accessor sizeVariant: ActionSizesType | undefined;
@property() accessor buttonDisplay: DisplayType | undefined;

@property() accessor theme: ThemeType = 'Light_value';

Expand All @@ -80,27 +105,28 @@ export class BlrButtonText extends LitElementCustom {
};

protected render() {
if (this.sizeVariant && this.buttonDisplay) {
const sanitized = this.sanitizedController.values;
if (sanitized.sizeVariant && this.buttonDisplay) {
const classes = classMap({
'blr-semantic-action': true,
'blr-button-text': true,
[this.variant]: this.variant,
[this.sizeVariant]: this.sizeVariant,
[sanitized.sizeVariant]: sanitized.sizeVariant,
'disabled': this.disabled,
'loading': this.loading,
[this.buttonDisplay]: this.buttonDisplay,
[sanitized.buttonDisplay]: sanitized.buttonDisplay,
[this.theme]: this.theme,
});

const iconClasses = classMap({
'icon': true,
'leading-icon-class': this.iconPosition === 'leading',
'trailing-icon-class': this.iconPosition === 'trailing',
'leading-icon-class': sanitized.iconPosition === 'leading',
'trailing-icon-class': sanitized.iconPosition === 'trailing',
});

const flexContainerClasses = classMap({
'flex-container': true,
[this.sizeVariant]: this.sizeVariant,
[sanitized.sizeVariant]: sanitized.sizeVariant,
[this.theme]: this.theme,
});

Expand All @@ -118,19 +144,19 @@ export class BlrButtonText extends LitElementCustom {
'buttons',
'loader',
'sizevariant',
this.sizeVariant,
sanitized.sizeVariant,
]).toLowerCase() as FormSizesType;

const iconSizeVariant = getComponentConfigToken([
'cmp',
'buttontext',
'icon',
'sizevariant',
this.sizeVariant,
]) as SizesType;
sanitized.sizeVariant,
]).toLowerCase() as SizesType;

const labelAndIconGroup = html` <div class="${flexContainerClasses}">
${this.icon && this.iconPosition === 'leading'
${this.icon && sanitized.iconPosition === 'leading'
? BlrIconRenderFunction(
{
icon: calculateIconName(this.icon, iconSizeVariant),
Expand All @@ -144,7 +170,7 @@ export class BlrButtonText extends LitElementCustom {
)
: nothing}
<span class="label">${this.label} </span>
${this.icon && this.iconPosition === 'trailing'
${this.icon && sanitized.iconPosition === 'trailing'
? BlrIconRenderFunction(
{
icon: calculateIconName(this.icon, iconSizeVariant),
Expand Down
67 changes: 48 additions & 19 deletions packages/ui-library/src/components/input-field-text/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,8 @@ import {
import { LitElementCustom, ElementInterface } from '../../utils/lit/element.js';
import { BlrIconEventHandlers } from '../icon/index.js';
import { ifDefined } from 'lit/directives/if-defined.js';
import { makeSanitizer } from '../../utils/lit/sanitize.js';
import { SanitizationController } from '../../utils/lit/sanitization-controller.js';

export type BlrInputFieldTextEventHandlers = {
blrFocus?: (event: BlrFocusEvent) => void;
Expand All @@ -34,20 +36,43 @@ export type BlrInputFieldTextEventHandlers = {
blrSelect?: (event: BlrSelectEvent) => void;
};

const propertySanitizer = makeSanitizer((unsanitized: BlrInputFieldTextType) => ({
type: unsanitized.type ?? 'text',
sizeVariant: unsanitized.sizeVariant ?? 'md',
theme: unsanitized.theme ?? 'Light_value',
}));

/**
* @fires blrFocus InputFieldText received focus
* @fires blrBlur InputFieldText lost focus
* @fires blrTextValueChange InputFieldText value changed
* @fires blrSelect Text in InputFieldText got selected
*/
export class BlrInputFieldText extends LitElementCustom {
private sanitizedController: SanitizationController<
BlrInputFieldTextType,
// eslint-disable-next-line @typescript-eslint/no-explicit-any
any
>;

constructor() {
super();
this.sanitizedController = new SanitizationController<
BlrInputFieldTextType,
// eslint-disable-next-line @typescript-eslint/no-explicit-any
any
>({
host: this,
sanitize: propertySanitizer,
});
}
static styles = [styleCustom];

@query('input')
protected accessor _inputFieldTextNode!: HTMLInputElement;

@property() accessor inputFieldTextId!: string;
@property() accessor type: InputTypes = 'text';
@property() accessor type: InputTypes | undefined;
@property() accessor arialabel!: string;
@property({ type: Boolean }) accessor hasLabel!: boolean;
@property() accessor label!: string;
Expand All @@ -56,7 +81,7 @@ export class BlrInputFieldText extends LitElementCustom {
@property() accessor placeholder: string | undefined;
@property({ type: Boolean }) accessor disabled: boolean | undefined;
@property({ type: Boolean }) accessor readonly: boolean | undefined;
@property() accessor sizeVariant: FormSizesType | undefined = 'md';
@property() accessor sizeVariant: FormSizesType | undefined;
@property({ type: Boolean }) accessor required: boolean | undefined;
@property({ type: Number }) accessor maxLength: number | undefined;
@property() accessor pattern: string | undefined;
Expand All @@ -69,9 +94,9 @@ export class BlrInputFieldText extends LitElementCustom {
@property() accessor errorMessageIcon: SizelessIconType | undefined;

@property() accessor name!: string;
@property() accessor theme: ThemeType = 'Light_value';
@property() accessor theme: ThemeType | undefined;

@state() protected accessor currentType: InputTypes = this.type;
@state() protected accessor currentType: InputTypes | undefined;
@state() protected accessor isFocused = false;

protected willUpdate(_changedProperties: PropertyValueMap<never> | Map<PropertyKey, unknown>): void {
Expand Down Expand Up @@ -155,7 +180,7 @@ export class BlrInputFieldText extends LitElementCustom {
'icon-input': true,
[this.sizeVariant!]: this.sizeVariant!,
'no-pointer-events': Boolean(this.disabled || this.type !== 'password'),
[this.theme]: this.theme,
[this.theme!]: this.theme!,
});

const iconName: SizelessIconType | undefined =
Expand All @@ -174,44 +199,45 @@ export class BlrInputFieldText extends LitElementCustom {
},
);
}

protected render() {
if (this.sizeVariant) {
const sanitized = this.sanitizedController.values;

if (sanitized.sizeVariant) {
const classes = classMap({
'blr-input-field-text': true,
[this.sizeVariant]: this.sizeVariant,
[this.theme]: this.theme,
[sanitized.sizeVariant]: sanitized.sizeVariant,
[sanitized.theme]: this.theme,
});

const inputClasses = classMap({
'error-input': this.hasError || false,
'disabled': this.disabled || false,
[this.sizeVariant]: this.sizeVariant,
[sanitized.sizeVariant]: sanitized.sizeVariant,
});

const inputContainerClasses = classMap({
'focus': this.isFocused || false,
'error-input': this.hasError || false,
'disabled': this.disabled || false,
'readonly': this.readonly ? true : false,
[this.sizeVariant]: this.sizeVariant,
[this.theme]: this.theme,
[sanitized.sizeVariant]: sanitized.sizeVariant,
[sanitized.theme]: sanitized.theme,
});

const captionContent = html`
${this.hasHint && (this.hintMessage || this.hintMessageIcon)
? BlrFormCaptionRenderFunction({
variant: 'hint',
theme: this.theme,
sizeVariant: this.sizeVariant,
theme: sanitized.theme,
sizeVariant: sanitized.sizeVariant,
message: this.hintMessage,
icon: this.hintMessageIcon,
})
: nothing}
${this.hasError && (this.errorMessage || this.errorMessageIcon)
? BlrFormCaptionRenderFunction({
variant: 'error',
theme: this.theme,
theme: sanitized.theme,
sizeVariant: this.sizeVariant,
message: this.errorMessage,
icon: this.errorMessageIcon,
Expand All @@ -226,10 +252,10 @@ export class BlrInputFieldText extends LitElementCustom {
<div class="label-wrapper">
${BlrFormLabelRenderFunction({
label: this.label,
sizeVariant: this.sizeVariant,
sizeVariant: sanitized.sizeVariant,
labelAppendix: this.labelAppendix,
forValue: this.inputFieldTextId,
theme: this.theme,
theme: sanitized.theme,
hasError: Boolean(this.hasError),
})}
</div>
Expand All @@ -242,7 +268,7 @@ export class BlrInputFieldText extends LitElementCustom {
id=${this.inputFieldTextId}
name="${ifDefined(this.name)}"
aria-label=${this.arialabel}
type="${this.currentType}"
type="${sanitized.type === 'text' ? this.currentType : sanitized.type}"
.value="${this.value}"
placeholder="${ifDefined(this.placeholder)}"
?disabled="${this.disabled}"
Expand All @@ -260,7 +286,10 @@ export class BlrInputFieldText extends LitElementCustom {
${this.renderInputIcon()}
</div>
${(this.hasHint && this.hintMessage) || (this.hasError && this.errorMessage)
? BlrFormCaptionGroupRenderFunction({ theme: this.theme, sizeVariant: this.sizeVariant }, captionContent)
? BlrFormCaptionGroupRenderFunction(
{ theme: sanitized.theme, sizeVariant: sanitized.sizeVariant },
captionContent,
)
: nothing}
</div>
`;
Expand Down
Loading

0 comments on commit 21856a3

Please sign in to comment.