Skip to content
This repository has been archived by the owner on Jun 29, 2023. It is now read-only.

Commit

Permalink
feat(pick-list): add removable prop for easier item removal (#820)
Browse files Browse the repository at this point in the history
  • Loading branch information
pr3tori4n authored Mar 2, 2020
1 parent 2642cba commit e41e460
Show file tree
Hide file tree
Showing 6 changed files with 108 additions and 64 deletions.
51 changes: 27 additions & 24 deletions src/components/calcite-pick-list-item/calcite-pick-list-item.e2e.ts
Original file line number Diff line number Diff line change
@@ -1,32 +1,25 @@
import { newE2EPage } from "@stencil/core/testing";
import { CSS } from "./resources";
import { setUpPage } from "../../tests/utils";
import { renders } from "../../tests/commonTests";

describe("calcite-pick-list-item", () => {
it("should render", async () => {
const page = await newE2EPage();

await page.setContent(`<calcite-pick-list-item></calcite-pick-list-item>`);
const item = await page.find("calcite-pick-list-item");
expect(item).not.toBeNull();
const isVisible = await item.isVisible();
expect(isVisible).toBe(true);
});
it("renders", async () => renders("calcite-pick-list-item"));

it("should toggle selected attribute when clicked", async () => {
const page = await newE2EPage();
const page = await setUpPage(`<calcite-pick-list-item text-label="test"></calcite-pick-list-item>`);

await page.setContent(`<calcite-pick-list-item text-label="test"></calcite-pick-list-item>`);
const item = await page.find("calcite-pick-list-item");
expect(await item.getProperty("selected")).toBe(false);

await item.click();
expect(await item.getProperty("selected")).toBe(true);

await item.click();
expect(await item.getProperty("selected")).toBe(false);
});

it("should fire event calciteListItemChange when item is clicked", async () => {
const page = await newE2EPage();

await page.setContent(`<calcite-pick-list-item text-label="test" value="example"></calcite-pick-list-item>`);
const page = await setUpPage(`<calcite-pick-list-item text-label="test" value="example"></calcite-pick-list-item>`);
const item = await page.find("calcite-pick-list-item");
await page.evaluate(() => {
document.addEventListener("calciteListItemChange", (event: CustomEvent) => {
Expand All @@ -36,29 +29,39 @@ describe("calcite-pick-list-item", () => {

await item.click();

await page.waitForChanges();

const eventDetail: any = await page.evaluateHandle(() => {
return (window as any).eventDetail;
});
const eventDetail: any = await page.evaluateHandle(() => (window as any).eventDetail);
const properties = await eventDetail.getProperties();

expect(properties.get("item")).toBeDefined();
expect(properties.get("value")._remoteObject.value).toBe("example");
expect(properties.get("selected")._remoteObject.value).toBe(true);
expect(properties.get("shiftPressed")._remoteObject.value).toBe(false);
});

it("prevents deselection when disableDeselect is true", async () => {
const page = await newE2EPage();

await page.setContent(
const page = await setUpPage(
`<calcite-pick-list-item text-label="test" value="example" disable-deselect selected></calcite-pick-list-item>`
);
const item = await page.find("calcite-pick-list-item");

await item.click();
await page.waitForChanges();

expect(await item.getProperty("selected")).toBe(true);
});

it("allows for easy removal", async () => {
const page = await setUpPage(
`<calcite-pick-list-item text-label="test" value="example" removable></calcite-pick-list-item>`
);

const removeButton = await page.find(`calcite-pick-list-item >>> .${CSS.remove}`);

expect(removeButton).toBeTruthy();

const item = await page.find("calcite-pick-list-item");
const removeEventSpy = await item.spyOnEvent("calciteListItemRemove");
await removeButton.click();

expect(removeEventSpy).toHaveReceivedEventTimes(1);
});
});
68 changes: 56 additions & 12 deletions src/components/calcite-pick-list-item/calcite-pick-list-item.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ import {
Watch,
h
} from "@stencil/core";
import { CSS, ICONS, SLOTS } from "./resources";
import { CSS, ICONS, SLOTS, TEXT } from "./resources";
import { ICON_TYPES } from "../calcite-pick-list/resources";

/**
Expand Down Expand Up @@ -54,17 +54,22 @@ export class CalcitePickListItem {
*/
@Prop() metadata?: object;

@Watch("metadata") metadataWatchHandler() {
@Watch("metadata") metadataWatchHandler(): void {
this.calciteListItemPropsChange.emit();
}

/**
* Set this to true to display a remove action that removes the item from the list.
*/
@Prop({ reflect: true }) removable? = false;

/**
* Set this to true to pre-select an item. Toggles when an item is checked/unchecked.
*/
@Prop({ reflect: true, mutable: true }) selected = false;

@Watch("selected")
selectedWatchHandler() {
selectedWatchHandler(): void {
this.calciteListItemChange.emit({
item: this.el,
value: this.value,
Expand All @@ -80,7 +85,7 @@ export class CalcitePickListItem {
*/
@Prop({ reflect: true }) textDescription?: string;

@Watch("textDescription") textDescriptionWatchHandler() {
@Watch("textDescription") textDescriptionWatchHandler(): void {
this.calciteListItemPropsChange.emit();
}

Expand All @@ -89,16 +94,21 @@ export class CalcitePickListItem {
*/
@Prop({ reflect: true }) textLabel: string;

@Watch("textLabel") textLabelWatchHandler() {
@Watch("textLabel") textLabelWatchHandler(): void {
this.calciteListItemPropsChange.emit();
}

/**
* The text for the remove item buttons. Only applicable if removable is true.
*/
@Prop({ reflect: true }) textRemove = TEXT.remove;

/**
* A unique value used to identify this item - similar to the value attribute on an <input>.
*/
@Prop({ reflect: true }) value!: string;

@Watch("value") valueWatchHandler(newValue, oldValue) {
@Watch("value") valueWatchHandler(newValue: string, oldValue: string): void {
this.calciteListItemValueChange.emit({ oldValue, newValue });
}

Expand All @@ -122,21 +132,32 @@ export class CalcitePickListItem {
* Emitted whenever the item is selected or unselected.
* @event calciteListItemChange
*/
@Event() calciteListItemChange: EventEmitter;
@Event() calciteListItemChange: EventEmitter<{
item: HTMLCalcitePickListItemElement;
value: string;
selected: boolean;
shiftPressed: boolean;
}>;

/**
* Emitted whenever the remove button is pressed.
* @event calciteListItemRemove
*/
@Event() calciteListItemRemove: EventEmitter<void>;

/**
* Emitted whenever the the item's textLabel, textDescription, value or metadata properties are modified.
* @event calciteListItemPropsChange
* @internal
*/
@Event() calciteListItemPropsChange: EventEmitter;
@Event() calciteListItemPropsChange: EventEmitter<void>;

/**
* Emitted whenever the the item's value property is modified.
* @event calciteListItemValueChange
* @internal
*/
@Event() calciteListItemValueChange: EventEmitter;
@Event() calciteListItemValueChange: EventEmitter<{ oldValue: string; newValue: string }>;

// --------------------------------------------------------------------------
//
Expand All @@ -148,7 +169,7 @@ export class CalcitePickListItem {
* Used to toggle the selection state. By default this won't trigger an event.
* The first argument allows the value to be coerced, rather than swapping values.
*/
@Method() async toggleSelected(coerce?: boolean) {
@Method() async toggleSelected(coerce?: boolean): Promise<void> {
if (this.disabled) {
return;
}
Expand Down Expand Up @@ -181,6 +202,10 @@ export class CalcitePickListItem {
}
};

removeClickHandler = (): void => {
this.calciteListItemRemove.emit();
};

// --------------------------------------------------------------------------
//
// Render Methods
Expand All @@ -189,9 +214,11 @@ export class CalcitePickListItem {

renderIcon() {
const { compact, icon, selected } = this;

if (!icon || compact) {
return null;
}

const iconName =
icon === ICON_TYPES.square
? selected
Expand All @@ -200,18 +227,35 @@ export class CalcitePickListItem {
: selected
? ICONS.selected
: ICONS.unselected;

return (
<span class={CSS.icon}>
<calcite-icon scale="s" icon={iconName} />
</span>
);
}

renderRemoveAction() {
if (!this.removable) {
return null;
}

return (
<calcite-action
scale="s"
class={CSS.remove}
icon={ICONS.remove}
text={this.textRemove}
onClick={this.removeClickHandler}
/>
);
}

renderSecondaryAction() {
const hasSecondaryAction = this.el.querySelector(`[slot=${SLOTS.secondaryAction}]`);
return hasSecondaryAction ? (
return hasSecondaryAction || this.removable ? (
<div class={CSS.action}>
<slot name={SLOTS.secondaryAction} />
<slot name={SLOTS.secondaryAction}>{this.renderRemoveAction()}</slot>
</div>
) : null;
}
Expand Down
8 changes: 7 additions & 1 deletion src/components/calcite-pick-list-item/resources.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ export const CSS = {
highlight: "highlight",
icon: "icon",
label: "label",
remove: "remove",
title: "title",
textContainer: "text-container"
};
Expand All @@ -14,9 +15,14 @@ export const ICONS = {
unchecked: "square",
checked: "check",
selected: "circle-filled",
unselected: "circle"
unselected: "circle",
remove: "x"
};

export const SLOTS = {
secondaryAction: "secondary-action"
};

export const TEXT = {
remove: "remove"
};
14 changes: 5 additions & 9 deletions src/components/calcite-pick-list/calcite-pick-list.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -103,15 +103,15 @@ export class CalcitePickList {
//
// --------------------------------------------------------------------------

connectedCallback() {
connectedCallback(): void {
initialize.call(this);
}

componentDidLoad() {
componentDidLoad(): void {
initializeObserver.call(this);
}

componentDidUnload() {
componentDidUnload(): void {
cleanUpObserver.call(this);
}

Expand Down Expand Up @@ -180,12 +180,8 @@ export class CalcitePickList {
//
// --------------------------------------------------------------------------

getIconType(): ICON_TYPES | null {
let type = ICON_TYPES.circle;
if (this.multiple) {
type = ICON_TYPES.square;
}
return type;
getIconType(): ICON_TYPES {
return this.multiple ? ICON_TYPES.square : ICON_TYPES.circle;
}

render() {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,11 @@ export class CalciteValueListItem {
*/
@Prop() metadata?: object;

/**
* Set this to true to display a remove action that removes the item from the list.
*/
@Prop({ reflect: true }) removable = false;

/**
* Set this to true to pre-select an item. Toggles when an item is checked/unchecked.
*/
Expand Down Expand Up @@ -149,6 +154,7 @@ export class CalciteValueListItem {
disableDeselect={this.disableDeselect}
selected={this.selected}
metadata={this.metadata}
removable={this.removable}
textLabel={this.textLabel}
textDescription={this.textDescription}
onCalciteListItemChange={this.handleSelectChange}
Expand Down
25 changes: 7 additions & 18 deletions src/demos/pick-list/basic.html
Original file line number Diff line number Diff line change
Expand Up @@ -19,27 +19,16 @@ <h1>calcite-pick-list</h1>

<h2>Pick List - Default</h2>
<calcite-pick-list id="one" compact>
<calcite-pick-list-item text-label="T. Rex" text-description="arm strength impaired" value="trex">
<calcite-action scale="s" appearance="clear" slot="secondary-action" text="click-me" onClick="console.log('clicked');">
<calcite-icon icon="x" scale="s"></calcite-icon>
</calcite-action>
</calcite-pick-list-item>
<calcite-pick-list-item text-label="Triceratops" text-description="3 horn" value="triceratops" selected>
<calcite-action scale="s" appearance="clear" slot="secondary-action" text="click-me" onClick="console.log('clicked');">
<calcite-icon icon="x" scale="s"></calcite-icon>
</calcite-action>
</calcite-pick-list-item>
<calcite-pick-list-item text-label="hi" text-description="there" value="helloWorld">
<calcite-action scale="s" appearance="clear" slot="secondary-action" text="click-me" onClick="console.log('clicked');">
<calcite-icon icon="x" scale="s"></calcite-icon>
</calcite-action>
</calcite-pick-list-item>
<calcite-pick-list-item text-label="T. Rex" text-description="arm strength impaired" value="trex" removable></calcite-pick-list-item>
<calcite-pick-list-item text-label="Triceratops" text-description="3 horn" value="triceratops" removable selected></calcite-pick-list-item>
<calcite-pick-list-item text-label="hi" text-description="there" value="helloWorld" removable></calcite-pick-list-item>
</calcite-pick-list>
<script>
const pickList1 = document.querySelector("#one");
pickList1.addEventListener("calciteListChange", (event) => {
console.log(event.detail);
});

pickList1.addEventListener("calciteListChange", (event) => console.log(event.detail));

pickList1.addEventListener("calciteListItemRemove", (event) => event.target.remove());
</script>

<h2>Pick List - multi-select filter-enabled</h2>
Expand Down

0 comments on commit e41e460

Please sign in to comment.