diff --git a/vuu-ui/packages/vuu-filters/src/filter-clause/value-editors/FilterClauseValueEditorText.tsx b/vuu-ui/packages/vuu-filters/src/filter-clause/value-editors/FilterClauseValueEditorText.tsx
index 7b8a88411..85fd033f6 100644
--- a/vuu-ui/packages/vuu-filters/src/filter-clause/value-editors/FilterClauseValueEditorText.tsx
+++ b/vuu-ui/packages/vuu-filters/src/filter-clause/value-editors/FilterClauseValueEditorText.tsx
@@ -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,
@@ -27,8 +27,6 @@ export interface FilterClauseTextValueEditorProps
value: string | string[];
}
-const NO_DATA_MATCH = ["No matching data"];
-
export const FilterClauseValueEditorText = forwardRef(
function FilterClauseTextValueEditor(
{
diff --git a/vuu-ui/packages/vuu-table/src/useControlledTableNavigation.ts b/vuu-ui/packages/vuu-table/src/useControlledTableNavigation.ts
index 929fc8124..a4358e2bf 100644
--- a/vuu-ui/packages/vuu-table/src/useControlledTableNavigation.ts
+++ b/vuu-ui/packages/vuu-table/src/useControlledTableNavigation.ts
@@ -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) =>
diff --git a/vuu-ui/packages/vuu-ui-controls/package.json b/vuu-ui/packages/vuu-ui-controls/package.json
index ac29a6142..2c2bc79bf 100644
--- a/vuu-ui/packages/vuu-ui-controls/package.json
+++ b/vuu-ui/packages/vuu-ui-controls/package.json
@@ -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",
diff --git a/vuu-ui/packages/vuu-ui-controls/src/__tests__/__component__/vuu-typeahead-input/VuuTypeaheadInput.cy.tsx b/vuu-ui/packages/vuu-ui-controls/src/__tests__/__component__/vuu-typeahead-input/VuuTypeaheadInput.cy.tsx
new file mode 100644
index 000000000..49a4fd5c0
--- /dev/null
+++ b/vuu-ui/packages/vuu-ui-controls/src/__tests__/__component__/vuu-typeahead-input/VuuTypeaheadInput.cy.tsx
@@ -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( );
+ 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( );
+ 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( );
+ 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( );
+ 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( );
+ 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( );
+ 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( );
+ 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( );
+ 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( );
+ 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",
+ );
+ });
+ });
+});
diff --git a/vuu-ui/packages/vuu-ui-controls/src/common-hooks/index.ts b/vuu-ui/packages/vuu-ui-controls/src/common-hooks/index.ts
index 4c54dfdc4..ae3748717 100644
--- a/vuu-ui/packages/vuu-ui-controls/src/common-hooks/index.ts
+++ b/vuu-ui/packages/vuu-ui-controls/src/common-hooks/index.ts
@@ -6,4 +6,3 @@ export * from "./useControlled";
export * from "./use-resize-observer";
export * from "./navigationTypes";
export * from "./selectionTypes";
-export * from "./useStateRef";
diff --git a/vuu-ui/packages/vuu-ui-controls/src/vuu-typeahead-input/VuuTypeaheadInput.css b/vuu-ui/packages/vuu-ui-controls/src/vuu-typeahead-input/VuuTypeaheadInput.css
index 1b4fb833d..50a3ce380 100644
--- a/vuu-ui/packages/vuu-ui-controls/src/vuu-typeahead-input/VuuTypeaheadInput.css
+++ b/vuu-ui/packages/vuu-ui-controls/src/vuu-typeahead-input/VuuTypeaheadInput.css
@@ -33,3 +33,7 @@
width: var(--vuu-icon-size);
}
}
+
+.vuuTypeaheadOption[aria-disabled="true"] {
+ color: var(--salt-content-secondary-foreground);
+}
diff --git a/vuu-ui/packages/vuu-ui-controls/src/vuu-typeahead-input/VuuTypeaheadInput.tsx b/vuu-ui/packages/vuu-ui-controls/src/vuu-typeahead-input/VuuTypeaheadInput.tsx
index 4df98e4b0..f1f989054 100644
--- a/vuu-ui/packages/vuu-ui-controls/src/vuu-typeahead-input/VuuTypeaheadInput.tsx
+++ b/vuu-ui/packages/vuu-ui-controls/src/vuu-typeahead-input/VuuTypeaheadInput.tsx
@@ -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";
@@ -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;
- suggestionProvider?: SuggestionProvider;
table: TableSchemaTable;
}
export const VuuTypeaheadInput = ({
+ allowFreeInput,
column,
+ freeTextWarning,
onCommit,
table,
}: VuuTypeaheadInputProps) => {
@@ -32,7 +42,9 @@ export const VuuTypeaheadInput = ({
});
const {
inputProps,
+ noFreeText,
onChange,
+ onKeyDown,
onOpenChange,
onSelectionChange,
open,
@@ -40,15 +52,20 @@ export const VuuTypeaheadInput = ({
typeaheadValues,
value,
} = useVuuTypeaheadInput({
+ allowFreeInput,
column,
+ freeTextWarning,
onCommit,
table,
});
+
+ console.log(`render with values ${typeaheadValues.join(",")}`);
return (
{typeaheadValues.map((state) => (
-
+
))}
);
diff --git a/vuu-ui/packages/vuu-ui-controls/src/vuu-typeahead-input/useVuuTypeaheadInput.ts b/vuu-ui/packages/vuu-ui-controls/src/vuu-typeahead-input/useVuuTypeaheadInput.ts
index 2f494bd9e..940550f7a 100644
--- a/vuu-ui/packages/vuu-ui-controls/src/vuu-typeahead-input/useVuuTypeaheadInput.ts
+++ b/vuu-ui/packages/vuu-ui-controls/src/vuu-typeahead-input/useVuuTypeaheadInput.ts
@@ -1,10 +1,17 @@
import { useTypeaheadSuggestions } from "@finos/vuu-data-react";
import type { TypeaheadParams } from "@finos/vuu-protocol-types";
-import { dispatchKeyboardEvent, getVuuTable } from "@finos/vuu-utils";
+import {
+ dispatchKeyboardEvent,
+ getVuuTable,
+ useStateRef,
+ NO_DATA_MATCH,
+} from "@finos/vuu-utils";
import {
ComponentPropsWithoutRef,
+ KeyboardEventHandler,
useCallback,
useEffect,
+ useMemo,
useRef,
useState,
type ChangeEventHandler,
@@ -13,26 +20,54 @@ import {
} from "react";
import type { VuuTypeaheadInputProps } from "./VuuTypeaheadInput";
-const NO_DATA_MATCH = ["No matching data"];
-
export type VuuTypeaheadInputHookProps = Pick<
VuuTypeaheadInputProps,
- "column" | "onCommit" | "table"
+ "allowFreeInput" | "column" | "freeTextWarning" | "onCommit" | "table"
>;
+const defaultFreeTextWarning =
+ "Please select a value from the list of suggestions. If no suggestions match your text, then the value is not valid. If you believe this should be a valid value, please reach out to the support team";
+
export const useVuuTypeaheadInput = ({
+ allowFreeInput = true,
column,
+ freeTextWarning,
onCommit,
table,
}: VuuTypeaheadInputHookProps) => {
- const [value, setValue] = useState("");
+ const NO_FREE_TEXT = useMemo(
+ () => [freeTextWarning ?? defaultFreeTextWarning],
+ [freeTextWarning],
+ );
+ const [valueRef, setValue] = useStateRef("");
const [open, setOpen] = useState(false);
+ const inputRef = useRef(null);
const rootRef = useRef(null);
const [typeaheadValues, setTypeaheadValues] = useState([]);
const getSuggestions = useTypeaheadSuggestions();
+ const pendingListFocusRef = useRef(false);
+
+ const { current: value } = valueRef;
+
+ const handleKeyDown = useCallback>(
+ (evt) => {
+ const { current: value } = valueRef;
+ if (evt.key === "Enter" && value !== "") {
+ if (allowFreeInput) {
+ onCommit?.(evt, value, "text-input");
+ setOpen(false);
+ } else {
+ setTypeaheadValues(NO_FREE_TEXT);
+ }
+ }
+ },
+ [NO_FREE_TEXT, allowFreeInput, onCommit, valueRef],
+ );
const callbackRef = useCallback>((el) => {
rootRef.current = el;
+ const input = el?.querySelector("input") ?? null;
+ inputRef.current = input;
}, []);
useEffect(() => {
@@ -48,10 +83,20 @@ export const useVuuTypeaheadInput = ({
// TODO is this right
setTypeaheadValues([]);
} else if (suggestions.length === 0 && value) {
- setTypeaheadValues(NO_DATA_MATCH);
+ setTypeaheadValues((values) =>
+ // Do not update if we have already set suggestions to the no free text warning
+ values === NO_FREE_TEXT ? NO_FREE_TEXT : NO_DATA_MATCH,
+ );
} else {
setTypeaheadValues(suggestions);
+ if (pendingListFocusRef.current && inputRef.current) {
+ // This is a workaround for the fact that ComboBox does not automatically
+ // highlight first list item when items have been populated dynamically.
+ // This has been raised as a bug.
+ dispatchKeyboardEvent(inputRef.current, "keydown", "ArrowUp");
+ }
}
+ pendingListFocusRef.current = false;
})
.catch((err) => {
console.error("Error getting suggestions", err);
@@ -60,27 +105,21 @@ export const useVuuTypeaheadInput = ({
setTypeaheadValues([]);
}
}
- }, [table, column, getSuggestions, value]);
+ }, [table, column, getSuggestions, value, NO_FREE_TEXT]);
const handleChange: ChangeEventHandler = (evt) => {
const { value: newValue } = evt.target;
-
+ const { current: value } = valueRef;
if (value === "" && newValue) {
setOpen(true);
const input = rootRef.current?.querySelector("input");
if (input) {
- // This is a workaround for the fact that ComboBox does not automatically
- // highlight first list item when items have been populated dynamically.
- // This has been raised as a bug.
- setTimeout(() => {
- dispatchKeyboardEvent(input, "keydown", "ArrowUp");
- }, 150);
+ pendingListFocusRef.current = true;
}
} else if (newValue === "" && value) {
// treat clear value as a commit event
onCommit(evt, "");
}
-
setValue(newValue);
};
@@ -88,7 +127,6 @@ export const useVuuTypeaheadInput = ({
evt: SyntheticEvent,
[newSelected]: string[],
) => {
- console.log(`useVuuTypeahead handleSelectionChange ${newSelected}`);
setValue(newSelected);
onCommit(
evt as SyntheticEvent,
@@ -98,7 +136,7 @@ export const useVuuTypeaheadInput = ({
};
const handleOpenChange = (newOpen: boolean) => {
- if (newOpen && value === "") {
+ if (newOpen && valueRef.current === "") {
// ignore this, don't open dropdown unless user has typed at least one character
} else {
setOpen(newOpen);
@@ -109,14 +147,17 @@ export const useVuuTypeaheadInput = ({
autoComplete: "off",
};
+ const [noFreeText] = NO_FREE_TEXT;
return {
inputProps,
+ noFreeText,
onChange: handleChange,
+ onKeyDown: handleKeyDown,
onOpenChange: handleOpenChange,
onSelectionChange: handleSelectionChange,
open,
ref: callbackRef,
typeaheadValues,
- value,
+ value: valueRef.current,
};
};
diff --git a/vuu-ui/packages/vuu-utils/src/form-utils.ts b/vuu-ui/packages/vuu-utils/src/form-utils.ts
index 01cccf862..466789712 100644
--- a/vuu-ui/packages/vuu-utils/src/form-utils.ts
+++ b/vuu-ui/packages/vuu-utils/src/form-utils.ts
@@ -1,5 +1,5 @@
import { VuuRowDataItemType } from "@finos/vuu-protocol-types";
-import { SyntheticEvent } from "react";
+import { KeyboardEvent, SyntheticEvent } from "react";
import { queryClosest } from "./html-utils";
/**
@@ -22,4 +22,8 @@ export type InputSource = "typeahead-suggestion" | "text-input";
export type CommitHandler<
E extends HTMLElement = HTMLInputElement,
T extends VuuRowDataItemType | undefined = string,
-> = (evt: SyntheticEvent, value: T, source?: InputSource) => void;
+> = (
+ evt: SyntheticEvent | KeyboardEvent,
+ value: T,
+ source?: InputSource,
+) => void;
diff --git a/vuu-ui/packages/vuu-utils/src/index.ts b/vuu-ui/packages/vuu-utils/src/index.ts
index 649eff250..6eb827803 100644
--- a/vuu-ui/packages/vuu-utils/src/index.ts
+++ b/vuu-ui/packages/vuu-utils/src/index.ts
@@ -48,10 +48,12 @@ export * from "./shell-layout-types";
export * from "./sort-utils";
export * from "./table-schema-utils";
export * from "./text-utils";
+export * from "./typeahead-utils";
export * from "./ThemeProvider";
export * from "./ts-utils";
export * from "./url-utils";
export * from "./useId";
+export * from "./useStateRef";
export * from "./user-types";
export * from "./useLayoutEffectSkipFirst";
diff --git a/vuu-ui/packages/vuu-utils/src/typeahead-utils.ts b/vuu-ui/packages/vuu-utils/src/typeahead-utils.ts
new file mode 100644
index 000000000..9776b7c92
--- /dev/null
+++ b/vuu-ui/packages/vuu-utils/src/typeahead-utils.ts
@@ -0,0 +1 @@
+export const NO_DATA_MATCH = ["No matching data"];
diff --git a/vuu-ui/packages/vuu-ui-controls/src/common-hooks/useStateRef.ts b/vuu-ui/packages/vuu-utils/src/useStateRef.ts
similarity index 73%
rename from vuu-ui/packages/vuu-ui-controls/src/common-hooks/useStateRef.ts
rename to vuu-ui/packages/vuu-utils/src/useStateRef.ts
index e28c8659e..c87ff3206 100644
--- a/vuu-ui/packages/vuu-ui-controls/src/common-hooks/useStateRef.ts
+++ b/vuu-ui/packages/vuu-utils/src/useStateRef.ts
@@ -7,12 +7,21 @@ import {
useState,
} from "react";
+/**
+ * Extension to useState that maintains a ref for the
+ * current state value. Useful where use of the ref can
+ * avoid referencing the state vale in a dependency array
+ * eg on a native event handler.
+ *
+ * @param value the initial value to store
+ */
+
const isSimpleStateValue = (arg: SetStateAction): arg is T =>
typeof arg !== "function";
// Keeps a ref value in sync with a state value
export const useStateRef = (
- initialValue: T
+ initialValue: T,
): [MutableRefObject, Dispatch>] => {
const [value, _setValue] = useState(initialValue);
const ref = useRef(value);
diff --git a/vuu-ui/showcase/src/examples/Filters/FilterClause/FilterClause.examples.tsx b/vuu-ui/showcase/src/examples/Filters/FilterClause/FilterClause.examples.tsx
index 16b4bc36d..553e1b723 100644
--- a/vuu-ui/showcase/src/examples/Filters/FilterClause/FilterClause.examples.tsx
+++ b/vuu-ui/showcase/src/examples/Filters/FilterClause/FilterClause.examples.tsx
@@ -1,63 +1,49 @@
-import { getSchema, vuuModule } from "@finos/vuu-data-test";
+import { LocalDataSourceProvider, getSchema } from "@finos/vuu-data-test";
import { SchemaColumn, TableSchema } from "@finos/vuu-data-types";
import { FilterClauseModel, FilterClause } from "@finos/vuu-filters";
import { useMemo } from "react";
-import { useAutoLoginToVuuServer } from "../../utils";
import "./FilterClause.examples.css";
import { ColumnDescriptorsByName } from "@finos/vuu-filter-types";
let displaySequence = 1;
-export const NewFilterClause = () => {
- const tableSchema = getSchema("instruments");
- const { typeaheadHook } = vuuModule("SIMUL");
-
- const filterClauseModel = useMemo(() => new FilterClauseModel({}), []);
-
+const FilterClauseTemplate = ({
+ filterClauseModel = new FilterClauseModel({}),
+ tableSchema = getSchema("instruments"),
+ columnsByName = columnDescriptorsByName(tableSchema.columns),
+}: {
+ columnsByName?: ColumnDescriptorsByName;
+ filterClauseModel?: FilterClauseModel;
+ tableSchema?: TableSchema;
+}) => {
return (
);
};
-NewFilterClause.displaySequence = displaySequence++;
-
-export const NewFilterClauseNoCompletions = () => {
- const tableSchema = getSchema("instruments");
-
- const filterClauseModel = useMemo(() => new FilterClauseModel({}), []);
-
- const alwaysEmptyTypeaheadHook = useMemo(() => {
- const suggestionFetcher = async () => {
- return [];
- };
-
- return () => suggestionFetcher;
- }, []);
+export const NewFilterClause = () => {
return (
-
-
-
+
+
+
);
};
+NewFilterClause.displaySequence = displaySequence++;
+
+export const NewFilterClauseNoCompletions = () => {
+ return ;
+};
NewFilterClauseNoCompletions.displaySequence = displaySequence++;
export const PartialFilterClauseColumnOnly = () => {
- useAutoLoginToVuuServer();
- const tableSchema = getSchema("instruments");
const filterClauseModel = useMemo(
() =>
new FilterClauseModel({
@@ -65,22 +51,15 @@ export const PartialFilterClauseColumnOnly = () => {
}),
[],
);
-
return (
-
-
-
+
+
+
);
};
PartialFilterClauseColumnOnly.displaySequence = displaySequence++;
export const PartialFilterClauseColumnAndOperator = () => {
- const { typeaheadHook } = vuuModule("SIMUL");
- const tableSchema = getSchema("instruments");
const filterClauseModel = useMemo(
() =>
new FilterClauseModel({
@@ -89,25 +68,15 @@ export const PartialFilterClauseColumnAndOperator = () => {
}),
[],
);
-
return (
-
-
-
+
+
+
);
};
PartialFilterClauseColumnAndOperator.displaySequence = displaySequence++;
export const CompleteFilterClauseTextEquals = () => {
- const { typeaheadHook } = vuuModule("SIMUL");
- const tableSchema = getSchema("instruments");
-
const filterClauseModel = useMemo(
() =>
new FilterClauseModel({
@@ -119,21 +88,14 @@ export const CompleteFilterClauseTextEquals = () => {
);
return (
-
-
-
+
+
+
);
};
CompleteFilterClauseTextEquals.displaySequence = displaySequence++;
export const PartialFilterClauseDateColumnOnly = () => {
- const { typeaheadHook } = vuuModule("SIMUL");
-
const tableColumns: SchemaColumn[] = [
{
name: "tradeDate",
@@ -173,14 +135,13 @@ export const PartialFilterClauseDateColumnOnly = () => {
}, []);
return (
-
-
+
-
+
);
};
PartialFilterClauseDateColumnOnly.displaySequence = displaySequence++;
diff --git a/vuu-ui/showcase/src/examples/Filters/FilterEditor/FilterEditor.examples.tsx b/vuu-ui/showcase/src/examples/Filters/FilterEditor/FilterEditor.examples.tsx
index 217ef9edc..6984d73e6 100644
--- a/vuu-ui/showcase/src/examples/Filters/FilterEditor/FilterEditor.examples.tsx
+++ b/vuu-ui/showcase/src/examples/Filters/FilterEditor/FilterEditor.examples.tsx
@@ -1,4 +1,4 @@
-import { getSchema, vuuModule } from "@finos/vuu-data-test";
+import { LocalDataSourceProvider, getSchema } from "@finos/vuu-data-test";
import type { SchemaColumn, TableSchema } from "@finos/vuu-data-types";
import type { Filter } from "@finos/vuu-filter-types";
import {
@@ -18,8 +18,6 @@ const FilterEditorTemplate = ({
columnDescriptors = tableSchema.columns,
...props
}: Partial) => {
- const { typeaheadHook } = vuuModule("SIMUL");
-
const onCancel = useCallback(() => {
console.log(`cancel filter edit`);
}, []);
@@ -47,14 +45,15 @@ const FilterEditorTemplate = ({
onCancel={onCancel}
onSave={onSave}
style={style}
- suggestionProvider={typeaheadHook}
tableSchema={tableSchema}
/>
);
};
export const NewFilter = (props: Partial) => (
-
+
+
+
);
NewFilter.displaySequence = displaySequence++;
@@ -86,11 +85,13 @@ export const NewFilterDateColumns = (props: Partial) => {
}, []);
return (
-
+
+
+
);
};
@@ -105,7 +106,11 @@ export const EditSimplerFilter = (props: Partial) => {
};
}, []);
- return ;
+ return (
+
+
+
+ );
};
EditSimplerFilter.displaySequence = displaySequence++;
@@ -128,7 +133,11 @@ export const EditMultiClauseAndFilter = (props: Partial) => {
};
}, []);
- return ;
+ return (
+
+
+
+ );
};
EditMultiClauseAndFilter.displaySequence = displaySequence++;
@@ -151,6 +160,10 @@ export const EditMultiClauseOrFilter = (props: Partial) => {
};
}, []);
- return ;
+ return (
+
+
+
+ );
};
EditMultiClauseOrFilter.displaySequence = displaySequence++;
diff --git a/vuu-ui/showcase/src/examples/UiControls/VuuTypeaheadInput.examples.tsx b/vuu-ui/showcase/src/examples/UiControls/VuuTypeaheadInput.examples.tsx
new file mode 100644
index 000000000..468375b85
--- /dev/null
+++ b/vuu-ui/showcase/src/examples/UiControls/VuuTypeaheadInput.examples.tsx
@@ -0,0 +1,119 @@
+import { LocalDataSourceProvider } from "@finos/vuu-data-test";
+import { TableSchemaTable } from "@finos/vuu-data-types";
+import { ColumnDescriptor } from "@finos/vuu-table-types";
+import { VuuTypeaheadInput } from "@finos/vuu-ui-controls";
+import { CommitHandler } from "@finos/vuu-utils";
+import { CSSProperties } from "react";
+
+let displaySequence = 1;
+
+const TypeaheadInputTemplate = ({
+ allowFreeInput,
+ column = { name: "currency", serverDataType: "string" },
+ onCommit,
+ table = { module: "SIMUL", table: "instrumentsExtended" },
+}: {
+ allowFreeInput?: boolean;
+ column?: ColumnDescriptor;
+ onCommit?: CommitHandler;
+ style?: CSSProperties;
+ table?: TableSchemaTable;
+}) => {
+ const handleCommit: CommitHandler = (evt, value) => {
+ console.log(`commit ${value}`);
+ onCommit?.(evt, value);
+ };
+
+ return (
+
+ );
+};
+
+export const CurrencyWithTypeaheadAllowFreeText = ({
+ onCommit,
+}: {
+ onCommit?: CommitHandler;
+}) => {
+ return (
+
+
+
+
+
+ );
+};
+CurrencyWithTypeaheadAllowFreeText.displaySequence = displaySequence++;
+
+export const CurrencyWithTypeaheadDisallowFreeText = ({
+ onCommit,
+}: {
+ onCommit?: CommitHandler;
+}) => {
+ return (
+
+
+
+
+
+ );
+};
+CurrencyWithTypeaheadDisallowFreeText.displaySequence = displaySequence++;
+
+export const CurrencyNoTypeaheadAllowFreeText = () => {
+ return (
+
+
+
+ );
+};
+CurrencyNoTypeaheadAllowFreeText.displaySequence = displaySequence++;
+
+export const CurrencyNoTypeaheadDisallowFreeText = () => {
+ return (
+
+
+
+ );
+};
+CurrencyNoTypeaheadDisallowFreeText.displaySequence = displaySequence++;
diff --git a/vuu-ui/showcase/src/examples/UiControls/index.ts b/vuu-ui/showcase/src/examples/UiControls/index.ts
index a78681e54..ec610d6cf 100644
--- a/vuu-ui/showcase/src/examples/UiControls/index.ts
+++ b/vuu-ui/showcase/src/examples/UiControls/index.ts
@@ -10,3 +10,4 @@ export * as Tree from "./Tree.examples";
export * as SplitButton from "./SplitButton.examples";
export * as Tabstrip from "./Tabstrip.examples";
export * as VuuInput from "./VuuInput.examples";
+export * as VuuTypeaheadInput from "./VuuTypeaheadInput.examples";