From 4fc284ac8a46110b2cb3991c615cc2d3d62b5fb9 Mon Sep 17 00:00:00 2001 From: basher Date: Fri, 15 Nov 2024 10:31:49 +0000 Subject: [PATCH] improve ajax web components - add fetch URL data attribute --- ui/package.json | 2 +- ui/src/javascript/utils/ajax-helpers.ts | 8 +++++--- .../javascript/web-components/webui-fetch-html.ts | 10 ++++++---- .../web-components/webui-predictive-search.ts | 15 +++++++++------ ui/stories/1. Storybook Intro/1-Introduction.mdx | 8 ++++---- .../WebUI Fetch Html/WebUIFetchHtml.js | 1 + .../WebUI Fetch Html/WebUIFetchHtml.mdx | 1 + .../WebUIPredictiveSearch.js | 1 + .../WebUIPredictiveSearch.mdx | 1 + 9 files changed, 29 insertions(+), 18 deletions(-) diff --git a/ui/package.json b/ui/package.json index afe39ed..e06e9fe 100644 --- a/ui/package.json +++ b/ui/package.json @@ -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": "4.0.4", + "version": "4.0.5", "license": "ISC", "repository": { "type": "git", diff --git a/ui/src/javascript/utils/ajax-helpers.ts b/ui/src/javascript/utils/ajax-helpers.ts index 1c5b8fa..194f4da 100644 --- a/ui/src/javascript/utils/ajax-helpers.ts +++ b/ui/src/javascript/utils/ajax-helpers.ts @@ -68,16 +68,18 @@ export const ajaxErrorHandler = (arg: AjaxError): void => { * @param {HTMLElement} ajaxTrigger - element that triggers the Ajax request * @param {string} eventType - event type that triggers the Ajax request * @param {Function} ajaxCallback - callback function that handles the Ajax request + * * @param {string} ajaxUrl - URL of data to be fetched (e.g. HTML fragment or API endpoint) * @returns {void} */ interface AjaxEvent { ajaxTrigger: HTMLElement; eventType: string; - ajaxCallback: (ajaxContainer: HTMLElement) => void; + ajaxCallback: (ajaxContainer: HTMLElement, ajaxUrl: string) => void; + ajaxUrl: string; } export const ajaxEventHandler = (arg: AjaxEvent): void => { - const { ajaxTrigger, eventType, ajaxCallback } = arg; + const { ajaxTrigger, eventType, ajaxCallback, ajaxUrl } = arg; // Value of 'data-fetch-trigger' must match 'data-fetch-container' so that an Ajax trigger (e.g. button) loads content into the correct container. const target = ajaxTrigger?.dataset.fetchTarget; @@ -88,7 +90,7 @@ export const ajaxEventHandler = (arg: AjaxEvent): void => { ajaxTrigger.addEventListener(eventType, () => { if (ajaxContainer) { ajaxContainer.classList.remove('ajax__error'); - ajaxCallback(ajaxContainer); + ajaxCallback(ajaxContainer, ajaxUrl); } }); }; diff --git a/ui/src/javascript/web-components/webui-fetch-html.ts b/ui/src/javascript/web-components/webui-fetch-html.ts index bbba5fc..ced9ad5 100644 --- a/ui/src/javascript/web-components/webui-fetch-html.ts +++ b/ui/src/javascript/web-components/webui-fetch-html.ts @@ -6,26 +6,28 @@ import { export default class WebUIFetchHtml extends HTMLElement { private fetchTrigger: HTMLButtonElement | null; + private fetchUrl: string | undefined; constructor() { super(); this.fetchTrigger = this.querySelector('[data-fetch-target]'); + this.fetchUrl = this.fetchTrigger?.dataset.fetchUrl; - if (!this.fetchTrigger) return; + if (!this.fetchTrigger || !this.fetchUrl) return; ajaxEventHandler({ ajaxTrigger: this.fetchTrigger, eventType: 'click', ajaxCallback: this.handleClick, + ajaxUrl: this.fetchUrl, }); } - private handleClick(ajaxContainer: HTMLElement): void { + private handleClick(ajaxContainer: HTMLElement, ajaxUrl: string): void { const showAjaxLoader = true; - // TODO: Add 'data-' attribute on button to specifiy HTML source to be fetched. - fetch('ajax/ajax.html', { + fetch(ajaxUrl, { method: 'GET', signal: ajaxAbortHandler({ ajaxContainer, diff --git a/ui/src/javascript/web-components/webui-predictive-search.ts b/ui/src/javascript/web-components/webui-predictive-search.ts index a06dce9..fc9a8ce 100644 --- a/ui/src/javascript/web-components/webui-predictive-search.ts +++ b/ui/src/javascript/web-components/webui-predictive-search.ts @@ -9,38 +9,41 @@ import searchResults from '../templates/search-results'; export default class WebUIPredictiveSearch extends HTMLElement { private searchForm: HTMLFormElement | null; private searchInput: HTMLInputElement | null; + private fetchUrl: string | undefined; constructor() { super(); this.searchForm = this.querySelector('[role="search"]'); this.searchInput = this.querySelector('[type="search"]'); + this.fetchUrl = this.searchInput?.dataset.fetchUrl; - if (!this.searchForm || !this.searchInput) return; + if (!this.searchForm || !this.searchInput || !this.fetchUrl) return; ajaxEventHandler({ ajaxTrigger: this.searchInput, eventType: 'keyup', ajaxCallback: this.handleKeyUp, + ajaxUrl: this.fetchUrl, }); this.searchForm.addEventListener('submit', this); } - private handleKeyUp = (ajaxContainer: HTMLElement): void => { + private handleKeyUp = ( + ajaxContainer: HTMLElement, + ajaxUrl: string, + ): void => { const showAjaxLoader = true; const query = this.searchInput?.value.toLowerCase(); - // API paths would normally be defined in a global config. - const API_PATH = 'https://pokeapi.co/api/v2/pokemon?limit=1000'; - if (!query) { ajaxContainer.innerHTML = ''; return; } if (query && query?.length > 2) { - fetch(API_PATH, { + fetch(ajaxUrl, { method: 'GET', headers: { 'Content-Type': 'application/json', diff --git a/ui/stories/1. Storybook Intro/1-Introduction.mdx b/ui/stories/1. Storybook Intro/1-Introduction.mdx index ef10c4a..f6e0af8 100644 --- a/ui/stories/1. Storybook Intro/1-Introduction.mdx +++ b/ui/stories/1. Storybook Intro/1-Introduction.mdx @@ -5,10 +5,10 @@ import { Meta } from '@storybook/blocks'; # Web UI Boilerplate Storybook This is an accessible UI boilerplate and component library for websites & webapps. -It comprises -- global design system tokens - colours, typography, icons -- fully functioning UI components and layouts, which can be optionally configured using Storybook's `controls` addon to demonstrate variations in behaviour & layout -- useful utilities & helpers +It comprises: +- Global design system tokens - colours, typography, icons. +- Fully functioning UI components and layouts, which can be optionally configured using Storybook's `controls` addon to demonstrate variations in behaviour & layout. +- Useful utilities & helpers. ``` No external frameworks have been harmed in the making of this boilerplate! diff --git a/ui/stories/6. Web Components Or Custom Elements/WebUI Fetch Html/WebUIFetchHtml.js b/ui/stories/6. Web Components Or Custom Elements/WebUI Fetch Html/WebUIFetchHtml.js index 1cdd4c3..a880750 100644 --- a/ui/stories/6. Web Components Or Custom Elements/WebUI Fetch Html/WebUIFetchHtml.js +++ b/ui/stories/6. Web Components Or Custom Elements/WebUI Fetch Html/WebUIFetchHtml.js @@ -5,6 +5,7 @@ export const WebUIFetchHtmlHtml = () => ` diff --git a/ui/stories/6. Web Components Or Custom Elements/WebUI Fetch Html/WebUIFetchHtml.mdx b/ui/stories/6. Web Components Or Custom Elements/WebUI Fetch Html/WebUIFetchHtml.mdx index fe78941..9718b1e 100644 --- a/ui/stories/6. Web Components Or Custom Elements/WebUI Fetch Html/WebUIFetchHtml.mdx +++ b/ui/stories/6. Web Components Or Custom Elements/WebUI Fetch Html/WebUIFetchHtml.mdx @@ -23,6 +23,7 @@ Attribute | Behaviour --- | --- `data-fetch-target` | Required. On the button that triggers the Fetch request. The value must match `data-fetch-container`. `data-fetch-container` | Required. On the ARIA live region. The value must match `data-fetch-target`. +`data-fetch-url` | Required. URL of HTML fragment to be fetched. ## TODO - Add `data-` attribute on button to specifiy HTML source to be fetched. diff --git a/ui/stories/6. Web Components Or Custom Elements/WebUI Predictive Search/WebUIPredictiveSearch.js b/ui/stories/6. Web Components Or Custom Elements/WebUI Predictive Search/WebUIPredictiveSearch.js index ec0d4b1..dae7255 100644 --- a/ui/stories/6. Web Components Or Custom Elements/WebUI Predictive Search/WebUIPredictiveSearch.js +++ b/ui/stories/6. Web Components Or Custom Elements/WebUI Predictive Search/WebUIPredictiveSearch.js @@ -10,6 +10,7 @@ export const WebUIPredictiveSearchHtml = () => ` placeholder="Search for a Pokemon" required data-fetch-target="search-results" + data-fetch-url="https://pokeapi.co/api/v2/pokemon?limit=1000" />