Skip to content

Commit

Permalink
enhance VuuTypeaheadInput
Browse files Browse the repository at this point in the history
allow input that does not match suggestions
allow full function in absence of suggestions
remove prop drilling of suggestionPtovider
  • Loading branch information
heswell committed Oct 23, 2024
1 parent decc7da commit ab23559
Show file tree
Hide file tree
Showing 16 changed files with 469 additions and 118 deletions.
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import { useTypeaheadSuggestions } from "@finos/vuu-data-react";
import type { TypeaheadParams } from "@finos/vuu-protocol-types";
import { ExpandoInput, MultiSelectionHandler } from "@finos/vuu-ui-controls";
import { CommitHandler, getVuuTable } from "@finos/vuu-utils";
import { CommitHandler, getVuuTable, NO_DATA_MATCH } from "@finos/vuu-utils";
import { Option } from "@salt-ds/core";
import {
FormEvent,
Expand All @@ -27,8 +27,6 @@ export interface FilterClauseTextValueEditorProps
value: string | string[];
}

const NO_DATA_MATCH = ["No matching data"];

export const FilterClauseValueEditorText = forwardRef(
function FilterClauseTextValueEditor(
{
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
import { useStateRef } from "@finos/vuu-ui-controls";
import { dispatchMouseEvent } from "@finos/vuu-utils";
import { dispatchMouseEvent, useStateRef } from "@finos/vuu-utils";
import { KeyboardEventHandler, useCallback, useRef } from "react";

export const isRowSelectionKey = (key: string) =>
Expand Down
1 change: 1 addition & 0 deletions vuu-ui/packages/vuu-ui-controls/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@
"@finos/vuu-table-types": "0.0.26"
},
"dependencies": {
"@finos/vuu-data-react": "0.0.26",
"@finos/vuu-layout": "0.0.26",
"@finos/vuu-popups": "0.0.26",
"@finos/vuu-table": "0.0.26",
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,177 @@
import {
CurrencyWithTypeaheadAllowFreeText,
CurrencyWithTypeaheadDisallowFreeText,
} from "../../../../../../showcase/src/examples/UiControls/VuuTypeaheadInput.examples";

describe("VuuTypeaheadInput", () => {
describe("Given a TypeaheadInput that shows currency suggestions and allows free text", () => {
describe("Then a matched input pattern will show currency suggestions", () => {
it("first of which which can be selected to commit by pressing Enter", () => {
const onCommit = cy.stub().as("onCommit");
cy.mount(<CurrencyWithTypeaheadAllowFreeText onCommit={onCommit} />);
cy.findByRole("combobox").type("G");
cy.findByRole("listbox").should("be.visible");
cy.findAllByRole("option").should("have.length", 2);
cy.findAllByRole("option")
.eq(0)
.should("have.class", "saltOption-active");
cy.findAllByRole("option")
.eq(0)
.should("have.class", "saltOption-focusVisible");
cy.realPress("Enter");
cy.get("@onCommit").should(
"be.calledWithMatch",
{ type: "keydown" },
"GBP",
);
cy.findByRole("listbox").should("not.exist");
});
it("any of which which can be selected (and committed) by clicking", () => {
const onCommit = cy.stub().as("onCommit");
cy.mount(<CurrencyWithTypeaheadAllowFreeText onCommit={onCommit} />);
cy.findByRole("combobox").type("G");
cy.findByRole("listbox").should("be.visible");
cy.findAllByRole("option").should("have.length", 2);
cy.findAllByRole("option").eq(1).click();
cy.get("@onCommit").should(
"be.calledWithMatch",
{ type: "click" },
"GBX",
);
cy.findByRole("listbox").should("not.exist");
});
it("which can be navigated with Arrow key", () => {
const onCommit = cy.stub().as("onCommit");
cy.mount(<CurrencyWithTypeaheadAllowFreeText onCommit={onCommit} />);
cy.findByRole("combobox").type("G");
cy.findByRole("listbox").should("be.visible");
cy.findAllByRole("option").should("have.length", 2);
cy.findAllByRole("option")
.eq(0)
.should("have.class", "saltOption-active");
cy.findAllByRole("option")
.eq(0)
.should("have.class", "saltOption-focusVisible");
cy.realPress("ArrowDown");
cy.realPress("Enter");
cy.get("@onCommit").should(
"be.calledWithMatch",
{ type: "keydown" },
"GBX",
);
cy.findByRole("listbox").should("not.exist");
});
it("a complete match will always show one suggestion, Enter commits", () => {
const onCommit = cy.stub().as("onCommit");
cy.mount(<CurrencyWithTypeaheadAllowFreeText onCommit={onCommit} />);
cy.findByRole("combobox").type("GBP");
cy.findByRole("listbox").should("be.visible");
cy.findAllByRole("option").should("have.length", 1);
cy.findAllByRole("option")
.eq(0)
.should("have.class", "saltOption-active");
cy.findAllByRole("option")
.eq(0)
.should("have.class", "saltOption-focusVisible");
cy.realPress("Enter");
cy.get("@onCommit").should(
"be.calledWithMatch",
{ type: "keydown" },
"GBP",
);
cy.findByRole("listbox").should("not.exist");
});
});

describe("Then a non-matched input pattern will show no suggestions", () => {
it("and any text can be committed", () => {
const onCommit = cy.stub().as("onCommit");
cy.mount(<CurrencyWithTypeaheadAllowFreeText onCommit={onCommit} />);
cy.findByRole("combobox").type("abc");

cy.findAllByRole("option").should("have.length", 1);
cy.findAllByRole("option")
.eq(0)
.should("have.attr", "aria-disabled", "true");
cy.findAllByRole("option")
.eq(0)
.should("have.text", "No matching data");

cy.realPress("Enter");
cy.get("@onCommit").should(
"be.calledWithMatch",
{ type: "keydown" },
"abc",
);
});
it("then clearing previously committed text will automatically commit", () => {
const onCommit = cy.stub().as("onCommit");
cy.mount(<CurrencyWithTypeaheadAllowFreeText onCommit={onCommit} />);
cy.findByRole("combobox").type("abc");
cy.realPress("Enter");
cy.get("@onCommit").should(
"be.calledWithMatch",
{ type: "keydown" },
"abc",
);
cy.realPress("Backspace");
cy.realPress("Backspace");
cy.realPress("Backspace");

cy.get("@onCommit").should(
"be.calledWithMatch",
{ type: "keydown" },
"",
);
});
});
});

describe("Given a TypeaheadInput that shows currency suggestions and DISALLOWS free text", () => {
it("Then a non-matched input pattern will show no suggestions", () => {
const onCommit = cy.stub().as("onCommit");
cy.mount(<CurrencyWithTypeaheadAllowFreeText onCommit={onCommit} />);
cy.findByRole("combobox").type("abc");

cy.findAllByRole("option").should("have.length", 1);
cy.findAllByRole("option")
.eq(0)
.should("have.attr", "aria-disabled", "true");
cy.findAllByRole("option").eq(0).should("have.text", "No matching data");

cy.realPress("Enter");
cy.get("@onCommit").should(
"be.calledWithMatch",
{ type: "keydown" },
"abc",
);
});
it("Then commit will not be allowed when input text matches no suggestions", () => {
const onCommit = cy.stub().as("onCommit");
cy.mount(<CurrencyWithTypeaheadDisallowFreeText onCommit={onCommit} />);
cy.findByRole("combobox").type("abc");
cy.realPress("Enter");
cy.get("@onCommit").should("not.be.called");
});

it("Then warning will be shown if commit attempted on non matching text", () => {
const onCommit = cy.stub().as("onCommit");
cy.mount(<CurrencyWithTypeaheadDisallowFreeText onCommit={onCommit} />);
cy.findByRole("combobox").type("abc");
cy.realPress("Enter");

cy.findAllByRole("option").should("have.length", 1);
cy.findAllByRole("option")
.eq(0)
.should("have.attr", "aria-disabled", "true");
cy.wait(200);
cy.findAllByRole("option")
.eq(0)
.invoke("text")
.should(
"contain",
"Please select a value from the list of suggestions",
);
});
});
});
1 change: 0 additions & 1 deletion vuu-ui/packages/vuu-ui-controls/src/common-hooks/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,4 +6,3 @@ export * from "./useControlled";
export * from "./use-resize-observer";
export * from "./navigationTypes";
export * from "./selectionTypes";
export * from "./useStateRef";
Original file line number Diff line number Diff line change
Expand Up @@ -33,3 +33,7 @@
width: var(--vuu-icon-size);
}
}

.vuuTypeaheadOption[aria-disabled="true"] {
color: var(--salt-content-secondary-foreground);
}
Original file line number Diff line number Diff line change
@@ -1,8 +1,5 @@
import type {
SuggestionProvider,
TableSchemaTable,
} from "@finos/vuu-data-types";
import type { CommitHandler } from "@finos/vuu-utils";
import type { TableSchemaTable } from "@finos/vuu-data-types";
import { NO_DATA_MATCH, type CommitHandler } from "@finos/vuu-utils";
import { ComboBox, Option } from "@salt-ds/core";
import { useComponentCssInjection } from "@salt-ds/styles";
import { useWindow } from "@salt-ds/window";
Expand All @@ -11,16 +8,29 @@ import { useVuuTypeaheadInput } from "./useVuuTypeaheadInput";
import vuuTypeaheadInputCss from "./VuuTypeaheadInput.css";

const classBase = "vuuTypeaheadInput";
const [noMatchingData] = NO_DATA_MATCH;

export interface VuuTypeaheadInputProps {
/**
* Allows a text string to be submitted that does not match any suggestion
* Defaults to true
*/
allowFreeInput?: boolean;
column: string;
/**
* A warning to display to the user if allowFreeText is false and they attempt
* to commit text which does not match any suggestions. A default message will
* be shown if not provided
*/
freeTextWarning?: string;
onCommit: CommitHandler<HTMLInputElement>;
suggestionProvider?: SuggestionProvider;
table: TableSchemaTable;
}

export const VuuTypeaheadInput = ({
allowFreeInput,
column,
freeTextWarning,
onCommit,
table,
}: VuuTypeaheadInputProps) => {
Expand All @@ -32,31 +42,43 @@ export const VuuTypeaheadInput = ({
});
const {
inputProps,
noFreeText,
onChange,
onKeyDown,
onOpenChange,
onSelectionChange,
open,
ref,
typeaheadValues,
value,
} = useVuuTypeaheadInput({
allowFreeInput,
column,
freeTextWarning,
onCommit,
table,
});

console.log(`render with values ${typeaheadValues.join(",")}`);
return (
<ComboBox
className={classBase}
inputProps={inputProps}
onChange={onChange}
onKeyDown={onKeyDown}
onOpenChange={onOpenChange}
onSelectionChange={onSelectionChange}
open={open}
ref={ref}
value={value}
>
{typeaheadValues.map((state) => (
<Option value={state} key={state} />
<Option
className="vuuTypeaheadOption"
value={state}
key={state}
disabled={state === noMatchingData || state === noFreeText}
/>
))}
</ComboBox>
);
Expand Down
Loading

0 comments on commit ab23559

Please sign in to comment.