Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
17 commits
Select commit Hold shift + click to select a range
69e2dd7
feat(searchbar): add number inputs
MatsJohansen87 Sep 23, 2025
955fcd1
feat(searchbar): add date inputs
MatsJohansen87 Sep 23, 2025
10d97ab
feat(searchbar): add string inputs
MatsJohansen87 Sep 23, 2025
3ee3fdb
chore(input components): remove unnessesary check
MatsJohansen87 Sep 23, 2025
256215a
fix(searchbar focus): clicking and focusing in and out now works as e…
MatsJohansen87 Sep 24, 2025
f5d4a0c
fix(search bar options): remove pointer from inputs to avoid confusio…
MatsJohansen87 Sep 24, 2025
b44663d
fix(searchbar focus): handle long pressed tab correctly, when bar loo…
MatsJohansen87 Sep 24, 2025
c009195
feat(search bars): add deleting inputs as default option when search …
MatsJohansen87 Sep 25, 2025
483e55a
feat(search bar): add more style options and update book
MatsJohansen87 Sep 26, 2025
8b9d9dd
Merge branch 'develop' into feautre/add-input-fields-to-searchbar
MatsJohansen87 Sep 26, 2025
b946394
refactor(searchbar): add less hacky solution for searchbar behaviour
MatsJohansen87 Oct 2, 2025
6616f39
fix(searchbar regex): prevent crash from unescaped characters
MatsJohansen87 Oct 2, 2025
81cb154
fix(searchbar): start with 2 letters to get blood type 'a+' etc
MatsJohansen87 Oct 2, 2025
5f3bf71
fix(searchbar): clear bars on search and remove export
MatsJohansen87 Oct 2, 2025
2d26683
chore(query types): restrict autocomplete field type to criterion
MatsJohansen87 Oct 2, 2025
fbae9b5
fix(searchbar): handle click inside properly
MatsJohansen87 Oct 6, 2025
fa46fb9
fix(searchbar delete button): remove input on click when there is onl…
MatsJohansen87 Oct 6, 2025
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
48 changes: 25 additions & 23 deletions book/src/components/searchbar.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
# Search Bar

The `lens-searchbar` component offers an interface for exploring of all single-select items. It serves as the primary interface for users to search, apply, and adjust query criteria. Selected items appear as interactive chips within the component, giving users a clear visual of their active filters. Users can easily refine their search by removing individual values or entire criteria directly from the chip display.
The `lens-searchbar` component offers an interface for exploring of all catalogue items. It serves as the primary interface for users to search, apply, and adjust query criteria. Selected items appear as interactive chips within the component, giving users a clear visual of their active filters. Users can easily refine their search by removing individual values or entire criteria directly from the chip display.

---

Expand Down Expand Up @@ -41,18 +41,6 @@ The `lens-searchbar` component offers an interface for exploring of all single-s

---

## Events

### `on:clear-search`

Triggered when the group clear button is clicked.

```svelte
<lens-search-bar on:clear-search={handleClear} />
```

---

## Example

```html
Expand All @@ -66,13 +54,27 @@ Triggered when the group clear button is clicked.

## Styling

| Part | Purpose |
| ------------------------------------------------------ | -------------------------------- |
| `lens-searchbar` | Wrapper for the entire component |
| `lens-searchbar-input` | Main input element |
| `lens-searchbar-chip`, `chip-name`, `chip-item` | Visual query chips |
| `lens-searchbar-autocomplete-options` | Autocomplete container |
| `lens-searchbar-autocomplete-options-item` | Individual result |
| `lens-searchbar-autocomplete-options-item-focused` | Highlighted result |
| `lens-searchbar-autocomplete-options-item-description` | Optional description |
| `lens-searchbar-autocomplete-options-item-facet-count` | Facet count badge |
| Part | Purpose |
| -------------------------------------------------------------- | ------------------------------------ |
| `lens-searchbar` | Wrapper for the entire component |
| `lens-searchbar-active` | Active searchbar |
| `lens-searchbar-chips` | Visual query chip wrapper |
| `lens-searchbar-chip` | Query chip |
| `lens-searchbar-chip-name` | Query chip criterion name |
| `lens-searchbar-chip-item` | Query chip values wrapper |
| `lens-searchbar-chip-item-text` | Query chip singular value |
| `lens-searchbar-input` | Main input element |
| `lens-searchbar-input-options-open` | Main input when autocomplete is open |
| `lens-searchbar-autocomplete-options` | Autocomplete container |
| `lens-searchbar-autocomplete-options-heading` | Autocomplete group heading |
| `lens-searchbar-autocomplete-options-item` | Individual result |
| `lens-searchbar-autocomplete-options-item-focused` | Highlighted result |
| `lens-searchbar-autocomplete-options-item-criterion` | Single select result |
| `lens-searchbar-autocomplete-options-item-numeric` | Numeric result input wrapper |
| `lens-searchbar-autocomplete-options-item-date` | Date result input wrapper |
| `lens-searchbar-autocomplete-options-item-string` | String result input wrapper |
| `lens-searchbar-autocomplete-options-item-name` | Option name |
| `lens-searchbar-autocomplete-options-item-description` | Optional description |
| `lens-searchbar-autocomplete-options-item-description-focused` | Focused optional description |
| `lens-searchbar-autocomplete-options-item-facet-count` | Facet count badge |
| `lens-searchbar-autocomplete-options-type-more-message` | Wrapper for type more message |
10 changes: 9 additions & 1 deletion demo.svelte
Original file line number Diff line number Diff line change
Expand Up @@ -243,6 +243,14 @@
type: "BETWEEN",
system: "",
},
{
fieldType: "date",
key: "date-of-diagnosis",
name: "Date of diagnosis",
type: "BETWEEN",
system: "",
max: "2025-09-04",
},
],
},
{
Expand Down Expand Up @@ -495,7 +503,7 @@
}

.card {
background-color: white;
background-color: var(--white);
border-radius: var(--border-radius-small);
border: 1px solid var(--lightest-gray);
padding: var(--gap-xs);
Expand Down
2 changes: 1 addition & 1 deletion eslint.config.js
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ import svelte from "eslint-plugin-svelte";
import svelteConfig from "./svelte.config.js";

export default defineConfig([
globalIgnores(["dist", "book/book", "docs/assets"]),
globalIgnores(["dist", "book", "docs/assets"]),
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Generally I would want to lint the book markdown files. Can we fix the issues without disabling linting of those files completely?

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I agree. but book/book doesn't exist anywhere, so this was unnecessary, right?

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

When you install mdbook and build the book locally it generates the HTML files there.

// Recommended JavaScript and TypeScript lints
pluginJs.configs.recommended,
...tseslint.configs.recommended,
Expand Down
1 change: 1 addition & 0 deletions src/components/buttons/SearchButtonComponent.wc.svelte
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@
const onclick = (): void => {
queryModified.set(false);
window.dispatchEvent(new CustomEvent("lens-search-triggered"));
window.dispatchEvent(new CustomEvent("reset-all-searchbar-inputs"));
};
</script>

Expand Down
5 changes: 3 additions & 2 deletions src/components/buttons/StoreDeleteButtonComponent.svelte
Original file line number Diff line number Diff line change
Expand Up @@ -14,9 +14,10 @@
index: number;
item?: QueryItem;
};
resetToEmptySearchBar?: () => void;
}

let { itemToDelete }: Props = $props();
let { itemToDelete, resetToEmptySearchBar = () => {} }: Props = $props();

const { type, index, item } = itemToDelete;

Expand Down Expand Up @@ -51,7 +52,7 @@
if (searchBarInputs) {
searchBarInputs[$activeQueryGroupIndex].focus();
}

resetToEmptySearchBar();
return query;
});
}
Expand Down
28 changes: 26 additions & 2 deletions src/components/catalogue/AddButton.svelte
Original file line number Diff line number Diff line change
@@ -1,8 +1,25 @@
<script lang="ts">
let { ...props } = $props();
import type {
KeyboardEventHandler,
MouseEventHandler,
} from "svelte/elements";
interface Props {
inSearchBar: boolean;
onkeydown?: KeyboardEventHandler<HTMLButtonElement>;
onclick?: MouseEventHandler<HTMLButtonElement>;
}
let { inSearchBar, onkeydown, onclick }: Props = $props();
</script>

<button aria-label="Add to query" part="lens-add-to-query-button" {...props}>
<button
aria-label="Add to query"
part="lens-add-to-query-button {inSearchBar
? 'lens-add-to-query-button-searchbar'
: ''}"
{onkeydown}
{onclick}
type="button"
>
<svg
xmlns="http://www.w3.org/2000/svg"
viewBox="0 0 24 24"
Expand Down Expand Up @@ -31,4 +48,11 @@
[part~="lens-add-to-query-button"]:hover svg {
color: var(--light-blue);
}

[part~="lens-add-to-query-button-searchbar"] svg {
color: var(--white);
}
[part~="lens-add-to-query-button-searchbar"]:hover svg {
color: var(--light-gray);
}
</style>
75 changes: 64 additions & 11 deletions src/components/catalogue/DatePickerComponent.svelte
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think the DatePickerComponent should be less concerned with the search bar functionality.

To keep up/down arrow navigation functionality I think it would be fine to use a capture phase event handler (https://svelte.dev/tutorial/svelte/capturing) in the search bar drop down to intercept up and down arrows before they even reach the text input using event.stopPropagation(). For numbers this would be fine in my opinion. I don't consider the up/down arrow to increment/decrement numbers important. I hope the date picker calendar arrow key navigation would not break.

I think you could use a similar strategy for detecting focus without touching the DatePickerComponent. Or alternatively just add a onfocus prop to the DatePickerComponent and handle everything else outside of the component.

Original file line number Diff line number Diff line change
Expand Up @@ -8,28 +8,41 @@

let {
element,
inSearchBar = false,
setActiveElement = () => {},
resetToEmptySearchBar = () => {},
focusSearchbar = () => {},
}: {
element: DateRangeCategory;
inSearchBar?: boolean;
setActiveElement?: (activate?: boolean) => void;
resetToEmptySearchBar?: (focus?: boolean) => void;
focusSearchbar?: () => void;
} = $props();

let form: HTMLFormElement;
let fromInput: HTMLInputElement;
let toInput: HTMLInputElement;
let from: string = $state("");
let to: string = $state("");

onMount(() => {
fromInput.focus();
if (inSearchBar === false) fromInput.focus();
});

$effect(() => {
if (from === "" && to === "") {
let formVlaid = $derived(validateForm(from, to));

function validateForm(from: string, to: string): boolean {
fromInput.setCustomValidity("");
if (!from && !to) {
fromInput.setCustomValidity(translate("cannot_both_be_empty"));
} else if (from !== "" && to !== "" && from > to) {
return false;
} else if (from && to && from > to) {
fromInput.setCustomValidity(translate("min_must_be_less_than_max"));
} else {
fromInput.setCustomValidity("");
return false;
}
});
return true;
}

function getMinMax(min: string | null, max: string | null): string {
if (min && max && min === max) return `${min}`;
Expand All @@ -39,8 +52,12 @@
return "";
}

function onsubmit(event: SubmitEvent): void {
event.preventDefault();
function addItem(): void {
if (!formVlaid) {
fromInput.reportValidity();
return;
}

addItemToQuery(
{
id: uuidv4(),
Expand All @@ -57,11 +74,46 @@
},
$activeQueryGroupIndex,
);

resetToEmptySearchBar();
}

function handleKeyDown(event: KeyboardEvent) {
if (inSearchBar === false) return;

if (event.key === "Escape") {
focusSearchbar();
}

if (event.key === "Enter") {
addItem();
}
}

function onfocusin(event: FocusEvent) {
if (!inSearchBar) return;
setActiveElement();
// toInput can not be reached by tab when the focus is outside the form,
// so this can handle the focus via mouse click instead of using another event listener
if (event.target === toInput) return;

const relatedTargetOutside =
event.relatedTarget instanceof Node &&
!form.contains(event.relatedTarget);

if (relatedTargetOutside) {
fromInput.focus();
}
}

function onfocusout() {
setActiveElement(false);
}
</script>

<form part="lens-date-input-form" {onsubmit}>
<form part="lens-date-input-form" bind:this={form} {onfocusin} {onfocusout}>
<input
onkeydown={handleKeyDown}
part="lens-date-input-formfield"
type="date"
min={element.min}
Expand All @@ -71,14 +123,15 @@
/>
<span part="date-input-range-separator">-</span>
<input
onkeydown={handleKeyDown}
part="lens-date-input-formfield"
type="date"
min={element.min}
max={element.max}
bind:value={to}
bind:this={toInput}
/>
<AddButton />
<AddButton onclick={addItem} onkeydown={handleKeyDown} {inSearchBar} />
</form>

<style>
Expand Down
Loading