Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions etc/lime-elements.api.md
Original file line number Diff line number Diff line change
Expand Up @@ -83,6 +83,7 @@ export interface Chip<T = any> {
iconTitle?: string;
id: number | string;
image?: Image_2;
invalid?: boolean;
loading?: boolean;
menuItems?: Array<MenuItem | ListSeparator>;
removable?: boolean;
Expand Down Expand Up @@ -4179,6 +4180,7 @@ export interface ListItem<T = any> {
// @deprecated
iconColor?: Color;
image?: Image_2;
invalid?: boolean;
primaryComponent?: ListComponent;
secondaryText?: string;
selected?: boolean;
Expand Down
2 changes: 2 additions & 0 deletions src/components/chip-set/chip-set.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,7 @@ import { createRandomString } from '../../util/random-string';
* @exampleComponent limel-example-chip-set-filter
* @exampleComponent limel-example-chip-set-filter-badge
* @exampleComponent limel-example-chip-set-input
* @exampleComponent limel-example-chip-set-invalid-chips
* @exampleComponent limel-example-chip-set-input-type-with-menu-items
* @exampleComponent limel-example-chip-set-input-type-text
* @exampleComponent limel-example-chip-set-input-type-search
Expand Down Expand Up @@ -618,6 +619,7 @@ export class ChipSet {
selected: chip.selected,
disabled: this.disabled,
loading: chip.loading,
invalid: chip.invalid,
readonly: readonly,
type: chipType,
removable: removable,
Expand Down
5 changes: 5 additions & 0 deletions src/components/chip-set/chip.types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -108,6 +108,11 @@ export interface Chip<T = any> {
* indeterminate progress indicator inside the chip.
*/
loading?: boolean;

/**
* Set to `true` to visualize the chip in an "invalid" or "error" state.
*/
invalid?: boolean;
}

/**
Expand Down
98 changes: 98 additions & 0 deletions src/components/chip-set/examples/chip-set-invalid-chips.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,98 @@
import { Chip, LimelChipSetCustomEvent } from '@limetech/lime-elements';
import { Component, h, State } from '@stencil/core';
import { ENTER } from '../../../util/keycodes';

/**
* Per-chip invalid state
*
* Set `invalid: true` on any chip in the `value` array to mark that
* specific chip as invalid. This is independent of the chip-set-level
* `invalid` prop, which is intended for signalling that the whole field
* is invalid. Per-chip `invalid` lets the consumer flag individual
* entries, for example an address that fails validation in a list of
* recipients.
*
* In this example, each entry is checked with a simple email regex when
* added. Invalid entries are rendered with `invalid: true` and an error
* icon.
*/
@Component({
tag: 'limel-example-chip-set-invalid-chips',
shadow: true,
})
export class ChipSetInvalidChipsExample {
@State()
private value: Chip[];

@State()
private textValue = '';

constructor() {
this.value = [
this.createChip('alice@example.com'),
this.createChip('not-an-email'),
this.createChip('bob@example.com'),
];
}

public render() {
return [
<limel-chip-set
type="input"
label="Recipients"
helperText="Type an email address and press Enter. Invalid entries are flagged on the chip."
searchLabel="Type an email address"
value={this.value}
onChange={this.handleChange}
onInput={this.handleInput}
onKeyUp={this.onKeyUp}
/>,
<limel-example-value value={this.value} />,
];
}

private handleInput = (
event: LimelChipSetCustomEvent<string> | InputEvent
) => {
if (event instanceof CustomEvent) {
this.textValue = event.detail;
}
};

private onKeyUp = (event: KeyboardEvent) => {
if (event.key === ENTER && this.textValue.trim()) {
this.value = [
...this.value,
this.createChip(this.textValue.trim()),
];
this.textValue = '';
}
};

private handleChange = (event: LimelChipSetCustomEvent<Chip[]>) => {
this.value = event.detail;
};

private createChip = (text: string): Chip => {
const isValid = this.looksLikeEmail(text);

return {
id: text,
text: text,
removable: true,
invalid: !isValid,
...(!isValid && { icon: 'error' }),
};
};

private looksLikeEmail = (text: string): boolean => {
const atIndex = text.indexOf('@');
if (atIndex <= 0 || atIndex === text.length - 1) {
return false;
}

const domain = text.slice(atIndex + 1);

return domain.includes('.') && !/\s/.test(text);
};
}
12 changes: 12 additions & 0 deletions src/components/list-item/list-item.types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,18 @@ export interface ListItem<T = any> {
*/
selected?: boolean;

/**
* Set to `true` to visualize the item in an "invalid" or "error" state.
*
* :::note
* This flag is currently only honoured when the item is rendered as a
* chip by `limel-picker`. It has no visual effect in plain lists or
* menus today; see the `limel-list` / `limel-menu` roadmap for when
* that support is added.
* :::
*/
invalid?: boolean;

/**
* Value of the list item.
*/
Expand Down
64 changes: 64 additions & 0 deletions src/components/picker/examples/picker-invalid-items.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
import { LimelPickerCustomEvent, ListItem } from '@limetech/lime-elements';
import { Component, h, State } from '@stencil/core';

/**
* Per-item invalid state
*
* Set `invalid: true` on any `ListItem` passed to the picker's `value` to
* render that specific selection as an invalid chip. This is useful when
* a previously-valid selection is no longer valid (for example, a
* deactivated user or an archived tag) while other selections remain
* valid.
*
* This is independent of the picker-level `invalid` prop, which marks
* the whole field as invalid.
*/
@Component({
tag: 'limel-example-picker-invalid-items',
shadow: true,
})
export class PickerInvalidItemsExample {
private allItems: Array<ListItem<number>> = [

Check warning on line 21 in src/components/picker/examples/picker-invalid-items.tsx

View check run for this annotation

SonarQubeCloud / SonarCloud Code Analysis

Member 'allItems' is never reassigned; mark it as `readonly`.

See more on https://sonarcloud.io/project/issues?id=Lundalogik_lime-elements&issues=AZ2Ql3X5BvXQJmSc56af&open=AZ2Ql3X5BvXQJmSc56af&pullRequest=4029
{ text: 'Admiral Swiggins', value: 1 },
{ text: 'Ayla', value: 2 },
{ text: 'Clunk', value: 3, invalid: true },
{ text: 'Coco', value: 4 },
{ text: 'Derpl', value: 5, invalid: true },
{ text: 'Froggy G', value: 6 },
];

@State()
private selectedItems: Array<ListItem<number>> = [
this.allItems[0],
this.allItems[2],
this.allItems[3],
];

public render() {
return [
<limel-picker
label="Team members"
value={this.selectedItems}
multiple={true}
allItems={this.availableItems()}
emptyResultMessage="No matching members found"
onChange={this.onChange}
/>,

Check warning on line 46 in src/components/picker/examples/picker-invalid-items.tsx

View check run for this annotation

SonarQubeCloud / SonarCloud Code Analysis

Missing "key" prop for element in array

See more on https://sonarcloud.io/project/issues?id=Lundalogik_lime-elements&issues=AZ2Ql3X5BvXQJmSc56ag&open=AZ2Ql3X5BvXQJmSc56ag&pullRequest=4029
<limel-example-value value={this.selectedItems} />,

Check warning on line 47 in src/components/picker/examples/picker-invalid-items.tsx

View check run for this annotation

SonarQubeCloud / SonarCloud Code Analysis

Missing "key" prop for element in array

See more on https://sonarcloud.io/project/issues?id=Lundalogik_lime-elements&issues=AZ2Ql3X5BvXQJmSc56ah&open=AZ2Ql3X5BvXQJmSc56ah&pullRequest=4029
];
}

private availableItems = (): Array<ListItem<number>> => {

Check warning on line 51 in src/components/picker/examples/picker-invalid-items.tsx

View check run for this annotation

SonarQubeCloud / SonarCloud Code Analysis

Member 'availableItems' is never reassigned; mark it as `readonly`.

See more on https://sonarcloud.io/project/issues?id=Lundalogik_lime-elements&issues=AZ2Ql3X5BvXQJmSc56ai&open=AZ2Ql3X5BvXQJmSc56ai&pullRequest=4029
return this.allItems.filter((item) => {
return !this.selectedItems.some((selected) => {
return selected.value === item.value;
});
});
};

private onChange = (

Check warning on line 59 in src/components/picker/examples/picker-invalid-items.tsx

View check run for this annotation

SonarQubeCloud / SonarCloud Code Analysis

Member 'onChange' is never reassigned; mark it as `readonly`.

See more on https://sonarcloud.io/project/issues?id=Lundalogik_lime-elements&issues=AZ2Ql3X5BvXQJmSc56aj&open=AZ2Ql3X5BvXQJmSc56aj&pullRequest=4029
event: LimelPickerCustomEvent<Array<ListItem<number>>>
) => {
this.selectedItems = [...event.detail];
};
}
2 changes: 2 additions & 0 deletions src/components/picker/picker.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@ const DEFAULT_SEARCHER_MAX_RESULTS = 20;
/**
* @exampleComponent limel-example-picker-basic
* @exampleComponent limel-example-picker-multiple
* @exampleComponent limel-example-picker-invalid-items
* @exampleComponent limel-example-picker-icons
* @exampleComponent limel-example-picker-pictures
* @exampleComponent limel-example-picker-value-as-object
Expand Down Expand Up @@ -356,6 +357,7 @@ export class Picker {
image: listItem.image,
value: listItem,
menuItems: listItem.actions,
invalid: listItem.invalid,
};
};

Expand Down
Loading