Skip to content

Commit

Permalink
Merge branch 'master' into sivanova/btn-group-styles
Browse files Browse the repository at this point in the history
  • Loading branch information
SisIvanova committed Apr 29, 2024
2 parents ab0b81b + 5abf935 commit dbefdd7
Show file tree
Hide file tree
Showing 16 changed files with 271 additions and 71 deletions.
1 change: 1 addition & 0 deletions CHANGELOG.md
Expand Up @@ -7,6 +7,7 @@ and this project adheres to [Semantic Versioning](http://semver.org/).
## [Unreleased]
### Added
- Button group component now allows resetting the selection state via the `selectedItems` property [#1168](https://github.com/IgniteUI/igniteui-webcomponents/pull/1168)
- Input, Textarea - exposed `validateOnly` to enable validation rules being enforced without restricting user input [#1178](https://github.com/IgniteUI/igniteui-webcomponents/pull/1178)

### Changed
- Combo, Select and Dropdown components now use the native Popover API [#1082](https://github.com/IgniteUI/igniteui-webcomponents/pull/1082)
Expand Down
2 changes: 1 addition & 1 deletion src/components/button-group/toggle-button.ts
Expand Up @@ -18,7 +18,7 @@ import { styles as shared } from './themes/shared/button/button.common.css.js';
*
* @csspart toggle - The native button element.
*/
@themes(all, true)
@themes(all)
export default class IgcToggleButtonComponent extends LitElement {
public static override styles = [styles, shared];
public static readonly tagName = 'igc-toggle-button';
Expand Down
20 changes: 8 additions & 12 deletions src/components/common/util.ts
Expand Up @@ -55,24 +55,20 @@ export function isLTR(element: HTMLElement) {

/**
* Builds a string from format specifiers and replacement parameters.
* Will coerce non-string parameters to their string representations.
*
* @example
* ```typescript
* format('{0} says "{1}".', 'John', 'Hello'); // 'John says "Hello".'
* formatString('{0} says "{1}".', 'John', 'Hello'); // 'John says "Hello".'
* formatString('{1} is greater than {0}', 0, 1); // '1 is greater than 0'
* ```
*/
export function format(template: string, ...params: string[]): string {
return template.replace(/{(\d+)}/g, (match: string, index: number) => {
if (index >= params.length) {
return match;
}
export function formatString(template: string, ...params: unknown[]): string {
const length = params.length;

const value: string = params[index];
if (typeof value !== 'number' && !value) {
return '';
}
return value;
});
return template.replace(/{(\d+)}/g, (match: string, index: number) =>
index >= length ? match : `${params[index]}`
);
}

/**
Expand Down
10 changes: 5 additions & 5 deletions src/components/common/validators.ts
@@ -1,5 +1,5 @@
import validatorMessages from './localization/validation-en.js';
import { asNumber, format, isDefined } from './util.js';
import { asNumber, formatString, isDefined } from './util.js';

type ValidatorHandler<T> = (host: T) => boolean;
type ValidatorMessageFormat<T> = (host: T) => string;
Expand Down Expand Up @@ -43,7 +43,7 @@ export const minLengthValidator: Validator<{
}> = {
key: 'tooShort',
message: ({ minLength }) =>
format(validatorMessages.minLength, `${minLength}`),
formatString(validatorMessages.minLength, minLength),
isValid: ({ minLength, value }) =>
minLength ? value.length >= minLength : true,
};
Expand All @@ -54,7 +54,7 @@ export const maxLengthValidator: Validator<{
}> = {
key: 'tooLong',
message: ({ maxLength }) =>
format(validatorMessages.maxLength, `${maxLength}`),
formatString(validatorMessages.maxLength, maxLength),
isValid: ({ maxLength, value }) =>
maxLength ? value.length <= maxLength : true,
};
Expand All @@ -71,7 +71,7 @@ export const minValidator: Validator<{
value: number | string;
}> = {
key: 'rangeUnderflow',
message: ({ min }) => format(validatorMessages.min, `${min}`),
message: ({ min }) => formatString(validatorMessages.min, min),
isValid: ({ min, value }) =>
isDefined(min)
? isDefined(value) && asNumber(value) >= asNumber(min)
Expand All @@ -83,7 +83,7 @@ export const maxValidator: Validator<{
value: number | string;
}> = {
key: 'rangeOverflow',
message: ({ max }) => format(validatorMessages.max, `${max}`),
message: ({ max }) => formatString(validatorMessages.max, max),
isValid: ({ max, value }) =>
isDefined(max)
? isDefined(value) && asNumber(value) <= asNumber(max)
Expand Down
6 changes: 3 additions & 3 deletions src/components/date-time-input/date-time-input.ts
Expand Up @@ -17,7 +17,7 @@ import { registerComponent } from '../common/definitions/register.js';
import messages from '../common/localization/validation-en.js';
import type { AbstractConstructor } from '../common/mixins/constructor.js';
import { EventEmitterMixin } from '../common/mixins/event-emitter.js';
import { format, partNameMap } from '../common/util.js';
import { formatString, partNameMap } from '../common/util.js';
import type { Validator } from '../common/validators.js';
import type { IgcInputEventMap } from '../input/input-base.js';
import {
Expand Down Expand Up @@ -84,7 +84,7 @@ export default class IgcDateTimeInputComponent extends EventEmitterMixin<
},
{
key: 'rangeUnderflow',
message: () => format(messages.min, `${this.min}`),
message: () => formatString(messages.min, this.min),
isValid: () =>
this.min
? !DateTimeUtil.lessThanMinValue(
Expand All @@ -97,7 +97,7 @@ export default class IgcDateTimeInputComponent extends EventEmitterMixin<
},
{
key: 'rangeOverflow',
message: () => format(messages.max, `${this.max}`),
message: () => formatString(messages.max, this.max),
isValid: () =>
this.max
? !DateTimeUtil.greaterThanMaxValue(
Expand Down
24 changes: 15 additions & 9 deletions src/components/input/input.ts
Expand Up @@ -3,9 +3,6 @@ import { property } from 'lit/decorators.js';
import { ifDefined } from 'lit/directives/if-defined.js';
import { live } from 'lit/directives/live.js';

import { alternateName } from '../common/decorators/alternateName.js';
import { blazorSuppress } from '../common/decorators/blazorSuppress.js';
import { blazorTwoWayBind } from '../common/decorators/blazorTwoWayBind.js';
import { watch } from '../common/decorators/watch.js';
import { registerComponent } from '../common/definitions/register.js';
import { partNameMap } from '../common/util.js';
Expand Down Expand Up @@ -111,12 +108,12 @@ export default class IgcInputComponent extends IgcInputBaseComponent {

protected _value = '';

/* @tsTwoWayProperty(true, "igcChange", "detail", false) */
/**
* The value of the control.
* @attr
*/
@property()
@blazorTwoWayBind('igcChange', 'detail')
public set value(value: string) {
this._value = value;
this.setFormValue(value ? value : null);
Expand All @@ -128,11 +125,11 @@ export default class IgcInputComponent extends IgcInputBaseComponent {
return this._value;
}

/* alternateName: displayType */
/**
* The type attribute of the control.
* @attr
*/
@alternateName('displayType')
@property({ reflect: true })
public type:
| 'email'
Expand Down Expand Up @@ -247,6 +244,15 @@ export default class IgcInputComponent extends IgcInputBaseComponent {
@property()
public autocomplete!: string;

/**
* Enables validation rules to be evaluated without restricting user input. This applies to the `maxLength` property for
* string-type inputs or allows spin buttons to exceed the predefined `min/max` limits for number-type inputs.
*
* @attr validate-only
*/
@property({ type: Boolean, reflect: true, attribute: 'validate-only' })
public validateOnly = false;

/**
* @internal
*/
Expand All @@ -270,8 +276,8 @@ export default class IgcInputComponent extends IgcInputBaseComponent {
this.updateValidity();
}

/* blazorSuppress */
/** Replaces the selected text in the input. */
@blazorSuppress()
public override setRangeText(
replacement: string,
start: number,
Expand Down Expand Up @@ -336,10 +342,10 @@ export default class IgcInputComponent extends IgcInputBaseComponent {
tabindex=${this.tabIndex}
autocomplete=${ifDefined(this.autocomplete as any)}
inputmode=${ifDefined(this.inputmode)}
min=${ifDefined(this.min)}
max=${ifDefined(this.max)}
min=${ifDefined(this.validateOnly ? undefined : this.min)}
max=${ifDefined(this.validateOnly ? undefined : this.max)}
minlength=${ifDefined(this.minLength)}
maxlength=${ifDefined(this.maxLength)}
maxlength=${ifDefined(this.validateOnly ? undefined : this.maxLength)}
step=${ifDefined(this.step)}
aria-invalid=${this.invalid ? 'true' : 'false'}
@change=${this.handleChange}
Expand Down
4 changes: 2 additions & 2 deletions src/components/progress/base.ts
Expand Up @@ -7,7 +7,7 @@ import {
} from 'lit/decorators.js';

import { watch } from '../common/decorators/watch.js';
import { asPercent, clamp, format } from '../common/util.js';
import { asPercent, clamp, formatString } from '../common/util.js';

export abstract class IgcProgressBaseComponent extends LitElement {
private __internals: ElementInternals;
Expand Down Expand Up @@ -193,7 +193,7 @@ export abstract class IgcProgressBaseComponent extends LitElement {
}

protected renderLabelFormat() {
return format(this.labelFormat, `${this.value}`, `${this.max}`);
return formatString(this.labelFormat, this.value, this.max);
}

protected renderDefaultSlot() {
Expand Down
10 changes: 5 additions & 5 deletions src/components/rating/rating.ts
Expand Up @@ -26,7 +26,7 @@ import type { Constructor } from '../common/mixins/constructor.js';
import { EventEmitterMixin } from '../common/mixins/event-emitter.js';
import { FormAssociatedMixin } from '../common/mixins/form-associated.js';
import { SizableMixin } from '../common/mixins/sizable.js';
import { clamp, format, isLTR } from '../common/util.js';
import { clamp, formatString, isLTR } from '../common/util.js';
import IgcIconComponent from '../icon/icon.js';
import IgcRatingSymbolComponent from './rating-symbol.js';
import { styles } from './themes/rating.base.css.js';
Expand Down Expand Up @@ -61,7 +61,7 @@ export interface IgcRatingEventMap {
* @cssproperty --symbol-full-filter - The filter(s) used for the filled symbol.
* @cssproperty --symbol-empty-filter - The filter(s) used for the empty symbol.
*/
@themes(all, true)
@themes(all)
export default class IgcRatingComponent extends FormAssociatedMixin(
SizableMixin(
EventEmitterMixin<IgcRatingEventMap, Constructor<LitElement>>(LitElement)
Expand Down Expand Up @@ -109,7 +109,7 @@ export default class IgcRatingComponent extends FormAssociatedMixin(
// Skip IEEE 754 representation for screen readers
const value = this.round(this.value);
return this.valueFormat
? format(this.valueFormat, `${value}`, `${this.max}`)
? formatString(this.valueFormat, value, this.max)
: `${value} of ${this.max}`;
}

Expand Down Expand Up @@ -431,8 +431,8 @@ export default class IgcRatingComponent extends FormAssociatedMixin(
aria-hidden="true"
part="symbols"
@click=${this.isInteractive ? this.handleClick : nothing}
@mouseenter=${hoverActive ? () => this.handleHoverEnabled : nothing}
@mouseleave=${hoverActive ? () => this.handleHoverDisabled : nothing}
@mouseenter=${hoverActive ? this.handleHoverEnabled : nothing}
@mouseleave=${hoverActive ? this.handleHoverDisabled : nothing}
@mousemove=${hoverActive ? this.handleMouseMove : nothing}
>
<slot name="symbol" @slotchange=${this.handleSlotChange}>
Expand Down
6 changes: 4 additions & 2 deletions src/components/slider/slider-base.ts
Expand Up @@ -27,7 +27,7 @@ import {
asNumber,
asPercent,
clamp,
format,
formatString,
isDefined,
isLTR,
} from '../common/util.js';
Expand Down Expand Up @@ -378,7 +378,9 @@ export class IgcSliderBaseComponent extends LitElement {

protected formatValue(value: number) {
const strValue = value.toLocaleString(this.locale, this.valueFormatOptions);
return this.valueFormat ? format(this.valueFormat, strValue) : strValue;
return this.valueFormat
? formatString(this.valueFormat, strValue)
: strValue;
}

private normalizeByStep(value: number) {
Expand Down
2 changes: 1 addition & 1 deletion src/components/stepper/step.ts
Expand Up @@ -44,7 +44,7 @@ import { all } from './themes/step/themes.js';
* @csspart body - Wrapper of the step's `content`.
* @csspart content - The steps `content`.
*/
@themes(all, true)
@themes(all)
export default class IgcStepComponent extends LitElement {
public static readonly tagName = 'igc-step';
public static override styles = [styles, shared];
Expand Down
2 changes: 1 addition & 1 deletion src/components/tabs/tab.ts
Expand Up @@ -22,7 +22,7 @@ import { styles } from './themes/tab.base.css.js';
* @csspart suffix - The suffix wrapper.
*/

@themes(all, true)
@themes(all)
export default class IgcTabComponent extends LitElement {
public static readonly tagName = 'igc-tab';
public static override styles = [styles, shared];
Expand Down
2 changes: 1 addition & 1 deletion src/components/tabs/tabs.ts
Expand Up @@ -56,7 +56,7 @@ export interface IgcTabsEventMap {
* @csspart end-scroll-button - The end scroll button displayed when the tabs overflow.
* @csspart content - The container for the tabs content.
*/
@themes(all, true)
@themes(all)
@blazorAdditionalDependencies('IgcTabComponent, IgcTabPanelComponent')
export default class IgcTabsComponent extends EventEmitterMixin<
IgcTabsEventMap,
Expand Down
11 changes: 11 additions & 0 deletions src/components/textarea/textarea.ts
Expand Up @@ -252,6 +252,15 @@ export default class IgcTextareaComponent extends FormAssociatedRequiredMixin(
@property()
public wrap: 'hard' | 'soft' | 'off' = 'soft';

/**
* Enables validation rules to be evaluated without restricting user input. This applies to the `maxLength` property
* when it is defined.
*
* @attr validate-only
*/
@property({ type: Boolean, reflect: true, attribute: 'validate-only' })
public validateOnly = false;

constructor() {
super();
this.addEventListener('focus', () => {
Expand Down Expand Up @@ -467,6 +476,8 @@ export default class IgcTextareaComponent extends FormAssociatedRequiredMixin(
autocapitalize=${ifDefined(this.autocapitalize)}
inputmode=${ifDefined(this.inputMode)}
spellcheck=${ifDefined(this.spellcheck)}
minlength=${ifDefined(this.minLength)}
maxlength=${ifDefined(this.validateOnly ? undefined : this.maxLength)}
?disabled=${this.disabled}
?required=${this.required}
?readonly=${this.readOnly}
Expand Down
2 changes: 1 addition & 1 deletion src/components/tree/tree-item.ts
Expand Up @@ -48,7 +48,7 @@ import type { IgcTreeSelectionService } from './tree.selection.js';
* @csspart text - The tree item displayed text.
* @csspart select - The checkbox of the tree item when selection is enabled.
*/
@themes(all, true)
@themes(all)
export default class IgcTreeItemComponent extends LitElement {
public static readonly tagName = 'igc-tree-item';
public static override styles = [styles, shared];
Expand Down

0 comments on commit dbefdd7

Please sign in to comment.