minor refactors
authorJustin Wind <justin.wind+git@gmail.com>
Fri, 22 Mar 2024 23:15:40 +0000 (16:15 -0700)
committerJustin Wind <justin.wind+git@gmail.com>
Fri, 22 Mar 2024 23:15:40 +0000 (16:15 -0700)
README.md
lib/template-helper.js
test/lib/template-helper.js

index 888bd544321116c86bff3f8a0b38db7716f84fa9..68f91ea85a20c2247666df5b4c20f1820cc03420 100644 (file)
--- a/README.md
+++ b/README.md
@@ -4,6 +4,8 @@ Simplistic helpers for rendering HTML and boilerplate.
 
 Specific to Squeep Framework Applications, this module has strong opinions and makes many assumptions.
 
+Expects `/static/theme.css` and `/static/custom.css` to exist.
+
 ## API
 
 - `initContext(ctx)`
@@ -41,4 +43,4 @@ Lists of errors and notices to show at top of page, before main content.
 
 ## Tests
 
-`LintHtml` class is a simple wrapper around html-validate package which can report issues with generated html strings.
+`LintHtml` class is a simple wrapper around `html-validate` package which can report issues with generated html strings.
index 7f7021ae010f2a58be394f17e23ea4120a4570d7..e8163abbe41733c0e6af0293b49cf22102a98178 100644 (file)
@@ -19,7 +19,7 @@ const initContext = (ctx) => {
 
 /**
  * Some fields may have values outside normal dates, handle them here.
- * @param {Date} date
+ * @param {Date|Number} date
  * @param {String} otherwise
  */
 const dateOrNot = (date, otherwise) => {
@@ -203,7 +203,8 @@ ${htmlFooter(ctx, options)}
  * @returns {String}
  */
 function renderNavLink(nav) {
-  return `<a href="${nav.href}"${nav.class ? (' class="' + nav.class + '"') : ''}>${nav.text}</a>`;
+  const aClass = nav.class ? ` class="${nav.class}"` : '';
+  return `<a href="${nav.href}"${aClass}>${nav.text}</a>`;
 }
 
 
@@ -277,7 +278,10 @@ ${spacer}</footer>` : '';
  * @returns {String}
  */
 function elementAttributes(attributes) {
-  const attr = Object.entries(attributes).map(([name, value]) => `${name}="${value}"`).join(' ');
+  const attr = Object.entries(attributes).map(([name, value]) => {
+    const v = value ? `="${value}"` : '';
+    return `${name}${v}`;
+  }).join(' ');
   return attr ? ' ' + attr : '';
 }
 
@@ -295,19 +299,33 @@ function LI(item, indent = 0, attributes = {}) {
 }
 
 
+/**
+ * Wrap an array of items in a list container element.
+ * @param {String} element
+ * @param {Number} indent
+ * @param {Object} attributes
+ * @param {String[]} items
+ * @param {(item, index, array) => {Object}} itemAttributeGenerator
+ * @returns {String}
+ */
+function listContainer(element, indent, attributes, items, itemAttributeGenerator) {
+  const spacer = '\t'.repeat(indent);
+  return `${spacer}<${element}${elementAttributes(attributes)}>
+${items.map((item, index, array) => LI(item, indent + 1, itemAttributeGenerator(item, index, array))).join('\n')}
+${spacer}</${element}>`;
+}
+
+
 /**
  * Wrap a list of items in an unordered list.
  * @param {String[]} items
  * @param {Number} indent
  * @param {Object} attributes
- * @param {(item) => Object} itemAttributeGenerator
+ * @param {(item, index, array) => Object} itemAttributeGenerator
  * @returns {String}
  */
 function UL(items, indent = 0, attributes = {}, itemAttributeGenerator = () => {}) {
-  const spacer = '\t'.repeat(indent);
-  return `${spacer}<ul${elementAttributes(attributes)}>
-${items.map((item) => LI(item, indent + 1, itemAttributeGenerator(item))).join('\n')}
-${spacer}</ul>`;
+  return listContainer('ul', indent, attributes, items, itemAttributeGenerator);
 }
 
 
@@ -316,15 +334,12 @@ ${spacer}</ul>`;
  * @param {String[]} items
  * @param {Number} indent
  * @param {Object} attributes
- * @param {(item) => Object} itemAttributeGenerator
+ * @param {(item, index, array) => Object} itemAttributeGenerator
  * @returns {String}
  */
 
 function OL(items, indent = 0, attributes = {}, itemAttributeGenerator = () => {}) {
-  const spacer = '\t'.repeat(indent);
-  return `${spacer}<ol${elementAttributes(attributes)}>
-${items.map((item) => LI(item, indent + 1, itemAttributeGenerator(item))).join('\n')}
-${spacer}</ol>`;
+  return listContainer('ol', indent, attributes, items, itemAttributeGenerator);
 }
 
 
@@ -404,7 +419,9 @@ module.exports = {
   indented,
   renderNavLink,
   LI,
+  listContainer,
   UL,
   OL,
   htmlPage,
+  elementAttributes,
 };
index 75481adfee0b010330cc89a39656fd7f28e862ef..c62a3c01161be58bbc283c057bf75240312f941f 100644 (file)
@@ -170,6 +170,17 @@ describe('Template Helper', function () {
     });
   }); // renderNavLink
 
+  describe('elementAttributes', function () {
+    it('covers', function () {
+      const attributes = {
+        class: 'foo bar',
+        disabled: '',
+      };
+      const result = th.elementAttributes(attributes);
+      assert.strictEqual(result, ' class="foo bar" disabled');
+    });
+  }); // elementAttributes
+
   describe('OL', function () {
     it('covers', function () {
       const result = th.OL(['item', 'another item'], 1, { class: 'class' }, (item) => ({ class: `${item}-class` }));
@@ -273,6 +284,7 @@ describe('Template Helper', function () {
       ];
     });
     it('covers', async function () {
+      this.slow(1000); // First invocation of htmlLint takes some time.
       const result = th.htmlPage(pagePathLevel, ctx, options, main);
       await htmlLint(result);
       assert(result);