Skip to content

Commit

Permalink
add web components
Browse files Browse the repository at this point in the history
  • Loading branch information
basher committed Mar 28, 2024
1 parent d252592 commit 027a76d
Show file tree
Hide file tree
Showing 22 changed files with 157 additions and 22 deletions.
2 changes: 1 addition & 1 deletion ui/.storybook/preview.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ const preview = {
parameters: {
options: {
storySort: {
order: ['Web UI Storybook', 'Design System', 'Layout','Forms', 'Components', 'Utilities', 'Pages'],
order: ['Web UI Storybook', 'Design System', 'Layout','Forms', 'Components', 'Web Components Or Custom Elements', 'Utilities', 'Pages'],
},
},
},
Expand Down
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.0.0",
"version": "2.1.0",
"license": "ISC",
"repository": {
"type": "git",
Expand Down
34 changes: 20 additions & 14 deletions ui/src/javascript/modules/disclosure.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,8 @@ export default class Disclosure {
private disclosure: Element;
private btnDisclosure: HTMLButtonElement | null;
private content: HTMLElement | null;
private bindEscapeKey?: boolean;
private bindClickOutside?: boolean;

constructor(disclosure: Element) {
this.disclosure = disclosure;
Expand All @@ -14,6 +16,12 @@ export default class Disclosure {
this.content = this.disclosure.querySelector(
'[data-disclosure-content]',
);
this.bindEscapeKey = this.disclosure.hasAttribute(
'data-disclosure-escape-key',
);
this.bindClickOutside = this.disclosure.hasAttribute(
'data-disclosure-click-outside',
);

this.init();
}
Expand All @@ -35,20 +43,18 @@ export default class Disclosure {
}

private initdisclosure(): void {
const button = this.btnDisclosure;
const content = this.content;
const bindEscapeKey = this.bindEscapeKey;
const bindClickOutside = this.bindClickOutside;

// Show/hide content.
const button = this.btnDisclosure as HTMLElement;
const content = this.content as HTMLElement;
const bindEscapeKey = this.disclosure.hasAttribute(
'data-disclosure-escape-key',
);
const bindClickOutside = this.disclosure.hasAttribute(
'data-disclosure-click-outside',
);
disclosure({
button,
content,
bindEscapeKey,
bindClickOutside,
});
button &&
disclosure({
button,
content,
bindEscapeKey,
bindClickOutside,
});
}
}
6 changes: 6 additions & 0 deletions ui/src/javascript/ui-init.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,8 @@ import Tabs from './modules/tabs';
import Toggle from './modules/toggle';
import VideoPlayer from './modules/video-player';

import WebDisclosure from './web-components/web-disclosure';

// For DEMO purposes only.
import demoAjaxFetchHTML from './modules/demo-ajax-fetch-html';

Expand All @@ -33,4 +35,8 @@ export const uiInit = (): void => {

// For DEMO purposes only.
demoAjaxFetchHTML.start();

// Define Web Components
!customElements.get('web-disclosure') &&
customElements.define('web-disclosure', WebDisclosure);
};
2 changes: 1 addition & 1 deletion ui/src/javascript/utils/disclosure.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ import { randomString } from './random-string';
* disclosure({ button, content });
*/
interface Disclosure {
button: HTMLElement;
button: HTMLButtonElement;
content: HTMLElement | null;
bindEscapeKey?: boolean;
bindClickOutside?: boolean;
Expand Down
37 changes: 37 additions & 0 deletions ui/src/javascript/web-components/web-disclosure.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
import { disclosure } from '../utils/disclosure';

export default class WebDisclosure extends HTMLElement {
private trigger: HTMLButtonElement | null;
private content: HTMLElement | null;
private bindEscapeKey?: boolean;
private bindClickOutside?: boolean;

constructor() {
super();

this.trigger = this.querySelector('[trigger]');
this.content = this.querySelector('[content]');
this.bindEscapeKey = this.hasAttribute('bind-escape-key');
this.bindClickOutside = this.hasAttribute('bind-click-outside');

this.init();

// NOTE: There are NO event listeners here. All events are handled by the external 'discloure()' dependency.
}

private init(): void {
if (!this.trigger || !this.content) return;

const button = this.trigger;
const content = this.content;
const bindEscapeKey = this.bindEscapeKey;
const bindClickOutside = this.bindClickOutside;

disclosure({
button,
content,
bindEscapeKey,
bindClickOutside,
});
}
}
1 change: 1 addition & 0 deletions ui/src/stylesheets/index.scss
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,7 @@ CSS Cascade Layers.
@layer components {
@include meta.load-css('form');
@include meta.load-css('components');
@include meta.load-css('web-components');
}

/*
Expand Down
1 change: 1 addition & 0 deletions ui/src/stylesheets/web-components/_index.scss
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
@use 'web-disclosure';
11 changes: 11 additions & 0 deletions ui/src/stylesheets/web-components/_web-disclosure.scss
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
/*
----------------------------------------------------------------------------
Dependencies.
----------------------------------------------------------------------------
*/
// @use '../base' as *;
// @use '../mixins' as *;

web-disclosure {
//
}
2 changes: 1 addition & 1 deletion ui/stories/3. Layout/UsefulTips.mdx
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { Meta } from '@storybook/blocks';

<Meta title="Layout/Useful layout tips" />
<Meta title="Layout/Useful Layout Tips" />

# Useful layout tips

Expand Down
3 changes: 3 additions & 0 deletions ui/stories/5. Components/Disclosure/Disclosure.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -12,5 +12,8 @@ import * as Disclosure from './Disclosure.stories';
- By adding the `data-disclosure-escape-key` attribute to the HTML, the `ESC` key can be used to hide the content.
- Similarly, the `data-disclosure-click-outside` attribute hides the content when clicking anywhere outside the content.

## Web component version
- See the [web-disclosure](/story/web-components-web-disclosure--web-disclosure) component.

<Canvas of={Disclosure.Disclosure} />
<Controls of={Disclosure.Disclosure} />
3 changes: 1 addition & 2 deletions ui/stories/5. Components/Disclosure/Disclosure.stories.js
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import { DisclosureHtml } from './Disclosure';

export default {
title: 'Components/Disclosure',
title: 'Components/Disclosure (Or Show|Hide)',
parameters: {
status: {
type: 'stable',
Expand All @@ -22,4 +22,3 @@ export default {
export const Disclosure = {
render: (args) => DisclosureHtml(args),
};
Disclosure.storyName = 'Disclosure (i.e. Show/Hide)';
13 changes: 13 additions & 0 deletions ui/stories/6. Web Components Or Custom Elements/WebComponents.mdx
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
import { Meta } from '@storybook/blocks';

<Meta title="Web Components Or Custom Elements/Using Web Components" />

# HTML Web Components
- The Web Components in this boilerplate are simply `custom elements` that wrap normal HTML markup, therefore providing a progressive enhancement layer via JavaScript.
- They are **not** empty shells that only work with JavaScript.
- They also don't make use of the [shadow DOM](https://developer.mozilla.org/en-US/docs/Web/API/Web_components/Using_shadow_DOM).

## Additional reading
- Great explanation by Jeremy Keith (and others) about the power of [HTML Web Components](https://adactio.com/journal/20618).
- Simple example from Chris Ferdinandi showing how to [create a Web Component from scratch](https://gomakethings.com/lets-create-a-web-component-from-scratch/). He also has lots more articles on this topic in his blog.
- Some notes on the recommended use of [connectedCallback() lifecycle method and event listeners](https://hawkticehurst.com/writing/you-are-probably-using-connectedcallback-wrong/).
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
export const WebDisclosureHtml = (args) => `
<web-disclosure
class="disclosure"
${args.bindEscapeKey === true ? 'bind-escape-key' : ''}
${args.bindClickOutside === true ? 'bind-click-outside' : ''}
>
<button
type="button"
class="button button--text"
trigger
hidden
>
<span>Show / Hide</span>
</button>
<div content>
<p>Content to be shown/hidden.</p>
<p>Use this component when <code>accordion</code> or <code>tabs</code> components cannot be used.</p>
</div>
</web-disclosure>
`;
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
import { Meta, Canvas, Controls } from '@storybook/blocks';
import * as WebDisclosure from './WebDisclosure.stories';

<Meta of={WebDisclosure} />

# `<web-disclosure>`
- This is functionally equivalent to the [disclosure](/story/components-disclosure--disclosure) component, with the same accessibility considerations.
- The attributes that bind clicking the `ESC` key and clicking outside are renamed to `bind-escape-key` and `bind-click-outside`.
- To maintain the [DRY code](https://en.wikipedia.org/wiki/Don%27t_repeat_yourself) approach in this boilerplate, the JavaScript logic to handle showing/hiding of content is imported from an external shared utility function in `javascript/utils/disclosure.ts`.

<Canvas of={WebDisclosure.WebDisclosure} />
<Controls of={WebDisclosure.WebDisclosure} />
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
import { WebDisclosureHtml } from './WebDisclosure';

export default {
title: 'Web Components Or Custom Elements/<web-disclosure>',
parameters: {
status: {
type: 'stable',
},
},
argTypes: {
bindEscapeKey: {
control: 'boolean',
description: 'Close with ESC key.'
},
bindClickOutside: {
control: 'boolean',
description: 'Close by clicking outside'
},
},
};

export const WebDisclosure = {
render: (args) => WebDisclosureHtml(args),
};
WebDisclosure.storyName = '<web-disclosure>';
File renamed without changes.
File renamed without changes.
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import { MarginPaddingHtml } from './Utilities';

export default {
title: 'Utilities/Helpers and utilities',
title: 'Utilities/Helpers and Utilities',
parameters: {
status: {
type: 'stable',
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ import {
} from './Utilities';

export default {
title: 'Utilities/Helpers and utilities',
title: 'Utilities/Helpers and Utilities',
parameters: {
status: {
type: 'stable',
Expand Down
File renamed without changes.
File renamed without changes.

0 comments on commit 027a76d

Please sign in to comment.