Skip to content

Commit

Permalink
add basic Filterbar tests for both mouse and keyboard
Browse files Browse the repository at this point in the history
  • Loading branch information
heswell committed Dec 14, 2023
1 parent da4d295 commit 4eb6792
Show file tree
Hide file tree
Showing 11 changed files with 295 additions and 101 deletions.
1 change: 1 addition & 0 deletions vuu-ui/packages/vuu-data-test/src/makeSuggestions.ts
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ const getUniqueValues = (table: Table, column: string, pattern = "") => {
uniqueValues.push(value);
}
}
uniqueValues.sort();
if (cachedEntry) {
cachedEntry.set(column, uniqueValues);
} else {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import { clear } from "console";
import React from "react";
// TODO try and get TS path alias working to avoid relative paths like this
import { DefaultFilterBar } from "../../../../../../showcase/src/examples/Filters/FilterBar/FilterBar.examples";
Expand All @@ -9,6 +10,34 @@ const ADD_BUTTON = ".vuuFilterBar-add";
const FILTER_CLAUSE = ".vuuFilterClause";
const FILTER_CLAUSE_FIELD = ".vuuFilterClauseField";

const findOverflowItem = (className: string) =>
cy.get(OVERFLOW_CONTAINER).find(className);

const clickListItem = (label: string) => {
cy.findByText(label).realHover();
cy.findByText(label).realClick();
};

const clickListItems = (...labels: string[]) => {
for (const label of labels) {
clickListItem(label);
}
};

const clickButton = (label: string) => {
cy.findByText(label).should("be.visible");
cy.findByText(label).realClick();
};

const waitUntilEditableLabelIsFocused = (overflowItemClassName: string) =>
findOverflowItem(overflowItemClassName)
.find(".vuuEditableLabel")
.find("input")
.should("be.focused");

const assertInputValue = (className: string, value: string) =>
cy.get(`${className} input`).should("have.attr", "value", value);

describe("WHEN it initially renders", () => {
it("THEN expected classname is present", () => {
cy.mount(<DefaultFilterBar />);
Expand All @@ -24,7 +53,7 @@ describe("WHEN it initially renders", () => {
});
});

describe("The mouse users experience", () => {
describe("The mouse user", () => {
describe("WHEN user click Add button on empty Filterbar", () => {
it("THEN new FilterClause is initiated", () => {
cy.mount(<DefaultFilterBar />);
Expand Down Expand Up @@ -57,16 +86,11 @@ describe("The mouse users experience", () => {
it("THEN focus moves to operator field", () => {
cy.mount(<DefaultFilterBar />);
cy.get(ADD_BUTTON).realClick();
cy.findByText("currency").realHover();
cy.findByText("currency").realClick();
clickListItem("currency");
cy.get(FILTER_CLAUSE).should("have.length", 1);
cy.get(FILTER_CLAUSE_FIELD).should("have.length", 2);

cy.get(".vuuFilterClauseColumn input").should(
"have.attr",
"value",
"currency"
);
assertInputValue(".vuuFilterClauseColumn", "currency");

cy.get(".vuuFilterClauseOperator input").should("be.focused");
cy.get(".vuuFilterClauseOperator input").should(
Expand All @@ -83,10 +107,7 @@ describe("The mouse users experience", () => {
it("THEN focus moves to value field", () => {
cy.mount(<DefaultFilterBar />);
cy.get(ADD_BUTTON).realClick();
cy.findByText("currency").realHover();
cy.findByText("currency").realClick();
cy.findByText("=").realHover();
cy.findByText("=").realClick();
clickListItems("currency", "=");

cy.get(FILTER_CLAUSE).should("have.length", 1);
cy.get(FILTER_CLAUSE_FIELD).should("have.length", 3);
Expand All @@ -105,13 +126,7 @@ describe("The mouse users experience", () => {
it("THEN Save menu is shown", () => {
cy.mount(<DefaultFilterBar />);
cy.get(ADD_BUTTON).realClick();
cy.findByText("currency").realHover();
cy.findByText("currency").realClick();
cy.findByText("=").realHover();
cy.findByText("=").realClick();
cy.findByText("USD").realHover();
cy.findByText("USD").realClick();

clickListItems("currency", "=", "USD");
cy.get(FILTER_CLAUSE).should("have.length", 1);
cy.get(`${FILTER_CLAUSE} ${FILTER_CLAUSE}-clearButton`).should(
"have.length",
Expand All @@ -122,28 +137,187 @@ describe("The mouse users experience", () => {
});

describe("WHEN user clicks APPLY AND SAVE", () => {
it("THEN filter is saved", () => {
it("THEN filtersChangedHandler callback is invoked", () => {
const onFiltersChanged = cy.stub().as("filtersChangedHandler");
cy.mount(<DefaultFilterBar onFiltersChanged={onFiltersChanged} />);
cy.get(ADD_BUTTON).realClick();
cy.findByText("currency").realHover();
cy.findByText("currency").realClick();
cy.findByText("=").realHover();
cy.findByText("=").realClick();
cy.findByText("USD").realHover();
cy.findByText("USD").realClick();
cy.findByText("APPLY AND SAVE").should("be.visible");
cy.findByText("APPLY AND SAVE").realClick();
cy.get("@filtersChangedHandler").should("have.been.called");
clickListItems("currency", "=", "USD");
clickButton("APPLY AND SAVE");
cy.get("@filtersChangedHandler").should("be.calledWith", [
{ column: "currency", op: "=", value: "USD" },
]);
});

it("THEN filter is applied", () => {
const onFiltersChanged = cy.stub().as("filtersChangedHandler");
const onFilterApplied = cy.stub().as("onFilterApplied");
cy.mount(
<DefaultFilterBar
onApplyFilter={onFilterApplied}
onFiltersChanged={onFiltersChanged}
/>
);
cy.get(ADD_BUTTON).realClick();
clickListItems("currency", "=", "USD");
clickButton("APPLY AND SAVE");
cy.get("@filtersChangedHandler").should("be.calledWith", [
{ column: "currency", op: "=", value: "USD" },
]);
cy.get("@onFilterApplied").should("be.calledWith", {
filter: 'currency = "USD"',
filterStruct: { column: "currency", op: "=", value: "USD" },
});
});

it("THEN filter pill is displayed, label is in edit state and focused", () => {
cy.mount(<DefaultFilterBar />);
cy.get(ADD_BUTTON).realClick();
clickListItems("currency", "=", "USD");
clickButton("APPLY AND SAVE");
cy.get(OVERFLOW_CONTAINER).find("> *").should("have.length", 2);
findOverflowItem(".vuuFilterPill").should("have.length", 1);
findOverflowItem(".vuuFilterPill")
.find(".vuuEditableLabel")
.should("have.class", "vuuEditableLabel-editing");
findOverflowItem(".vuuFilterPill")
.find(".vuuEditableLabel")
.find("input")
.should("be.focused");
});

describe("WHEN user overtypes label and presses ENTER", () => {
it("THEN label is applied and exits edit mode", () => {
const onFiltersChanged = cy.stub().as("filtersChangedHandler");
cy.mount(<DefaultFilterBar onFiltersChanged={onFiltersChanged} />);
cy.get(ADD_BUTTON).realClick();
clickListItems("currency", "=", "USD");
clickButton("APPLY AND SAVE");
waitUntilEditableLabelIsFocused(".vuuFilterPill");
cy.realType("test");
cy.realPress("Enter");
findOverflowItem(".vuuFilterPill")
.find(".vuuEditableLabel")
.should("not.have.class", "vuuEditableLabel-editing");
cy.get("@filtersChangedHandler").should("be.calledWith", [
{ column: "currency", op: "=", value: "USD", name: "test" },
]);
});

it("THEN filter pill has focus", () => {
cy.mount(<DefaultFilterBar />);
cy.get(ADD_BUTTON).realClick();
clickListItems("currency", "=", "USD");
clickButton("APPLY AND SAVE");
waitUntilEditableLabelIsFocused(".vuuFilterPill");
cy.realType("test");
cy.realPress("Enter");
findOverflowItem(".vuuFilterPill").should("be.focused");
});
});
});
});

// describe("The keyboard users experience", () => {
// describe("WHEN user click Add button on empty Filterbar", () => {
// it("THEN new FilterClasue is initiated adn column is focussed", () => {});
// });
// });
describe("The keyboard user", () => {
describe("WHEN user navigates with keyboard to empty Filterbar", () => {
it("THEN add button is focussed", () => {
cy.mount(<DefaultFilterBar />);
cy.findByTestId("pre-filterbar").find("input").focus();
cy.realPress("Tab");
cy.get(ADD_BUTTON).should("be.focused");
});

describe("WHEN user presses ADD then uses keyboard to select currency", () => {
it("THEN currency is selected and focus moves to operator", () => {
cy.mount(<DefaultFilterBar />);

cy.findByTestId("pre-filterbar").find("input").focus();
cy.realPress("Tab");
cy.get(ADD_BUTTON).should("be.focused");
cy.realPress("Enter");
cy.findByRole("combobox").should("be.focused");

// make sure columns list has renderered
cy.findByText("currency").should("exist");
cy.realPress("ArrowDown");
cy.get(".vuuListItem.vuuHighlighted").should("have.text", "currency");
cy.realPress("Enter");

assertInputValue(".vuuFilterClauseColumn", "currency");

cy.get(".vuuFilterClauseOperator input").should("be.focused");
cy.get(".vuuFilterClauseOperator input").should(
"have.attr",
"aria-expanded",
"true"
);
});
});
describe("THEN WHEN user uses keyboard to select =", () => {
it("THEN = is selected and focus moves to value", () => {
cy.mount(<DefaultFilterBar />);

cy.findByTestId("pre-filterbar").find("input").focus();
cy.realPress("Tab");
cy.get(ADD_BUTTON).should("be.focused");
cy.realPress("Enter");
cy.findByRole("combobox").should("be.focused");

// make sure columns list has renderered
cy.findByText("currency").should("exist");
cy.realPress("ArrowDown");
cy.get(".vuuListItem.vuuHighlighted").should("have.text", "currency");
cy.realPress("Enter");

cy.findByText("=").should("exist");
cy.get(".vuuListItem.vuuHighlighted").should("have.text", "=");
cy.realPress("Enter");

assertInputValue(".vuuFilterClauseOperator", "=");

cy.get(".vuuFilterClauseValue input").should("be.focused");
cy.get(".vuuFilterClauseValue input").should(
"have.attr",
"aria-expanded",
"true"
);
});
describe("THEN WHEN user uses keyboard to select USD", () => {
it("THEN USD is selected, and focus moves to Menu", () => {
cy.mount(<DefaultFilterBar />);

cy.findByTestId("pre-filterbar").find("input").focus();
cy.realPress("Tab");
cy.get(ADD_BUTTON).should("be.focused");
cy.realPress("Enter");
cy.findByRole("combobox").should("be.focused");

// make sure columns list has renderered
cy.findByText("currency").should("exist");
cy.realPress("ArrowDown");
cy.realPress("Enter");

cy.findByText("=").should("exist");
cy.realPress("Enter");

cy.findByText("USD").should("exist");
cy.realPress("ArrowDown");
cy.realPress("ArrowDown");
cy.realPress("ArrowDown");
cy.realPress("ArrowDown");
cy.realPress("Enter");

assertInputValue(".vuuFilterClauseValue", "USD");

cy.get(FILTER_CLAUSE).should("have.length", 1);
cy.get(`${FILTER_CLAUSE} ${FILTER_CLAUSE}-clearButton`).should(
"have.length",
1
);
cy.get(".vuuFilterBuilderMenuList")
.should("be.visible")
.should("be.focused");
});
});
});
});
});
38 changes: 22 additions & 16 deletions vuu-ui/packages/vuu-filters/src/filter-bar/useFilterBar.ts
Original file line number Diff line number Diff line change
Expand Up @@ -61,17 +61,12 @@ export const useFilterBar = ({
const editingFilter = useRef<Filter | undefined>();
const [activeFilterIndex, setActiveFilterIndex] =
useState<number[]>(activeFilterIdexProp);
const [showMenu, _setShowMenu] = useState(showMenuProp);
const [showMenu, setShowMenu] = useState(showMenuProp);
const [editFilter, setEditFilter] = useState<
Partial<Filter> | FilterWithPartialClause | undefined
>();
const [promptProps, setPromptProps] = useState<PromptProps | null>(null);

const setShowMenu = useCallback((show) => {
console.trace(`set show menu ${show}`);
_setShowMenu(show);
}, []);

const {
filters,
onAddFilter,
Expand Down Expand Up @@ -280,12 +275,17 @@ export const useFilterBar = ({
return true;
}

case "and-clause":
setEditFilter((filter) =>
addClause(filter as Filter, EMPTY_FILTER_CLAUSE)
case "and-clause": {
const newFilter = addClause(
editFilter as Filter,
EMPTY_FILTER_CLAUSE
);
console.log({ newFilter });
setEditFilter(newFilter);
setShowMenu(false);
return true;
}

case "or-clause":
setEditFilter((filter) =>
addClause(filter as Filter, {}, { combineWith: "or" })
Expand Down Expand Up @@ -328,7 +328,7 @@ export const useFilterBar = ({

const handleClickAddFilter = useCallback(() => {
setEditFilter({});
}, []);
}, [setEditFilter]);

const handleClickRemoveFilter = useCallback(() => {
setEditFilter(undefined);
Expand All @@ -340,12 +340,17 @@ export const useFilterBar = ({
onExitEditMode: handleExitEditFilterName,
};

const handleChangeFilterClause = (filterClause: Partial<FilterClause>) => {
if (filterClause !== undefined) {
setEditFilter((filter) => replaceClause(filter, filterClause));
setShowMenu(true);
}
};
const handleChangeFilterClause = useCallback(
(filterClause: Partial<FilterClause>) => {
console.log(`handleCHangeFilterClause ${JSON.stringify(filterClause)}`);
if (filterClause !== undefined) {
const newFilter = replaceClause(editFilter, filterClause);
setEditFilter(newFilter);
setShowMenu(true);
}
},
[editFilter]
);

const handleCancelFilterClause = useCallback<FilterClauseCancelHandler>(
(reason) => {
Expand Down Expand Up @@ -396,6 +401,7 @@ export const useFilterBar = ({
console.log(`keydown from List ${evt.key}`);
const { current: container } = containerRef;
if (evt.key === "Backspace" && container) {
evt.preventDefault();
const fields = Array.from(
container.querySelectorAll(".vuuFilterClauseField")
);
Expand Down
Loading

0 comments on commit 4eb6792

Please sign in to comment.