Skip to content

Commit cc7132d

Browse files
authored
Merge pull request #711 from HelixDesignSystem/surf-1859-2041-implement-hx-beacon
feat(hx-beacon): implement beacon with component tests
2 parents 4de207b + 21e5df6 commit cc7132d

File tree

14 files changed

+427
-6
lines changed

14 files changed

+427
-6
lines changed

docs/_data/nav.json5

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@
1717
{ label: 'Accordion', path: 'accordion' },
1818
{ label: 'Alert', path: 'alert' },
1919
{ label: 'Badge', path: 'badge' },
20+
{ label: 'Beacon', path: 'beacon' },
2021
{ label: 'Box', path: 'box' },
2122
{ label: 'Breadcrumb', path: 'breadcrumb' },
2223
{ label: 'Button', path: 'button' },
@@ -70,6 +71,7 @@
7071
{ label: '<hx-accordion>', path: 'hx-accordion' },
7172
{ label: '<hx-accordion-panel>', path: 'hx-accordion-panel' },
7273
{ label: '<hx-alert>', path: 'hx-alert' },
74+
{ label: '<hx-beacon>', path: 'hx-beacon' },
7375
{ label: '<hx-busy>', path: 'hx-busy' },
7476
{ label: '<hx-checkbox>', path: 'hx-checkbox' },
7577
{ label: '<hx-checkbox-control>', path: 'hx-checkbox-control' },
Lines changed: 73 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,73 @@
1+
import Util from '../../_util';
2+
3+
(function () {
4+
5+
if (document.getElementById('vue-beaconDemo')) {
6+
new Vue({
7+
el: '#vue-beaconDemo',
8+
data: {
9+
beaconEl: '<hx-beacon></hx-beacon>',
10+
},
11+
methods: {
12+
onEvent: function (evt) {
13+
alert(`Beacon clicked! Event: ${evt.type}`);
14+
this.beaconEl = '';
15+
},
16+
},
17+
computed: {
18+
snippet: function () {
19+
return Util.snippet(`
20+
<div>
21+
${this.beaconEl}
22+
</div>
23+
`);
24+
},
25+
},
26+
});
27+
}//vue-beaconDemo
28+
29+
if (document.getElementById('vue-beacon-inline-styles')) {
30+
new Vue({
31+
el: '#vue-beacon-inline-styles',
32+
data: {
33+
beaconInlineStyles: 'top: 42%; left: 24px;',
34+
isOpen: false,
35+
isDismissed: false,
36+
},
37+
methods: {
38+
onEvent: function () {
39+
this.isOpen = true;
40+
this.isDismissed = true;
41+
},
42+
43+
},
44+
computed: {
45+
beaconSnippet: function () {
46+
if (this.isDismissed) {
47+
return '';
48+
} else {
49+
return `<hx-beacon style="${this.beaconInlineStyles}"></hx-beacon>`;
50+
}
51+
},
52+
snippetInlineStyles: function () {
53+
return Util.snippet(`
54+
<!-- Providing a border around the <div> for illustration -->
55+
<div style="position: relative;">
56+
${this.beaconSnippet}
57+
<hx-disclosure class="hxBtn" aria-controls="basicMenuId">
58+
<hx-icon type="cog"></hx-icon>
59+
<span>Actions</span>
60+
<hx-icon class="hxPrimary" type="angle-down"></hx-icon>
61+
</hx-disclosure>
62+
<hx-menu id="basicMenuId">
63+
<hx-menuitem>Action 1</hx-menuitem>
64+
<hx-menuitem>Action 2</hx-menuitem>
65+
<hx-menuitem>Action 3</hx-menuitem>
66+
</hx-menu>
67+
</div>
68+
`);
69+
},
70+
},
71+
});
72+
}//vue-beacon-inline-styles
73+
})();

docs/components/beacon/index.html

Lines changed: 103 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,103 @@
1+
---
2+
title: Beacon
3+
minver: 0.23.0
4+
also:
5+
elements/hx-beacon: <hx-beacon>
6+
---
7+
{% extends 'component.njk' %}
8+
9+
{% block page_header %}
10+
<p>
11+
A {{page.title}} is an animation that attracts a user's attention
12+
to a new feature or advanced functionality in a control panel.
13+
</p>
14+
{% endblock %}
15+
16+
{% block content %}
17+
<section>
18+
<header>
19+
<h2 id="basic-beacon">Basic Beacon</h2>
20+
</header>
21+
<p>
22+
A beacon will emit a <code>dismiss</code> event and it will also be removed
23+
from the DOM, when a user clicks it.
24+
</p>
25+
<div class="example" id="vue-beaconDemo" v-cloak>
26+
<div>
27+
<hx-beacon
28+
@dismiss="onEvent"
29+
>
30+
</hx-beacon>
31+
</div>
32+
33+
<footer>
34+
<pre><code v-text="snippet"></code></pre>
35+
</footer>
36+
</div>
37+
</section>
38+
39+
40+
<section>
41+
<header>
42+
<h2 id="beacon-with-inline-styles">Beacon with Inline Styles</h2>
43+
</header>
44+
<p>
45+
This is an example implementation of <code>&lt;hx-beacon&gt;</code>.
46+
We leave it to HelixUI implementors to decide what works best for their
47+
applications.
48+
</p>
49+
<p class="hxSubdued hxSubBody">
50+
<hx-icon type="exclamation-triangle"></hx-icon>
51+
The parent element of the {{page.title}} must have
52+
<code>position:relative</code> set. Additionally, you may have to
53+
use <b>inline or additional styles</b> to adjust the
54+
<code>&lt;hx-beacon&gt;</code> position. Valid styles are <b>top</b>,
55+
<b>bottom</b>, <b>left</b>, <b>right</b>. (Please see demo snippet below)
56+
</p>
57+
<div class="example" id="vue-beacon-inline-styles" v-cloak>
58+
<header>
59+
<form class="beta-hxForm">
60+
<fieldset>
61+
<hx-text-control>
62+
<input
63+
id="txtBeaconInlineStyles"
64+
type="text"
65+
v-model="beaconInlineStyles"
66+
/>
67+
<label for="txtBeaconInlineStyles">
68+
Beacon Inline Styles
69+
</label>
70+
</hx-text-control>
71+
<p class="hxSubdued hxSubBody">
72+
<hx-icon type="info-circle"></hx-icon>
73+
Use percentages and/or pixel values to adjust the position
74+
relative to the parent element.
75+
</p>
76+
</fieldset>
77+
</form>
78+
</header>
79+
80+
<div style="position: relative; border: 1px dashed black;"> <!-- demo snippet -->
81+
<hx-beacon
82+
@dismiss="onEvent"
83+
:style="beaconInlineStyles"
84+
>
85+
</hx-beacon>
86+
<hx-disclosure class="hxBtn" aria-controls="basicMenuId">
87+
<hx-icon type="cog"></hx-icon>
88+
<span>Actions</span>
89+
<hx-icon class="hxPrimary" type="angle-down"></hx-icon>
90+
</hx-disclosure>
91+
<hx-menu id="basicMenuId" :open="isOpen">
92+
<hx-menuitem>Action 1</hx-menuitem>
93+
<hx-menuitem>Action 2</hx-menuitem>
94+
<hx-menuitem>Action 3</hx-menuitem>
95+
</hx-menu>
96+
</div>
97+
98+
<footer>
99+
<pre><code v-text="snippetInlineStyles"></code></pre>
100+
</footer>
101+
</div>
102+
</section>
103+
{% endblock %}

docs/components/toast/index.html

Lines changed: 4 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -7,11 +7,10 @@
77
{% extends 'component.njk' %}
88

99
{% block page_header %}
10-
<hx-alert type="info" status="note" persist>
11-
Currently, only CTA and dismiss functionality of the toast itself has been
12-
implemented. Positioning, stacking, animation, etc. functionality will be coming soon.
13-
</hx-alert>
14-
{# TODO: add component description #}
10+
<p>
11+
{{page.title}} provides a user with information for action
12+
outside of the user's current context.
13+
</p>
1514
{% endblock %}
1615

1716
{% block content %}

docs/docs.js

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ HelixUI.initialize();
66
import './_config';
77
import './components/accordion/accordion-demo';
88
import './components/alert/alert-demo';
9+
import './components/beacon/beacon-demo.js';
910
import './components/box/box-demo';
1011
import './components/button/button-demo';
1112
import './components/checkbox/checkbox-demo';

docs/elements/hx-beacon/index.html

Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,48 @@
1+
---
2+
title: <hx-beacon>
3+
minver: 0.23.0
4+
also:
5+
components/beacon: Beacon
6+
---
7+
{% extends 'element.njk' %}
8+
{% block page_header %}
9+
<p>
10+
The custom <code>{{page.title}}</code> element provides an animation
11+
that attracts a user's attention to a new feature or advanced functionality
12+
in a control panel. A beacon disappears after a user clicks it.
13+
</p>
14+
{% endblock %}
15+
16+
{% block content %}
17+
<section>
18+
<dl class="metadata hxList">
19+
<div>
20+
<dt>Permitted Parents</dt>
21+
<dd>Any that accept Phrasing Content</dd>
22+
</div>
23+
<div>
24+
<dt>Permitted Children</dt>
25+
<dd>Phrasing Content</dd>
26+
</div>
27+
<div>
28+
<dt>Events</dt>
29+
<dd>
30+
Any of the following:
31+
<ul class="hxList">
32+
<li>
33+
<code>dismiss</code> - User clicks on beacon to dismiss it.
34+
</li>
35+
</ul>
36+
</dd>
37+
</div>
38+
</dl>
39+
</section>
40+
41+
<section>
42+
<h2 id="methods">Methods</h2>
43+
<dl>
44+
<dt>dismiss()</dt>
45+
<dd>Dismiss the beacon (removes element from the DOM).</dd>
46+
</dl>
47+
</section>
48+
{% endblock %}
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
<div id="hxBeacon">
2+
<button id="hxDismiss" type="button">
3+
<span id="hxBeaconPulse"></span>
4+
</button>
5+
</div>
Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,53 @@
1+
// inline external styles
2+
@import "components/button/config";
3+
@import "../base";
4+
5+
// define Shadow DOM styles
6+
:host {
7+
#hxDismiss {
8+
@include hxButton--reset;
9+
10+
#hxBeaconPulse {
11+
-webkit-animation: hx-pulse 2s infinite linear;
12+
animation: hx-pulse 2s infinite linear;
13+
border-radius: 3rem;
14+
box-shadow: 0 0 1px 1px $gray-750;
15+
content: "";
16+
height: 1rem;
17+
left: 0;
18+
margin: 0;
19+
padding: 0;
20+
position: absolute;
21+
top: 0;
22+
-webkit-transform-origin: 50% 50%;
23+
transform-origin: 50% 50%;
24+
width: 1rem;
25+
cursor: pointer;
26+
}
27+
}
28+
29+
@keyframes hx-pulse {
30+
0% {
31+
transform: scale(1);
32+
opacity: 1;
33+
}
34+
35+
100% {
36+
transform: scale(2.5);
37+
opacity: 0;
38+
}
39+
}
40+
41+
// Safari
42+
@-webkit-keyframes hx-pulse {
43+
0% {
44+
transform: scale(1);
45+
opacity: 1;
46+
}
47+
48+
100% {
49+
transform: scale(2.5);
50+
opacity: 0;
51+
}
52+
}
53+
}

src/elements/hx-beacon/index.js

Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,53 @@
1+
import { HXElement } from '../../interfaces/HXElement/index.js';
2+
3+
import shadowMarkup from './_shadow.html';
4+
import shadowStyles from './_shadow.scss';
5+
6+
/**
7+
* Defines behavior for the `<hx-beacon>` element.
8+
*
9+
* @extends HXElement
10+
* @emits beacon:dismiss
11+
* @hideconstructor
12+
* @since 0.23.0
13+
*/
14+
export class HXBeaconElement extends HXElement {
15+
static get is () {
16+
return 'hx-beacon';
17+
}
18+
19+
static get template () {
20+
return `<style>${shadowStyles}</style>${shadowMarkup}`;
21+
}
22+
23+
$onCreate () {
24+
this._onDismiss = this._onDismiss.bind(this);
25+
}
26+
27+
$onConnect () {
28+
this._btnDismiss.addEventListener('click', this._onDismiss);
29+
}
30+
31+
$onDisconnect () {
32+
this._btnDismiss.removeEventListener('click', this._onDismiss);
33+
}
34+
35+
/**
36+
* Dismiss the beacon (removes element from the DOM)
37+
*/
38+
dismiss () {
39+
if (this.$emit('dismiss')) {
40+
this.remove();
41+
}
42+
}
43+
44+
/** @private */
45+
_onDismiss () {
46+
this.dismiss();
47+
}
48+
49+
/** @private */
50+
get _btnDismiss () {
51+
return this.shadowRoot.getElementById('hxDismiss');
52+
}
53+
}

0 commit comments

Comments
 (0)