mirror of
https://github.com/jacobtread/obsidian-timekeep.git
synced 2026-07-22 10:10:27 +00:00
tests: TimesheetNameInput remaining tests
This commit is contained in:
parent
e8afd91512
commit
c2dce2ed18
2 changed files with 88 additions and 18 deletions
|
|
@ -239,6 +239,10 @@ describe("TimesheetNameInput", () => {
|
|||
expect(component.getValue()).toBe("Test");
|
||||
});
|
||||
|
||||
it("should get an empty string attempting to retrieve the input value before load", () => {
|
||||
expect(component.getValue()).toBe("");
|
||||
});
|
||||
|
||||
it("should be able to reset the input value", () => {
|
||||
autocomplete.names.setState(["Test", "Test 1"]);
|
||||
component.load();
|
||||
|
|
@ -256,6 +260,10 @@ describe("TimesheetNameInput", () => {
|
|||
expect(inputEl.value).toBe("");
|
||||
});
|
||||
|
||||
it("resetting the input value before loading should do nothing", () => {
|
||||
component.resetValue();
|
||||
});
|
||||
|
||||
it("down arrow should be able to move the focused suggestion down", () => {
|
||||
autocomplete.names.setState(["Test", "Test 1"]);
|
||||
|
||||
|
|
@ -332,6 +340,24 @@ describe("TimesheetNameInput", () => {
|
|||
expect(onSelectSuggestion).toHaveBeenLastCalledWith("Test");
|
||||
});
|
||||
|
||||
it("enter should do nothing without any suggestions", () => {
|
||||
autocomplete.names.setState(["Test", "Other"]);
|
||||
|
||||
const onKeyDown = vi.spyOn(component, "onKeyDown");
|
||||
const onSelectSuggestion = vi.spyOn(component, "onSelectSuggestion");
|
||||
|
||||
component.load();
|
||||
|
||||
const inputEl = containerEl.querySelector(".timekeep-name")! as HTMLInputElement;
|
||||
expect(inputEl).not.toBeNull();
|
||||
inputEl.dispatchEvent(
|
||||
new KeyboardEvent("keydown", { bubbles: true, cancelable: true, key: "Enter" })
|
||||
);
|
||||
|
||||
expect(onKeyDown).toHaveBeenCalledOnce();
|
||||
expect(onSelectSuggestion).not.toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it("escape should close the suggestions", () => {
|
||||
autocomplete.names.setState(["Test", "Other"]);
|
||||
|
||||
|
|
@ -396,6 +422,34 @@ describe("TimesheetNameInput", () => {
|
|||
expect(setSuggestionsOpen).toHaveBeenLastCalledWith(false);
|
||||
});
|
||||
|
||||
it("clicking inside the input container should not close suggestions", () => {
|
||||
document.body.appendChild(containerEl);
|
||||
|
||||
const component = new TimesheetNameInput(containerEl, autocomplete);
|
||||
|
||||
autocomplete.names.setState(["Test", "Other"]);
|
||||
|
||||
const onClickOutside = vi.spyOn(component, "onClickOutside");
|
||||
const setSuggestionsOpen = vi.spyOn(component, "setSuggestionsOpen");
|
||||
|
||||
component.load();
|
||||
|
||||
const inputEl = containerEl.querySelector(".timekeep-name")! as HTMLInputElement;
|
||||
expect(inputEl).not.toBeNull();
|
||||
|
||||
inputEl.value = "Test";
|
||||
inputEl.focus();
|
||||
|
||||
component.wrapperEl!.dispatchEvent(
|
||||
new MouseEvent("mousedown", { bubbles: true, cancelable: true })
|
||||
);
|
||||
|
||||
expect(onClickOutside).toHaveBeenCalledOnce();
|
||||
expect(setSuggestionsOpen).not.toHaveBeenLastCalledWith(false);
|
||||
|
||||
document.body.removeChild(containerEl);
|
||||
});
|
||||
|
||||
it("touching outside the input container should close suggestions", () => {
|
||||
const component = new TimesheetNameInput(containerEl, autocomplete);
|
||||
|
||||
|
|
@ -434,6 +488,7 @@ describe("TimesheetNameInput", () => {
|
|||
]);
|
||||
|
||||
const onSelectSuggestion = vi.spyOn(component, "onSelectSuggestion");
|
||||
const onClickSuggestions = vi.spyOn(component, "onClickSuggestions");
|
||||
component.load();
|
||||
|
||||
const inputEl = containerEl.querySelector(".timekeep-name")! as HTMLInputElement;
|
||||
|
|
@ -451,6 +506,7 @@ describe("TimesheetNameInput", () => {
|
|||
suggestion.dispatchEvent(new MouseEvent("mousedown", { bubbles: true, cancelable: true }));
|
||||
|
||||
expect(onSelectSuggestion).toHaveBeenCalledWith("Test");
|
||||
expect(onClickSuggestions).toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it("clicking events should be ignored when outside a suggestion", () => {
|
||||
|
|
|
|||
|
|
@ -137,8 +137,13 @@ export class TimesheetNameInput extends DomComponent {
|
|||
*/
|
||||
onClickSuggestions(event: MouseEvent) {
|
||||
const target = event.target;
|
||||
|
||||
/* v8 ignore start -- @preserve */
|
||||
if (!(target instanceof HTMLElement)) return;
|
||||
/* v8 ignore stop -- @preserve */
|
||||
|
||||
if (!target.id.startsWith("timekeepSuggestion-")) return;
|
||||
|
||||
const suggestion = target;
|
||||
const value = suggestion.getAttribute("value");
|
||||
assert(value !== null, "Suggestion value should not be null");
|
||||
|
|
@ -167,13 +172,16 @@ export class TimesheetNameInput extends DomComponent {
|
|||
*
|
||||
* @param event The click / touch event
|
||||
*/
|
||||
|
||||
onClickOutside(event: MouseEvent | TouchEvent) {
|
||||
if (
|
||||
!(event.target instanceof Node) ||
|
||||
(this.wrapperEl && !this.wrapperEl.contains(event.target))
|
||||
) {
|
||||
this.setSuggestionsOpen(false);
|
||||
}
|
||||
assert(
|
||||
this.wrapperEl,
|
||||
"Wrapper element must be defined for the onClickOutside event to fire"
|
||||
);
|
||||
|
||||
if (this.wrapperEl.contains(event.target as Node | null)) return;
|
||||
|
||||
this.setSuggestionsOpen(false);
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -194,7 +202,8 @@ export class TimesheetNameInput extends DomComponent {
|
|||
const suggestionsEl = this.#suggestionsEl;
|
||||
assert(suggestionsEl, "Suggestions element should be defined");
|
||||
|
||||
const suggestionsOpen = !(suggestionsEl.hidden ?? false);
|
||||
const suggestionsHidden = suggestionsEl.hidden;
|
||||
const suggestionsOpen = !suggestionsHidden;
|
||||
|
||||
if (!suggestionsOpen && event.key === "ArrowDown") {
|
||||
this.setSuggestionsOpen(true);
|
||||
|
|
@ -224,14 +233,18 @@ export class TimesheetNameInput extends DomComponent {
|
|||
}
|
||||
|
||||
case "Enter": {
|
||||
event.preventDefault();
|
||||
|
||||
// Clamp focus within suggestion bounds before making decisions
|
||||
this.clampSuggestionFocus();
|
||||
|
||||
const focusIndex = this.#suggestionFocusIndex;
|
||||
if (focusIndex >= 0) {
|
||||
event.preventDefault();
|
||||
const suggestion = this.#suggestions[focusIndex];
|
||||
if (suggestion) {
|
||||
this.onSelectSuggestion(suggestion.item);
|
||||
}
|
||||
}
|
||||
if (focusIndex < 0) return;
|
||||
|
||||
const suggestion = this.#suggestions[focusIndex];
|
||||
assert(suggestion, "Suggestion should always be within defined bounds");
|
||||
|
||||
this.onSelectSuggestion(suggestion.item);
|
||||
break;
|
||||
}
|
||||
|
||||
|
|
@ -337,10 +350,11 @@ export class TimesheetNameInput extends DomComponent {
|
|||
* @param value The suggestion value
|
||||
*/
|
||||
onSelectSuggestion(value: string) {
|
||||
if (this.#inputEl) {
|
||||
this.#inputEl.value = value;
|
||||
}
|
||||
|
||||
assert(
|
||||
this.#inputEl,
|
||||
"Input element should be defined for onSelectSuggestion to be called"
|
||||
);
|
||||
this.#inputEl.value = value;
|
||||
this.setSuggestionsOpen(false);
|
||||
this.setSuggestionFocus(-1);
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in a new issue