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 filter now only filters on text, value and metadata p…
Browse files Browse the repository at this point in the history
…rops (#461)

* 3 new tests for filter. metadata wip

* change filter data from to selected properties.
Prior was using all attributes.

* add event for modified properties filter depends on

* split filter setup so it can called on prop change

* fix bug in tests

* remove .only from test

* filter: add a warning when there is no data.

* fix getTag to work with no attributes

* add tests for filter

* fix accessibility

* PickList and ValueList: share more code between lists 461 (#484)

* remove .only from test

* fix lint errors

* add temporary backup files to gitignore

* abstract common methods to a shared location

* share selection/deselection tests

* fix shiftClick behavior for valueList

* abstract shared tests between lists

* improve method return type

* normalize render functions

* add this typing to shared logic file
https://stackoverflow.com/questions/54710927/typescript-types-and-bind

* moving compact prop to alpha order

* added typing to items

* add compact property to value list

* fix type error on textHeading

* fix typings

* update lists to use common MO callback
MO: Mutation Observer

* comment touchup

* fix backwards compat and event typing

* add missing typings

* space the tests out

* code review follow up

* abstracted some types

* move comment up

* PickList and ValueList: share render methods between lists 461 (#488)

* fix lint errors

* add temporary backup files to gitignore

* abstract common methods to a shared location

* share selection/deselection tests

* fix shiftClick behavior for valueList

* abstract shared tests between lists

* improve method return type

* normalize render functions

* abstract the render methods to functional comp

* switch value list over to use shared render

* fix calcite-block accessible test.

* update calciteListItemPropsUpdated event docs

* calciteListItemPropsUpdated is internal only
  • Loading branch information
pr3tori4n authored Nov 6, 2019
1 parent f4a69f9 commit 51fd559
Show file tree
Hide file tree
Showing 16 changed files with 666 additions and 367 deletions.
4 changes: 4 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -24,3 +24,7 @@ Thumbs.db
UserInterfaceState.xcuserstate
.env
src/**/components.d.ts

# backup files
*.bak
*.orig
2 changes: 1 addition & 1 deletion src/components/calcite-block/calcite-block.e2e.ts
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ describe("calcite-block", () => {
it("is accessible", async () =>
accessible(`
<calcite-block heading="heading" summary="summary" open collapsible>
<div slot=${SLOTS.icon}>✅</div>
<div slot=${SLOTS.icon}>✅</div>
<div>content</div>
<label slot=${SLOTS.control}>test <input placeholder="control"/></label>
</calcite-block>
Expand Down
79 changes: 79 additions & 0 deletions src/components/calcite-filter/calcite-filter.e2e.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,79 @@
import { E2EPage, newE2EPage } from "@stencil/core/testing";
import { accessible, hidden, renders } from "../../tests/commonTests";

describe("calcite-filter", () => {
it("renders", async () => renders("calcite-filter"));

it("honors hidden attribute", async () => hidden("calcite-filter"));
it("is accessible", async () => accessible(`<calcite-filter></calcite-filter>`));

describe("filter behavior", () => {
let page: E2EPage;
beforeEach(async () => {
page = await newE2EPage();
await page.setContent("<calcite-filter></calcite-filter>");
await page.evaluate(() => {
const filter = document.querySelector("calcite-filter");
filter.data = [
{
name: "Harry",
description: "developer",
value: "harry",
metadata: { haircolor: "red", favoriteBand: "MetallicA" }
},
{
name: "Matt",
description: "developer",
value: "matt",
metadata: { haircolor: "black", favoriteBand: "unknown" }
},
{
name: "Franco",
description: "developer",
value: "franco",
metadata: { haircolor: "black", favoriteBand: "The Mars Volta" }
},
{
name: "Katy",
description: "engineer",
value: "katy",
metadata: { haircolor: "red", favoriteBand: "unknown" }
},
{
name: "Jon",
description: "developer",
value: "jon",
metadata: { haircolor: "brown", favoriteBand: "Hippity Hops" }
}
];
});
});
it("emits an event with filtered data after a search query is typed into the input", async () => {
await page.evaluate(() => {
const filter = document.querySelector("calcite-filter");
const filterInput = filter.shadowRoot.querySelector("input");
filterInput.value = "developer";
filterInput.dispatchEvent(new Event("input"));
});
const event = await page.waitForEvent("calciteFilterChange");
expect(event.detail).toBeDefined();
expect(event.detail.find((element) => element.value === "harry")).toBeDefined();
expect(event.detail.find((element) => element.value === "matt")).toBeDefined();
expect(event.detail.find((element) => element.value === "franco")).toBeDefined();
expect(event.detail.find((element) => element.value === "jon")).toBeDefined();
expect(event.detail.find((element) => element.value === "katy")).toBeUndefined();
});
it("searches recursively in data and works and matches on a partial string ignoring case", async () => {
await page.evaluate(() => {
const filter = document.querySelector("calcite-filter");
const filterInput = filter.shadowRoot.querySelector("input");
filterInput.value = "volt";
filterInput.dispatchEvent(new Event("input"));
});
const event = await page.waitForEvent("calciteFilterChange");
expect(event.detail).toBeDefined();
expect(event.detail.length).toBe(1);
expect(event.detail.find((element) => element.value === "franco")).toBeDefined();
});
});
});
14 changes: 13 additions & 1 deletion src/components/calcite-filter/calcite-filter.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,13 @@ export class CalciteFilter {
filter = debounce((value: string): void => {
const regex = new RegExp(value, "ig");

if (this.data.length === 0) {
console.warn(`No data was passed to calcite-filter.
The data property expects an array of objects`);
this.calciteFilterChange.emit([]);
return;
}

const find = (input: object, RE: RegExp) => {
let found = false;
forIn(input, (val) => {
Expand Down Expand Up @@ -99,7 +106,12 @@ export class CalciteFilter {
<Host>
<label>
{this.textLabel}
<input type="text" placeholder={this.textPlaceholder} onInput={this.inputHandler} />
<input
type="text"
placeholder={this.textPlaceholder}
onInput={this.inputHandler}
aria-label={this.textLabel || "Filter"}
/>
<div class={CSS.searchIcon}>
<CalciteIcon size="16" path={search16} />
</div>
Expand Down
34 changes: 29 additions & 5 deletions src/components/calcite-pick-list-item/calcite-pick-list-item.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,11 @@ export class CalcitePickListItem {
//
// --------------------------------------------------------------------------

/**
* Compact removes the selection icon (radio or checkbox) and adds a compact attribute. This allows for a more compact version of the pick-list-item.
*/
@Prop({ reflect: true }) compact = false;

/**
* When true, the item cannot be clicked and is visually muted.
*/
Expand All @@ -42,16 +47,15 @@ export class CalcitePickListItem {
*/
@Prop() metadata: object;

@Watch("metadata") metadataWatchHandler() {
this.calciteListItemPropsUpdated.emit();
}

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

/**
* Compact removes the selection icon (radio or checkbox) and adds a compact attribute. This allows for a more compact version of the pick-list-item.
*/
@Prop({ reflect: true }) compact = false;

@Watch("selected")
selectedWatchHandler(newValue) {
if (this.isSelected !== newValue) {
Expand All @@ -65,16 +69,28 @@ export class CalcitePickListItem {
*/
@Prop({ reflect: true }) textHeading: string;

@Watch("textHeading") textHeadingWatchHandler() {
this.calciteListItemPropsUpdated.emit();
}

/**
* An optional description for this item. This will appear below the label text.
*/
@Prop({ reflect: true }) textDescription?: string;

@Watch("textDescription") textDescriptionWatchHandler() {
this.calciteListItemPropsUpdated.emit();
}

/**
* The main label for this item. This will appear next to the icon.
*/
@Prop({ reflect: true }) textLabel: string;

@Watch("textLabel") textLabelWatchHandler() {
this.calciteListItemPropsUpdated.emit();
}

/**
* A unique value used to identify this item - similar to the value attribute on an <input>.
*/
Expand Down Expand Up @@ -102,6 +118,14 @@ export class CalcitePickListItem {
*/
@Event() calciteListItemChange: EventEmitter;

/**
* Emitted whenever the the item's textLabel, textDescription, value or metadata properties are modified.
* It also fires on textHeading property changes for backwards compatibility until that's fully removed.
* @event calciteListItemPropsUpdated
* @internal
*/
@Event() calciteListItemPropsUpdated: EventEmitter;

// --------------------------------------------------------------------------
//
// Public Methods
Expand Down
81 changes: 16 additions & 65 deletions src/components/calcite-pick-list/calcite-pick-list.e2e.ts
Original file line number Diff line number Diff line change
@@ -1,73 +1,16 @@
import { newE2EPage } from "@stencil/core/testing";
import { CSS, ICON_TYPES } from "./resources";
import { ICON_TYPES } from "./resources";
import { hidden, renders } from "../../tests/commonTests";
import { tests } from "./shared-list-tests";

describe("calcite-pick-list", () => {
it("should render", async () => {
const page = await newE2EPage();
const { selectionAndDeselection, filterBehavior, disabledStates } = tests;

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

describe("Selection and Deselection", () => {
describe("when multiple is false and a item is clicked", () => {
it("should emit an event with the last selected item data", async () => {
const page = await newE2EPage();
await page.setContent(`<calcite-pick-list>
<calcite-pick-list-item value="one"></calcite-pick-list-item>
<calcite-pick-list-item value="two"></calcite-pick-list-item>
</calcite-pick-list>`);

const pickList = await page.find("calcite-pick-list");
const item1 = await pickList.find("[value=one]");
const item2 = await pickList.find("[value=two]");
const toggleSpy = await pickList.spyOnEvent("calciteListChange");

await item1.click();
await item2.click();
expect(toggleSpy).toHaveReceivedEventTimes(2);
});
});
describe("when multiple is true and a item is clicked", () => {
it("should emit an event with each selected item's data", async () => {
const page = await newE2EPage();
await page.setContent(`<calcite-pick-list multiple>
<calcite-pick-list-item value="one"></calcite-pick-list-item>
<calcite-pick-list-item value="two"></calcite-pick-list-item>
</calcite-pick-list>`);

const pickList = await page.find("calcite-pick-list");
const item1 = await pickList.find("[value=one]");
const item2 = await pickList.find("[value=two]");
const toggleSpy = await pickList.spyOnEvent("calciteListChange");

await item1.click();
await item2.click();
await item2.click(); // deselect
expect(toggleSpy).toHaveReceivedEventTimes(3);
});
});
describe("preselected items", () => {
it("should be included in the list of selected items", async () => {
const page = await newE2EPage();
await page.setContent(`<calcite-pick-list multiple>
<calcite-pick-list-item value="one" selected></calcite-pick-list-item>
<calcite-pick-list-item value="two"></calcite-pick-list-item>
</calcite-pick-list>`);

const numSelected = await page.evaluate(() => {
const pickList = document.querySelector("calcite-pick-list");
return pickList.getSelectedItems().then((result) => {
return result.size;
});
});

expect(numSelected).toBe(1);
});
});
selectionAndDeselection("pick");
});

describe("icon logic", () => {
Expand All @@ -92,4 +35,12 @@ describe("calcite-pick-list", () => {
expect(icon).toBe(ICON_TYPES.square);
});
});

describe("filter behavior (hide/show items)", () => {
filterBehavior("pick");
});

describe("disabled states", () => {
disabledStates("pick");
});
});
Loading

0 comments on commit 51fd559

Please sign in to comment.