From 4eb679276949b256e976541bbf262447b1ac3ce3 Mon Sep 17 00:00:00 2001 From: heswell Date: Thu, 14 Dec 2023 11:06:32 +0000 Subject: [PATCH] add basic Filterbar tests for both mouse and keyboard --- .../vuu-data-test/src/makeSuggestions.ts | 1 + .../__component__/Filterbar/Filterbar.cy.tsx | 242 +++++++++++++++--- .../src/filter-bar/useFilterBar.ts | 38 +-- .../src/filter-clause/FilterClauseEditor.tsx | 21 +- .../src/filter-clause/TextInput.tsx | 10 +- .../src/filter-clause/filterClauseTypes.ts | 1 + .../filter-clause/useFilterClauseEditor.ts | 31 ++- .../src/combo-box/ComboBox.tsx | 5 + .../src/combo-box/useCombobox.ts | 8 +- .../Filters/FilterBar/FilterBar.examples.tsx | 36 ++- vuu-ui/tsconfig.json | 3 +- 11 files changed, 295 insertions(+), 101 deletions(-) diff --git a/vuu-ui/packages/vuu-data-test/src/makeSuggestions.ts b/vuu-ui/packages/vuu-data-test/src/makeSuggestions.ts index 842ac2cb85..9a5e6e7873 100644 --- a/vuu-ui/packages/vuu-data-test/src/makeSuggestions.ts +++ b/vuu-ui/packages/vuu-data-test/src/makeSuggestions.ts @@ -20,6 +20,7 @@ const getUniqueValues = (table: Table, column: string, pattern = "") => { uniqueValues.push(value); } } + uniqueValues.sort(); if (cachedEntry) { cachedEntry.set(column, uniqueValues); } else { diff --git a/vuu-ui/packages/vuu-filters/src/__tests__/__component__/Filterbar/Filterbar.cy.tsx b/vuu-ui/packages/vuu-filters/src/__tests__/__component__/Filterbar/Filterbar.cy.tsx index 45678722f3..513480a40d 100644 --- a/vuu-ui/packages/vuu-filters/src/__tests__/__component__/Filterbar/Filterbar.cy.tsx +++ b/vuu-ui/packages/vuu-filters/src/__tests__/__component__/Filterbar/Filterbar.cy.tsx @@ -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"; @@ -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(); @@ -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(); @@ -57,16 +86,11 @@ describe("The mouse users experience", () => { it("THEN focus moves to operator field", () => { cy.mount(); 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( @@ -83,10 +107,7 @@ describe("The mouse users experience", () => { it("THEN focus moves to value field", () => { cy.mount(); 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); @@ -105,13 +126,7 @@ describe("The mouse users experience", () => { it("THEN Save menu is shown", () => { cy.mount(); 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", @@ -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(); 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( + + ); + 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(); + 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(); + 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(); + 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(); + 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(); + + 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(); + + 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(); + + 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"); + }); + }); + }); + }); +}); diff --git a/vuu-ui/packages/vuu-filters/src/filter-bar/useFilterBar.ts b/vuu-ui/packages/vuu-filters/src/filter-bar/useFilterBar.ts index dae19f87d3..c7a8ba3908 100644 --- a/vuu-ui/packages/vuu-filters/src/filter-bar/useFilterBar.ts +++ b/vuu-ui/packages/vuu-filters/src/filter-bar/useFilterBar.ts @@ -61,17 +61,12 @@ export const useFilterBar = ({ const editingFilter = useRef(); const [activeFilterIndex, setActiveFilterIndex] = useState(activeFilterIdexProp); - const [showMenu, _setShowMenu] = useState(showMenuProp); + const [showMenu, setShowMenu] = useState(showMenuProp); const [editFilter, setEditFilter] = useState< Partial | FilterWithPartialClause | undefined >(); const [promptProps, setPromptProps] = useState(null); - const setShowMenu = useCallback((show) => { - console.trace(`set show menu ${show}`); - _setShowMenu(show); - }, []); - const { filters, onAddFilter, @@ -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" }) @@ -328,7 +328,7 @@ export const useFilterBar = ({ const handleClickAddFilter = useCallback(() => { setEditFilter({}); - }, []); + }, [setEditFilter]); const handleClickRemoveFilter = useCallback(() => { setEditFilter(undefined); @@ -340,12 +340,17 @@ export const useFilterBar = ({ onExitEditMode: handleExitEditFilterName, }; - const handleChangeFilterClause = (filterClause: Partial) => { - if (filterClause !== undefined) { - setEditFilter((filter) => replaceClause(filter, filterClause)); - setShowMenu(true); - } - }; + const handleChangeFilterClause = useCallback( + (filterClause: Partial) => { + console.log(`handleCHangeFilterClause ${JSON.stringify(filterClause)}`); + if (filterClause !== undefined) { + const newFilter = replaceClause(editFilter, filterClause); + setEditFilter(newFilter); + setShowMenu(true); + } + }, + [editFilter] + ); const handleCancelFilterClause = useCallback( (reason) => { @@ -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") ); diff --git a/vuu-ui/packages/vuu-filters/src/filter-clause/FilterClauseEditor.tsx b/vuu-ui/packages/vuu-filters/src/filter-clause/FilterClauseEditor.tsx index 819b885470..bcb3640699 100644 --- a/vuu-ui/packages/vuu-filters/src/filter-clause/FilterClauseEditor.tsx +++ b/vuu-ui/packages/vuu-filters/src/filter-clause/FilterClauseEditor.tsx @@ -49,6 +49,7 @@ export const FilterClauseEditor = ({ onChangeValue, onClear, onClearKeyDown, + onDeselectValue, onSelectionChangeColumn, onSelectionChangeOperator, operator, @@ -63,21 +64,6 @@ export const FilterClauseEditor = ({ tableSchema, }); - const handleFocus = useCallback(() => { - console.log("focus"); - }, []); - - const handleOpenChange = useCallback( - (open, closeReason) => { - if (open) { - onDropdownOpen?.(); - } else { - onDropdownClose?.(closeReason); - } - }, - [onDropdownClose, onDropdownOpen] - ); - const getInputElement = useCallback(() => { if (selectedColumn === null || operator === undefined) { return null; @@ -92,7 +78,7 @@ export const FilterClauseEditor = ({ column={selectedColumn} data-field="value" filterClause={filterClause} - onOpenChange={handleOpenChange} + onDeselect={onDeselectValue} onInputComplete={onChangeValue} operator={operator} ref={valueRef} @@ -127,7 +113,6 @@ export const FilterClauseEditor = ({ operator, InputProps, filterClause, - handleOpenChange, onChangeValue, valueRef, suggestionProvider, @@ -144,7 +129,6 @@ export const FilterClauseEditor = ({ data-field="column" initialHighlightedIndex={0} itemToString={(column) => column.name} - onOpenChange={handleOpenChange} onSelectionChange={onSelectionChangeColumn} ref={columnRef} source={columns} @@ -160,7 +144,6 @@ export const FilterClauseEditor = ({ })} data-field="operator" initialHighlightedIndex={0} - onOpenChange={handleOpenChange} onSelectionChange={onSelectionChangeOperator} ref={operatorRef} source={getOperators(selectedColumn)} diff --git a/vuu-ui/packages/vuu-filters/src/filter-clause/TextInput.tsx b/vuu-ui/packages/vuu-filters/src/filter-clause/TextInput.tsx index d410cf5d5c..7127205bad 100644 --- a/vuu-ui/packages/vuu-filters/src/filter-clause/TextInput.tsx +++ b/vuu-ui/packages/vuu-filters/src/filter-clause/TextInput.tsx @@ -23,6 +23,8 @@ import { import { ExpandoCombobox } from "./ExpandoCombobox"; import { FilterClauseValueEditor } from "./filterClauseTypes"; +const selectionKeys = ["Enter", " "]; + export interface TextInputProps extends FilterClauseValueEditor, HTMLAttributes { @@ -41,7 +43,7 @@ export const TextInput = forwardRef(function TextInput( className, column, "data-field": dataField, - onOpenChange, + onDeselect, onInputComplete, operator, suggestionProvider = useTypeaheadSuggestions, @@ -133,10 +135,10 @@ export const TextInput = forwardRef(function TextInput( initialHighlightedIndex={0} source={typeaheadValues} onInputChange={handleInputChange} - onOpenChange={onOpenChange} onSelectionChange={handleMultiValueSelectionChange} ref={forwardedRef} selectionStrategy="multiple" + selectionKeys={selectionKeys} value={value} /> ); @@ -157,7 +159,6 @@ export const TextInput = forwardRef(function TextInput( } source={typeaheadValues} onInputChange={handleInputChange} - onOpenChange={onOpenChange} onSelectionChange={handleSingleValueSelectionChange} ref={forwardedRef} value={value} @@ -189,7 +190,7 @@ export const TextInput = forwardRef(function TextInput( source={typeaheadValues} title="value" onInputChange={handleInputChange} - onOpenChange={onOpenChange} + onDeselect={onDeselect} onSelectionChange={handleSingleValueSelectionChange} ref={forwardedRef} value={value} @@ -207,6 +208,7 @@ export const TextInput = forwardRef(function TextInput( forwardedRef, value, valueInputValue, + onDeselect, handleSingleValueSelectionChange, ]); diff --git a/vuu-ui/packages/vuu-filters/src/filter-clause/filterClauseTypes.ts b/vuu-ui/packages/vuu-filters/src/filter-clause/filterClauseTypes.ts index 3498f8580b..5b6a53f8b2 100644 --- a/vuu-ui/packages/vuu-filters/src/filter-clause/filterClauseTypes.ts +++ b/vuu-ui/packages/vuu-filters/src/filter-clause/filterClauseTypes.ts @@ -8,6 +8,7 @@ export interface FilterClauseValueEditor { InputProps?: InputProps; filterClause: Partial; column: ColumnDescriptor; + onDeselect?: () => void; onInputComplete: (value: T | T[]) => void; onOpenChange?: (open: boolean, closeReason: CloseReason) => void; table?: VuuTable; diff --git a/vuu-ui/packages/vuu-filters/src/filter-clause/useFilterClauseEditor.ts b/vuu-ui/packages/vuu-filters/src/filter-clause/useFilterClauseEditor.ts index ea3dedce1c..f9c6bfa057 100644 --- a/vuu-ui/packages/vuu-filters/src/filter-clause/useFilterClauseEditor.ts +++ b/vuu-ui/packages/vuu-filters/src/filter-clause/useFilterClauseEditor.ts @@ -73,7 +73,6 @@ const focusNextElement = () => { const filterClause = filterClauseField?.closest(".vuuFilterClause"); if (filterClause && filterClauseField) { if (filterClauseField.classList.contains("vuuFilterClauseValue")) { - console.log("focus clear button"); const clearButton = filterClause.querySelector( ".vuuFilterClause-clearButton" ) as HTMLButtonElement; @@ -274,15 +273,15 @@ export const useFilterClauseEditor = ({ value, }); } - // This have no effect if we are inside a FilterBar - // requestAnimationFrame(() => { - // focusNextElement(); - // }); } }, [onChange, operator, selectedColumn?.name] ); + const handleDeselectValue = useCallback(() => { + setValue(undefined); + }, []); + const handleKeyDownInput = useCallback( (evt: KeyboardEvent) => { if (["ArrowLeft", "ArrowRight"].includes(evt.key)) { @@ -294,21 +293,24 @@ export const useFilterClauseEditor = ({ const input = evt.target as HTMLInputElement; const field = input.closest("[data-field]") as HTMLElement; if (field.dataset.field === "column") { - // evt.stopPropagation(); const column = findColumn(input.value); if (column) { setSelectedColumn(column); focusNextElement(); } } else if (field.dataset.field === "value") { - console.log(`Enter value ${input.value}`); - const newValue = input.value; - setValue(newValue); - onChange({ - column: selectedColumn?.name, - op: operator, - value: newValue, - }); + if (operator === "starts") { + // // don't let this bubble to the Toolbar, it would be + // // interpreted as selection + evt.stopPropagation(); + const newValue = input.value; + setValue(newValue); + onChange({ + column: selectedColumn?.name, + op: operator, + value: newValue, + }); + } } } }, @@ -367,6 +369,7 @@ export const useFilterClauseEditor = ({ onChangeValue: handleChangeValue, onClear: handleClear, onClearKeyDown: handleClearKeyDown, + onDeselectValue: handleDeselectValue, onSelectionChangeColumn: handleSelectionChangeColumn, onSelectionChangeOperator: handleSelectionChangeOperator, operator, diff --git a/vuu-ui/packages/vuu-ui-controls/src/combo-box/ComboBox.tsx b/vuu-ui/packages/vuu-ui-controls/src/combo-box/ComboBox.tsx index 04601fe08f..84618f28b1 100644 --- a/vuu-ui/packages/vuu-ui-controls/src/combo-box/ComboBox.tsx +++ b/vuu-ui/packages/vuu-ui-controls/src/combo-box/ComboBox.tsx @@ -38,6 +38,7 @@ export interface ComboBoxProps< getFilterRegex?: (inputValue: string) => RegExp; initialHighlightedIndex?: number; itemsToString?: (items: Item[]) => string; + onDeselect?: () => void; onSetSelectedText?: (text: string) => void; disableFilter?: boolean; value?: string; @@ -74,9 +75,11 @@ export const ComboBox = forwardRef(function Combobox< isOpen: isOpenProp, itemToString = defaultItemToString, itemsToString, + onDeselect, onOpenChange: onOpenChangeProp, onSelectionChange, selected: selectedProp, + selectionKeys, selectionStrategy, source, value: valueProp, @@ -143,6 +146,7 @@ export const ComboBox = forwardRef(function Combobox< label: props.title, listRef, onBlur, + onDeselect, onFocus, onChange, onSelect, @@ -154,6 +158,7 @@ export const ComboBox = forwardRef(function Combobox< onSelectionChange, onSetSelectedText, selected: selectedProp, + selectionKeys, selectionStrategy, value: initialValue, }); diff --git a/vuu-ui/packages/vuu-ui-controls/src/combo-box/useCombobox.ts b/vuu-ui/packages/vuu-ui-controls/src/combo-box/useCombobox.ts index 6e34a5b28c..a5063293bf 100644 --- a/vuu-ui/packages/vuu-ui-controls/src/combo-box/useCombobox.ts +++ b/vuu-ui/packages/vuu-ui-controls/src/combo-box/useCombobox.ts @@ -43,6 +43,7 @@ export interface ComboboxHookProps< | "defaultValue" | "initialHighlightedIndex" | "itemsToString" + | "onDeselect" | "onSetSelectedText" | "value" >, @@ -81,6 +82,7 @@ export const useCombobox = ({ onBlur, onFocus, onChange, + onDeselect, onSelect, id, initialHighlightedIndex = -1, @@ -93,6 +95,7 @@ export const useCombobox = ({ onSelectionChange, onSetSelectedText, selected: selectedProp, + selectionKeys = EnterOnly, selectionStrategy, value: valueProp, InputProps: inputProps = { @@ -291,7 +294,7 @@ export const useCombobox = ({ onKeyboardNavigation: handleKeyboardNavigation, onSelectionChange: handleSelectionChange, selected: collectionHook.itemToCollectionItemId(selectedProp as any), - selectionKeys: EnterOnly, + selectionKeys, selectionStrategy, tabToSelect: !isMultiSelect, }); @@ -328,9 +331,10 @@ export const useCombobox = ({ (e) => { if (e.key === "Backspace" && allowBackspaceClearsSelection) { selectedRef.current = null; + onDeselect?.(); } }, - [allowBackspaceClearsSelection] + [allowBackspaceClearsSelection, onDeselect] ); const { onFocus: inputOnFocus = onFocus } = inputProps; diff --git a/vuu-ui/showcase/src/examples/Filters/FilterBar/FilterBar.examples.tsx b/vuu-ui/showcase/src/examples/Filters/FilterBar/FilterBar.examples.tsx index 546dab3bb3..dec1c74970 100644 --- a/vuu-ui/showcase/src/examples/Filters/FilterBar/FilterBar.examples.tsx +++ b/vuu-ui/showcase/src/examples/Filters/FilterBar/FilterBar.examples.tsx @@ -10,7 +10,9 @@ let displaySequence = 1; export const DefaultFilterBar = ({ filters: filtersProp = [], + onApplyFilter, onFiltersChanged, + style, }: Partial) => { const [filters, setFilters] = useState(filtersProp); const [filterStruct, setFilterStruct] = useState(null); @@ -18,15 +20,23 @@ export const DefaultFilterBar = ({ const tableSchema = getSchema("instruments"); const { typeaheadHook } = vuuModule("SIMUL"); - const handleApplyFilter = useCallback((filter: DataSourceFilter) => { - setFilterStruct(filter.filterStruct ?? null); - }, []); + const handleApplyFilter = useCallback( + (filter: DataSourceFilter) => { + onApplyFilter?.(filter); + setFilterStruct(filter.filterStruct ?? null); + console.log(`handleApplyFilter ${JSON.stringify(filter)}`); + }, + [onApplyFilter] + ); - const handleFiltersChanged = useCallback((filters: Filter[]) => { - onFiltersChanged?.(filters); - console.log("filters changed"); - setFilters(filters); - }, []); + const handleFiltersChanged = useCallback( + (filters: Filter[]) => { + onFiltersChanged?.(filters); + console.log(`filters changed ${JSON.stringify(filters, null, 2)}`); + setFilters(filters); + }, + [onFiltersChanged] + ); const handleChangeActiveFilterIndex = useCallback( (index) => { @@ -40,8 +50,12 @@ export const DefaultFilterBar = ({ }, []); return ( - <> - +
+
{JSON.stringify(filterStruct, null, 2)}
- +
); }; DefaultFilterBar.displaySequence = displaySequence++; diff --git a/vuu-ui/tsconfig.json b/vuu-ui/tsconfig.json index 2c4ae9d290..d990f0f03e 100644 --- a/vuu-ui/tsconfig.json +++ b/vuu-ui/tsconfig.json @@ -25,6 +25,7 @@ "sourceMap": true }, "include": [ + "cypress", "packages/*/src", "sample-apps/*/src", "showcase/src", @@ -33,7 +34,7 @@ "global.d.ts" , "packages/vuu-data-test/src/UpdateGenerator.ts" ], "exclude": [ - "**/*.cy.*", + // "**/*.cy.*", "**/*.test.*" ], "references": [