From fbc20ebc37f119ad6a0c2461fe6d5a325231ccbf Mon Sep 17 00:00:00 2001 From: AdriSolid Date: Fri, 12 Mar 2021 16:28:53 +0100 Subject: [PATCH] fix: update htmlForFeature in skeleton (#215) --- CHANGELOG.md | 1 + .../template/src/utils/htmlForFeature.js | 137 +++++++++++++++++- 2 files changed, 131 insertions(+), 7 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 9a0b57b3..d287aabd 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,6 +1,7 @@ # CHANGELOG ## Not released +- Add improved htmlForFeature in skeleton [#215](https://github.com/CartoDB/carto-react-template/pull/215) ## 1.0.0-rc.1 (2021-03-11) - Add cypress e2e tests in CI [#194](https://github.com/CartoDB/carto-react-template/pull/194) diff --git a/template-skeleton/template/src/utils/htmlForFeature.js b/template-skeleton/template/src/utils/htmlForFeature.js index c23a21ef..412c8e45 100644 --- a/template-skeleton/template/src/utils/htmlForFeature.js +++ b/template-skeleton/template/src/utils/htmlForFeature.js @@ -1,10 +1,133 @@ -export default function htmlForFeature(feature) { +import { currencyFormatter, numberFormatter } from 'utils/formatter'; + +const FORMATTER_TYPES = Object.freeze({ + CURRENCY: 'currency', + NUMBER: 'number', +}); + +const formatterFunctions = { + [FORMATTER_TYPES.CURRENCY](value) { + const formatted = currencyFormatter(value); + return `${formatted.prefix}${formatted.value}`; + }, + [FORMATTER_TYPES.NUMBER](value) { + return numberFormatter(value); + }, +}; + +const DEFAULT_FORMATTER = { + type: '', + columns: [], +}; + +export default function htmlForFeature({ + title, + feature, + formatter = DEFAULT_FORMATTER, + includeColumns = '*', + showColumnName = true, +}) { + if (!feature) { + throw new Error(`htmlForFeature needs "info.object" information`); + } + + const propertyNames = Object.keys(feature.properties); + + if ( + formatter?.type && + formatter?.columns && + !isFormatterValid(propertyNames, formatter) + ) { + return; + } + + if (!includedColumnsAreValid(propertyNames, includeColumns)) { + return; + } + let html = ''; - Object.keys(feature.properties).forEach((propertyName) => { - if (propertyName === 'layerName') return; - html = html.concat( - `${propertyName}: ${feature.properties[propertyName]}
` - ); - }); + + if (title) { + html = `

${title}

`; + } + + for (const name of propertyNames) { + if ( + name !== 'layerName' && + (includeColumns.includes(name) || includeColumns === '*') + ) { + if (formatter?.columns.includes(name)) { + const formatterFunction = formatterFunctions[formatter.type]; + html = generateHtml(feature, name, showColumnName, html, formatterFunction); + } else { + html = generateHtml(feature, name, showColumnName, html); + } + } + } + return html; } + +function generateHtml( + feature, + propertyName, + showColumnName, + html, + formatterFunction = (v) => v +) { + return html.concat( + `${showColumnName ? `${propertyName}: ` : ''}${formatterFunction( + feature.properties[propertyName] + )}
` + ); +} + +function isFormatterValid(properties, formatter) { + const supportedTypes = Object.values(FORMATTER_TYPES); + + if (!supportedTypes.includes(formatter.type)) { + throw new Error( + `"${formatter.type}" is not supported as formatter, use one of "${supportedTypes}"` + ); + } + + if (!isArrayOfStrings(formatter.columns)) { + throw new Error(`"formatter.columns" property needs to be an array of strings`); + } + + for (const column of formatter.columns) { + if (!properties.includes(column)) { + throw new Error( + `"formatted.columns" property needs to be an array of existing feature columns` + ); + } + } + + return true; +} + +function includedColumnsAreValid(properties, includeColumns) { + if (includeColumns === '*') { + return true; + } + + if (!isArrayOfStrings(includeColumns)) { + throw new Error( + `"includeColumns" property needs to be an array of existing feature columns or "*"` + ); + } + + if (isArrayOfStrings(includeColumns)) { + for (const column of includeColumns) { + if (!properties.includes(column)) { + throw new Error('colums set in "includeColumns" should exist in picked feature'); + } + } + } + + return true; +} + +function isArrayOfStrings(value) { + return Array.isArray(value) && value.length && value.every(String); +}