Skip to content

Commit b2a548e

Browse files
committed
✨ Update Web Component Class for camelCase Props
1 parent 5eb89ec commit b2a548e

File tree

7 files changed

+149
-86
lines changed

7 files changed

+149
-86
lines changed

CHANGELOG.md

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,13 +16,20 @@ Overall the core Framework files, React Components, and Web Components and API a
1616
* https://dataformsjs.com/examples/places-demo-hbs.htm
1717
* All pages with Excel and CSV export pass prop to a HTML Control
1818
* Search Screen renders nested HTML control when `app.refreshHtmlControl()` is called from the JS Control `<json-data>`
19+
* Web Components - Component Class
20+
* `~/js/web-components/Component.js`
21+
* https://github.com/dataformsjs/dataformsjs/blob/master/js/web-components/Component.js
22+
* Update so that props defined with `camelCase` will be available as `dashed-case` from HTML Observable Attributes
23+
* Update HTML Observable Attributes to convert strings to correct type for true, false, null and empty values
24+
* Example is being published with the Web Components Places Demo:
25+
* https://www.dataformsjs.com/examples/places-demo-web.htm
1926
* Framework Plugins - Excel and CSV Export
2027
* `~/js/plugins/exportToCsv.js`
2128
* `~/js/plugins/exportToExcel.js`
2229
* Add support so that elements using the plugin are refreshed when `app.refreshHtmlControl()` is called.
2330
* Minor fix handled by using `onRendered(rootElement)` instead of `onRendered()`.
2431
* For Excel text columns were the the max character width is less than 20 an extra 2 pixels of space will be added so content better fits.
25-
* I18N update
32+
* I18N update
2633
* Framework Plugin `~/js/plugins/i18n.js`
2734
* Web Component `~/js/web-components/i18n-service.js`
2835
* Added ability to find and replace i18n keys inside of an attribute string by using syntax `[[key]]`

examples/html/cities-web.htm

Lines changed: 1 addition & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -3,27 +3,7 @@ <h1><span data-i18n="Largest Cities in"></span> <span url-param="country"></span
33
<nav>
44
<a href="#/en/" data-i18n="Countries" data-i18n-href></a>
55
<a href="#/[window.i18n_Locale]/regions/[country]" data-i18n="Regions" url-attr-param="href"></a>
6-
<div class="download-links ml-20">
7-
<span class="download" data-i18n="Download"></span>
8-
<img class="download"
9-
src="img/Excel.svg"
10-
height="16"
11-
width="16"
12-
alt="Download to Excel"
13-
title="Download to Excel"
14-
data-export-excel-selector="table"
15-
data-export-file-name="[[Cities]].xlsx"
16-
data-i18n-attr="alt, title, data-export-file-name">
17-
<img class="download"
18-
src="img/CSV.svg"
19-
height="16"
20-
width="16"
21-
alt="Download to CSV"
22-
title="Download to CSV"
23-
data-export-csv-selector="table"
24-
data-export-file-name="[[Cities]].csv"
25-
data-i18n-attr="alt, title, data-export-file-name">
26-
</div>
6+
<download-links file-name="Cities"></download-links>
277
</nav>
288

299
<json-data url="{rootApiUrl}/cities/:country/:region" url-params load-only-once>

examples/html/regions-web.htm

Lines changed: 1 addition & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -2,27 +2,7 @@ <h1><span>Regions for Country Code</span> <span url-param="country"></span></h1>
22

33
<nav>
44
<a href="#/en/" data-i18n="Countries" data-i18n-href></a>
5-
<div class="download-links ml-20">
6-
<span class="download" data-i18n="Download"></span>
7-
<img class="download"
8-
src="img/Excel.svg"
9-
height="16"
10-
width="16"
11-
alt="Download to Excel"
12-
title="Download to Excel"
13-
data-export-excel-selector="table"
14-
data-export-file-name="[[Regions]].xlsx"
15-
data-i18n-attr="alt, title, data-export-file-name">
16-
<img class="download"
17-
src="img/CSV.svg"
18-
height="16"
19-
width="16"
20-
alt="Download to CSV"
21-
title="Download to CSV"
22-
data-export-csv-selector="table"
23-
data-export-file-name="[[Regions]].csv"
24-
data-i18n-attr="alt, title, data-export-file-name">
25-
</div>
5+
<download-links file-name="Regions"></download-links>
266
</nav>
277

288
<!--

examples/html/search-places-web.htm

Lines changed: 1 addition & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -77,25 +77,7 @@ <h2><span data-bind="cities.length"></span> <span data-i18n="Cities Found"></spa
7777

7878
<div data-show="typeof cities === 'object' && cities.length > 0">
7979
<nav style="display: inline-flex;">
80-
<span class="download" data-i18n="Download" style="margin-left: 0;"></span>
81-
<img class="download"
82-
src="img/Excel.svg"
83-
height="16"
84-
width="16"
85-
alt="Download to Excel"
86-
title="Download to Excel"
87-
data-export-excel-selector="table"
88-
data-export-file-name="[[Search]].xlsx"
89-
data-i18n-attr="alt, title, data-export-file-name">
90-
<img class="download"
91-
src="img/CSV.svg"
92-
height="16"
93-
width="16"
94-
alt="Download to CSV"
95-
title="Download to CSV"
96-
data-export-csv-selector="table"
97-
data-export-file-name="[[Search]].csv"
98-
data-i18n-attr="alt, title, data-export-file-name">
80+
<download-links file-name="Search" margin-left="false"></download-links>
9981
</nav>
10082
</div>
10183

examples/places-demo-web.htm

Lines changed: 95 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -89,27 +89,11 @@ <h1 data-i18n="Countries"></h1>
8989
<nav>
9090
<a href="#/en/search" data-i18n="Search" data-i18n-href></a>
9191
<a href="#/en/info" data-i18n="Info" data-i18n-href></a>
92-
<div class="download-links ml-20">
93-
<span class="download" data-i18n="Download"></span>
94-
<img class="download"
95-
src="img/Excel.svg"
96-
height="16"
97-
width="16"
98-
alt="Download to Excel"
99-
title="Download to Excel"
100-
data-export-excel-selector="table"
101-
data-export-file-name="[[Countries]].xlsx"
102-
data-i18n-attr="alt, title, data-export-file-name">
103-
<img class="download"
104-
src="img/CSV.svg"
105-
height="16"
106-
width="16"
107-
alt="Download to CSV"
108-
title="Download to CSV"
109-
data-export-csv-selector="table"
110-
data-export-file-name="[[Countries]].csv"
111-
data-i18n-attr="alt, title, data-export-file-name">
112-
</div>
92+
<!--
93+
<download-links> is a custom Web Component defined
94+
for this app near the bottom of this file.
95+
-->
96+
<download-links file-name="Countries"></download-links>
11397
</nav>
11498

11599
<json-data url="{rootApiUrl}/countries" load-only-once onready="showMessage('Countries List is ready')">
@@ -336,5 +320,95 @@ <h1 data-i18n="Countries"></h1>
336320
});
337321
})();
338322
</script>
323+
324+
<!--
325+
Define Custom Web Component <download-links>
326+
327+
This uses the DataFormsJS Class `Component` which was created so that
328+
custom Web Components can be quickly defined for an app.
329+
-->
330+
<script type="module">
331+
// import { Component, html } from 'https://cdn.jsdelivr.net/npm/dataformsjs@5/js/web-components/Component.min.js';
332+
import { Component, html } from '../js/web-components/Component.js';
333+
334+
// Define class for the <download-links> element
335+
class DownloadLinks extends Component {
336+
// Props end up being assigned to both HTML observable attributes
337+
// and JavaScript properties. Example usage:
338+
// document.querySelector('download-links').fileName = 'Report';
339+
// document.querySelector('download-links').setAttribute('file-name', 'Report');
340+
static get props() {
341+
return {
342+
fileName: null,
343+
marginLeft: true,
344+
}
345+
}
346+
347+
render() {
348+
return html`
349+
<div class="download-links ${this.marginLeft ? 'ml-20' : ''}">
350+
<span class="download">${window.i18nText('Download')}</span>
351+
<img class="download"
352+
src="img/Excel.svg"
353+
height="16"
354+
width="16"
355+
alt="${window.i18nText('Download to Excel')}"
356+
title="${window.i18nText('Download to Excel')}"
357+
data-export-excel-selector="table"
358+
data-export-file-name="${window.i18nText(this.fileName)}.xlsx">
359+
<img class="download"
360+
src="img/CSV.svg"
361+
height="16"
362+
width="16"
363+
alt="${window.i18nText('Download to CSV')}"
364+
title="${window.i18nText('Download to CSV')}"
365+
data-export-csv-selector="table"
366+
data-export-file-name="${window.i18nText(this.fileName)}.csv">
367+
</div>
368+
`;
369+
}
370+
}
371+
372+
// Add <download-links> element to the page
373+
window.customElements.define('download-links', DownloadLinks);
374+
</script>
375+
<script nomodule>
376+
// Only modern browsers support Web Components so for IE and older browsers
377+
// handle the document event 'app:routeChanged' which gets triggered from
378+
// [polyfill.js] and then manually render HTML for the Web Component.
379+
(function() {
380+
function displayDownloadLinks() {
381+
var element = document.querySelector('download-links');
382+
if (element === null) {
383+
return;
384+
}
385+
386+
var fileName = element.getAttribute('file-name');
387+
var marginLeft = !(element.getAttribute('margin-left') === 'false');
388+
var html = '<div class="download-links ' + (marginLeft ? 'ml-20' : '') + '">';
389+
html += '<span class="download">' + window.i18nText('Download') + '</span>';
390+
html += '<img class="download"';
391+
html += ' src="img/Excel.svg"';
392+
html += ' height="16"';
393+
html += ' width="16"';
394+
html += ' alt="' + window.i18nText('Download to Excel') + '"';
395+
html += ' title="' + window.i18nText('Download to Excel') + '"';
396+
html += ' data-export-excel-selector="table"';
397+
html += ' data-export-file-name="' + window.i18nText(fileName) + '.xlsx">';
398+
html += '<img class="download"';
399+
html += ' src="img/CSV.svg"';
400+
html += ' height="16"';
401+
html += ' width="16"';
402+
html += ' alt="' + window.i18nText('Download to CSV') + '"';
403+
html += ' title="' + window.i18nText('Download to CSV') + '"';
404+
html += ' data-export-csv-selector="table"';
405+
html += ' data-export-file-name="' + window.i18nText(fileName) + '.csv">';
406+
html += '</div>';
407+
element.innerHTML = html;
408+
}
409+
410+
document.addEventListener('app:routeChanged', displayDownloadLinks);
411+
})();
412+
</script>
339413
</body>
340414
</html>

js/web-components/Component.js

Lines changed: 42 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -99,14 +99,54 @@ export class Component extends HTMLElement {
9999
}
100100

101101
static get observedAttributes() {
102-
return (this.props === undefined ? [] : Object.keys(this.props));
102+
if (this.props === undefined) {
103+
return [];
104+
}
105+
const attr = [];
106+
for (const prop of Object.keys(this.props)) {
107+
attr.push(prop);
108+
// If a [camelCased] property is defined then add HTML attribute
109+
// support for a dashed-version of the property.
110+
// Example: `fileName` = `file-name`
111+
// Regex from:
112+
// https://stackoverflow.com/a/47836484/3422084
113+
const dashed = prop.replace(/[A-Z]/g, m => "-" + m.toLowerCase());
114+
if (prop !== dashed) {
115+
attr.push(dashed);
116+
}
117+
}
118+
return attr;
103119
}
104120

105121
attributeChangedCallback(attrName, oldValue, newValue) {
122+
function stringToValue(value) {
123+
switch (value) {
124+
case 'true':
125+
case '': // Empty values default to `true`
126+
return true;
127+
case 'false':
128+
return false;
129+
case 'null':
130+
return null;
131+
default:
132+
return value;
133+
}
134+
}
135+
106136
const props = this.constructor.props;
107137
if (props && props[attrName] !== undefined && oldValue !== newValue) {
108-
this.state[attrName] = newValue;
138+
this.state[attrName] = stringToValue(newValue);
109139
this.update();
140+
} else if (attrName.includes('-')) {
141+
// Convert from dashed-case to camelCased.
142+
// Example: `file-name` to `fileName`
143+
// Regex from:
144+
// https://stackoverflow.com/a/6661012/3422084
145+
const camelCased = attrName.replace(/-([a-z])/g, m => m[1].toUpperCase());
146+
if (props && props[camelCased] !== undefined && oldValue !== newValue) {
147+
this.state[camelCased] = stringToValue(newValue);
148+
this.update();
149+
}
110150
}
111151
}
112152

js/web-components/Component.min.js

Lines changed: 1 addition & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

0 commit comments

Comments
 (0)