Skip to content

Commit

Permalink
add webui-predictive-search
Browse files Browse the repository at this point in the history
  • Loading branch information
basher committed Apr 5, 2024
1 parent d89c89b commit 1889c60
Show file tree
Hide file tree
Showing 13 changed files with 195 additions and 146 deletions.
2 changes: 1 addition & 1 deletion ui/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
"name": "web-ui-boilerplate",
"description": "UI boilerplate for websites/webapps using vanilla HTML/CSS/JavaScript, powered by Storybook, bundled by Parcel.",
"author": "basher",
"version": "2.6",
"version": "2.7",
"license": "ISC",
"repository": {
"type": "git",
Expand Down
9 changes: 4 additions & 5 deletions ui/src/javascript/ui-init.ts
Original file line number Diff line number Diff line change
@@ -1,12 +1,11 @@
// Dependencies.

// Web components.
// Web Components.
import WebUIDisclosure from './web-components/webui-disclosure';
import WebUIFetchHtml from './web-components/webui-fetch-html';
import WebUIFormValidate from './web-components/webui-form-validate';
import WebUIMakeClickable from './web-components/webui-make-clickable';
import WebUIModal from './web-components/webui-modal';
import WebUINotify from './web-components/webui-notify';
import WebUIPredictiveSearch from './web-components/webui-predictive-search';
import WebUIProse from './web-components/webui-prose';
import WebUIRangeInput from './web-components/webui-range-input';
import WebUIShare from './web-components/webui-share';
Expand All @@ -15,7 +14,6 @@ import WebUITabs from './web-components/webui-tabs';
import WebUIVideoPlayer from './web-components/webui-video-player';

// Modules.
import Search from './modules/search';
import Slider from './modules/slider';

export const uiInit = (): void => {
Expand All @@ -32,6 +30,8 @@ export const uiInit = (): void => {
customElements.define('webui-modal', WebUIModal);
!customElements.get('webui-notify') &&
customElements.define('webui-notify', WebUINotify);
!customElements.get('webui-predictive-search') &&
customElements.define('webui-predictive-search', WebUIPredictiveSearch);
!customElements.get('webui-prose') &&
customElements.define('webui-prose', WebUIProse);
!customElements.get('webui-range-input') &&
Expand All @@ -45,6 +45,5 @@ export const uiInit = (): void => {
!customElements.get('webui-video-player') &&
customElements.define('webui-video-player', WebUIVideoPlayer);

Search.start();
Slider.start();
};
Original file line number Diff line number Diff line change
@@ -1,56 +1,32 @@
import main from '../../javascript/config/main';
import { addJSClass } from '../utils/progressive-enhancement';
import main from '../config/main';
import {
ajaxAbortHandler,
ajaxErrorHandler,
ajaxEventHandler,
} from '../utils/ajax-helpers';
import searchResults from '../templates/search-results';

class Search {
private searchComponent: Element;
export default class WebUIPredictiveSearch extends HTMLElement {
private searchForm: HTMLFormElement | null;
private searchInput: HTMLInputElement | null;

constructor(searchComponent: Element) {
this.searchComponent = searchComponent;
this.searchForm = this.searchComponent.querySelector('[role="search"]');
this.searchInput =
this.searchComponent.querySelector('[type="search"]');
constructor() {
super();

this.init();
}
this.searchForm = this.querySelector('[role="search"]');
this.searchInput = this.querySelector('[type="search"]');

public static start(): void {
const searchComponents = document.querySelectorAll(
'[data-module="search"]',
);
if (!this.searchForm || !this.searchInput) return;

searchComponents.forEach((searchComponent) => {
addJSClass(searchComponent);
const instance = new Search(searchComponent);
return instance;
ajaxEventHandler({
ajaxTrigger: this.searchInput,
eventType: 'keyup',
ajaxCallback: this.handleKeyUp,
});
}

private init(): void {
this.searchForm?.addEventListener('submit', (e: Event) =>
this.handleSubmit(e),
);

this.searchInput &&
ajaxEventHandler({
ajaxTrigger: this.searchInput,
eventType: 'keyup',
ajaxCallback: this.handleKeyUp,
});
this.searchForm.addEventListener('submit', this);
}

private handleSubmit = (e: Event): void => {
// Results are shown dynamically, so no need to submit.
e.preventDefault();
};

private handleKeyUp = (ajaxContainer: HTMLElement): void => {
const showAjaxSpinner = true;
const query = this.searchInput?.value.toLowerCase();
Expand Down Expand Up @@ -113,6 +89,10 @@ class Search {
});
}
};
}

export default Search;
// Handle constructor() event listeners.
handleEvent(e: SubmitEvent) {
// Results are shown dynamically, so no need to submit.
e.preventDefault();
}
}
2 changes: 1 addition & 1 deletion ui/src/javascript/web-components/webui-toggle.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ export default class WebUIToggle extends HTMLElement {

constructor() {
super();
this.switch = this.querySelector('[role=switch]');
this.switch = this.querySelector('[role="switch"]');

if (!this.switch) return;

Expand Down
25 changes: 2 additions & 23 deletions ui/src/stylesheets/components/_search.scss
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,8 @@
Dependencies.
----------------------------------------------------------------------------
*/
@use '../base' as *;
@use '../mixins' as *;
// @use '../base' as *;
// @use '../mixins' as *;

.search {
.form {
Expand All @@ -23,25 +23,4 @@ Dependencies.
.input {
flex-basis: 100%;
}

&.is-js-enabled {
[type='submit'] {
display: none;
}
}

&__results {
margin-block-start: $gutter-m;

.grid {
@include responsive-grid-auto-columns($max-width: 10rem);
}
}

&__result {
&__link {
@include focus;
color: inherit;
}
}
}
1 change: 1 addition & 0 deletions ui/src/stylesheets/web-components/_index.scss
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
@use 'webui-make-clickable';
@use 'webui-modal';
@use 'webui-notify';
@use 'webui-predictive-search';
@use 'webui-prose';
@use 'webui-range-input';
@use 'webui-share';
Expand Down
28 changes: 28 additions & 0 deletions ui/src/stylesheets/web-components/_webui-predictive-search.scss
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
/*
----------------------------------------------------------------------------
Dependencies.
----------------------------------------------------------------------------
*/
@use '../base' as *;
@use '../mixins' as *;

webui-predictive-search {
[type='submit'] {
display: none;
}

.search__results {
margin-block-start: $gutter-m;

.grid {
@include responsive-grid-auto-columns($max-width: 10rem);
}
}

.search__result {
&__link {
@include focus;
color: inherit;
}
}
}
138 changes: 69 additions & 69 deletions ui/stories/5. Components/Search/Search.js
Original file line number Diff line number Diff line change
@@ -1,79 +1,79 @@
export const SearchHtml = () => `
<div class="search">
<form class="form search__form" role="search">
<div class="form__field">
<label for="search" class="label">Search</label>
<input
id="search"
type="search"
class="input"
placeholder="Search"
required
/>
</div>
<button type="submit" class="button button--text button--positive">
Submit
</button>
</form>
</div>
<div class="search">
<form class="form search__form" role="search">
<div class="form__field">
<label for="search" class="label">Search</label>
<input
id="search"
type="search"
class="input"
placeholder="Search"
required
/>
</div>
<button type="submit" class="button button--text button--positive">
Submit
</button>
</form>
</div>
`;

export const SearchDatalistHtml = () => `
<div class="search">
<form class="form search__form" role="search">
<div class="form__field">
<label for="search" class="label">Search</label>
<input
id="search"
type="text"
class="input"
placeholder="Search"
required
list="search-terms"
/>
<datalist id="search-terms">
<option value="test1">
<option value="test2">
<option value="test3">
<option value="abc1">
<option value="abc2">
<option value="abc3">
<option value="longer text1">
<option value="longer text2">
<option value="longer text3">
</datalist>
</div>
<button type="submit" class="button button--text button--positive">
Submit
</button>
</form>
</div>
<div class="search">
<form class="form search__form" role="search">
<div class="form__field">
<label for="search" class="label">Search</label>
<input
id="search"
type="text"
class="input"
placeholder="Search"
required
list="search-terms"
/>
<datalist id="search-terms">
<option value="test1">
<option value="test2">
<option value="test3">
<option value="abc1">
<option value="abc2">
<option value="abc3">
<option value="longer text1">
<option value="longer text2">
<option value="longer text3">
</datalist>
</div>
<button type="submit" class="button button--text button--positive">
Submit
</button>
</form>
</div>
`;

export const SearchAjaxHtml = () => `
<div class="search" data-module="search">
<form class="form search__form" role="search">
<div class="form__field">
<label for="search" class="label">Search</label>
<input
id="search"
type="search"
class="input"
placeholder="Search for a Pokemon"
required
data-ajax-trigger="search-results"
/>
</div>
<button type="submit" class="button button--text button--positive">
Submit
</button>
</form>
<div
class="search__results ajax__container"
data-ajax-container="search-results"
role="region"
aria-live="polite"
>
<div class="search" data-module="search">
<form class="form search__form" role="search">
<div class="form__field">
<label for="search" class="label">Search</label>
<input
id="search"
type="search"
class="input"
placeholder="Search for a Pokemon"
required
data-ajax-trigger="search-results"
/>
</div>
<button type="submit" class="button button--text button--positive">
Submit
</button>
</form>
<div
class="search__results ajax__container"
data-ajax-container="search-results"
role="region"
aria-live="polite"
>
</div>
</div>
`;
4 changes: 3 additions & 1 deletion ui/stories/5. Components/Search/Search.mdx
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import { Meta, Canvas } from '@storybook/blocks';
import * as Search from './Search.stories';
import * as WebUIPredictiveSearch from '../../6. Web Components Or Custom Elements/WebUI Predictive Search/WebUIPredictiveSearch.stories';

<Meta of={Search} />

Expand All @@ -15,9 +16,10 @@ import * as Search from './Search.stories';
<Canvas of={Search.SearchDatalist} />

## Predictive search (with dynamic results from API)
- Uses the [`<webui-predictive-search>`](/docs/web-components-or-custom-elements-webui-predictive-search--docs) custom element.
- When JavaScript is enabled, form submit is disabled and submit button is hidden.

### Accessibility considerations
- Dynamic search results are displayed in an [aria-live region](https://developer.mozilla.org/en-US/docs/Web/Accessibility/ARIA/ARIA_Live_Regions).

<Canvas of={Search.SearchAjax} />
<Canvas of={WebUIPredictiveSearch.WebUIPredictiveSearch} />
Loading

0 comments on commit 1889c60

Please sign in to comment.