From b6b48e8349bea5e371472ae8b1d59d8e719c1c43 Mon Sep 17 00:00:00 2001 From: Wyatt Pearsall Date: Tue, 10 Oct 2023 21:55:00 -0400 Subject: [PATCH 1/2] Trim and tweak simple-chart code to work with cct --- .../css/on-demand/simple-chart.less | 10 + .../on-demand/simple-chart/chart-hooks.js | 10 + .../on-demand/simple-chart/data-filters.js | 283 ------------------ .../on-demand/simple-chart/simple-chart.js | 3 - .../on-demand/simple-chart/tilemap-chart.js | 87 +----- .../js/routes/on-demand/simple-chart/utils.js | 1 - .../v1/includes/organisms/simple-chart.html | 2 +- 7 files changed, 27 insertions(+), 369 deletions(-) delete mode 100644 cfgov/unprocessed/js/routes/on-demand/simple-chart/data-filters.js diff --git a/cfgov/unprocessed/css/on-demand/simple-chart.less b/cfgov/unprocessed/css/on-demand/simple-chart.less index fc715361c86..f062e6940f5 100644 --- a/cfgov/unprocessed/css/on-demand/simple-chart.less +++ b/cfgov/unprocessed/css/on-demand/simple-chart.less @@ -260,6 +260,16 @@ margin-top: unit(45 / @size-vi, em); padding-top: unit(15 / @size-vi, em); } + + .cct { + .highcharts-series-0 .highcharts-graph { + stroke-width: 3px; + } + + .highcharts-series-1 .highcharts-graph { + stroke-width: 1px; + } + } } .highcharts-legend-item-hidden * { diff --git a/cfgov/unprocessed/js/routes/on-demand/simple-chart/chart-hooks.js b/cfgov/unprocessed/js/routes/on-demand/simple-chart/chart-hooks.js index 8372606bf66..bb30c130caf 100644 --- a/cfgov/unprocessed/js/routes/on-demand/simple-chart/chart-hooks.js +++ b/cfgov/unprocessed/js/routes/on-demand/simple-chart/chart-hooks.js @@ -24,6 +24,16 @@ const hooks = { })); }, + cct_yoy_transform(d) { + return d['Number of Loans'].map((v, i) => { + return { + x: v[0], + loans: v[1] * 100, + volume: d['Dollar Volume'][i][1] * 100, + }; + }); + }, + getDateString(x) { return new Date(x).toLocaleDateString('en-US', { month: 'short', diff --git a/cfgov/unprocessed/js/routes/on-demand/simple-chart/data-filters.js b/cfgov/unprocessed/js/routes/on-demand/simple-chart/data-filters.js deleted file mode 100644 index 0f2e6c572e8..00000000000 --- a/cfgov/unprocessed/js/routes/on-demand/simple-chart/data-filters.js +++ /dev/null @@ -1,283 +0,0 @@ -import chartHooks from './chart-hooks.js'; -import { extractSeries, overrideStyles } from './utils.js'; - -/** - * Generates an array of filters, bucketed based on key if present - * @param {object} filter - Object with a filter key and possible label - * @param {object} data - The raw chart data - * @param {boolean} isDate - Whether the data should be stored as JS dates - * @returns {Array} All the buckets of data - */ -function getDataBuckets(filter, data, isDate) { - const vals = {}; - const key = filter.key ? filter.key : filter; - data.forEach((d) => { - let item = d[key]; - if (!Array.isArray(item)) item = [item]; - item.forEach((v) => { - vals[v] = 1; - }); - }); - - const options = Object.keys(vals); - if (isDate) return options.map((v) => Number(new Date(v))); - return options; -} - -/** - * @param {number} option - The JS-date formatted option - * @returns {string} Specially formatted date - */ -function processDate(option) { - const [quarter, year] = chartHooks.cci_dateToQuarter(option); - return `${quarter} ${year}`; -} - -/** - * @param {Array} options - List of options to build for the select component - * @param {object} chartNode - The DOM node of the current chart - * @param {object} filter - key and possible label to filter on - * @returns {object} the built select DOM node - */ -function makeSelectFilterDOM(options, chartNode, filter) { - const id = Math.random() + filter.key; - const attachPoint = chartNode.getElementsByClassName( - 'o-simple-chart_filters', - )[0]; - - const wrapper = document.createElement('div'); - wrapper.className = 'filter-wrapper m-form-field m-form-field__select'; - - const label = document.createElement('label'); - label.className = 'a-label a-label__heading'; - label.innerText = filter.label ? filter.label : 'Select ' + filter.key; - label.htmlFor = id; - - const selectDiv = document.createElement('div'); - selectDiv.className = 'a-select'; - - const select = document.createElement('select'); - select.id = id; - - /* Explicitly pass "all" key as part of filter */ - if (filter.all) { - const allOpt = document.createElement('option'); - allOpt.value = 'View all'; - allOpt.innerText = 'View all'; - select.appendChild(allOpt); - } - - options.forEach((option) => { - const opt = document.createElement('option'); - opt.value = option; - - if (filter.key === 'tilemap') opt.innerText = processDate(option); - else opt.innerText = option; - - select.appendChild(opt); - }); - - selectDiv.appendChild(select); - wrapper.appendChild(label); - wrapper.appendChild(selectDiv); - attachPoint.appendChild(wrapper); - - const selector = { - nodes: [select], - filterProp: filter.key, - value: select.value, - attach(filterFn) { - select.addEventListener('change', () => { - selector.value = select.value; - filterFn(); - }); - filterFn(); - }, - }; - - return selector; -} - -/** - * @param {Array} buckets - List of buckets to build radio inputs from - * @param {object} chartNode - The DOM node of the current chart - * @param {object} filter - key and possible label to filter on - * @returns {object} the built select DOM node - */ -function makeRadioFilterDOM(buckets, chartNode, filter) { - const attachPoint = chartNode.getElementsByClassName( - 'o-simple-chart_filters', - )[0]; - const radios = []; - - const wrapper = document.createElement('div'); - wrapper.className = 'filter-wrapper'; - - const bucketLabel = document.createElement('h4'); - bucketLabel.innerText = filter.label ? filter.label : 'Select ' + filter.key; - - wrapper.appendChild(bucketLabel); - - /** - * @param {string} bucket - The bucket on which to filter data - * @param {number} i - Calling order - */ - function makeRadioGroup(bucket, i) { - const id = Math.random() + bucket; - const radioWrapper = document.createElement('div'); - radioWrapper.className = 'm-form-field m-form-field__radio u-mb5'; - let radioGroupName = document.querySelectorAll('.filter-wrapper').length; - radioGroupName = 'radio-group_' + radioGroupName; - - const input = document.createElement('input'); - input.className = 'a-radio'; - input.type = 'radio'; - input.id = id; - input.value = bucket; - input.name = radioGroupName; - if (i === 0) input.checked = true; - - const label = document.createElement('label'); - label.className = 'a-label'; - label.htmlFor = id; - label.innerText = bucket; - - radioWrapper.appendChild(input); - radioWrapper.appendChild(label); - wrapper.appendChild(radioWrapper); - - radios.push(input); - } - - /* Explicitly pass "all" key as part of filter */ - if (filter.all) { - buckets.unshift('View all'); - } - - buckets.forEach(makeRadioGroup); - - attachPoint.appendChild(wrapper); - - const selector = { - nodes: radios, - filterProp: filter.key, - value: radios[0].value, - attach(filterFn) { - radios.forEach((r) => { - r.addEventListener('change', () => { - selector.value = r.value; - filterFn(); - }); - }); - filterFn(); - }, - }; - - return selector; -} - -/** - * Filters raw or transformed data by a select prop. - * @param {Array} data - Transformed or raw chart data. - * @param {object} filterProp - Key on which to filter. - * @param {object} filterVal - Value of the selectNode against - * which we're filtering. - * @returns {Array} Filtered chart data. - */ -function filterData(data, filterProp, filterVal) { - if (filterVal === 'View all') return data; - - return data.filter((d) => { - const match = d[filterProp]; - if (Array.isArray(match)) return match.indexOf(filterVal) >= 0; - return match === filterVal; - }); -} - -/** - * Wires up filter elements when provided filters - * @param {object} dataAttributes - Data passed via data-* tags - * @param {object} chartNode - The DOM node of the current chart - * @param {object} chart - The initialized chart - * @param {object} data - The chart data object, {raw, series, transformed} - */ -function initFilters(dataAttributes, chartNode, chart, data) { - let filters = dataAttributes.filters; - if (!filters) return; - // Allow plain Wagtail strings - if (!filters.match('{') && !filters.match('"')) { - filters = `"${filters}"`; - } - - const rawOrTransformed = data.transformed || data.raw; - - try { - filters = JSON.parse(filters); - if (!Array.isArray(filters)) filters = [filters]; - - const selectors = []; - - filters.forEach((filter) => { - const buckets = getDataBuckets(filter, rawOrTransformed); - if (buckets.length < 6) { - selectors.push(makeRadioFilterDOM(buckets, chartNode, filter)); - } else { - selectors.push(makeSelectFilterDOM(buckets, chartNode, filter)); - } - }); - - if (selectors.length) { - attachFilters(selectors, chart, dataAttributes, rawOrTransformed); - } - } catch (err) { - /* eslint-disable-next-line no-console */ - console.error(err, 'Bad JSON in chart filters ', filters); - } -} - -/** - * @param {object} selectors - List of selectors that need to be run - * @param {object} chart - The Highcharts chart object - * @param {object} dataAttributes - Data passed via data-* tags - * @param {object} data - Chart data, either raw or transformed - */ -function attachFilters(selectors, chart, dataAttributes, data) { - const { styleOverrides } = dataAttributes; - - /** - * Filter data and update the chart on select - */ - function filterOnSelect() { - // filter on all selects - let filtered = data; - - for (let i = 0; i < selectors.length; i++) { - const selector = selectors[i]; - const { filterProp, value } = selector; - filtered = filterData(filtered, filterProp, value); - } - - const filteredSeries = extractSeries(filtered, dataAttributes); - filteredSeries.forEach((dataSeries) => { - chart.series.forEach((chartSeries) => { - if (dataSeries.name === chartSeries.name) { - chartSeries.setData(dataSeries.data, false); - } - }); - }); - - const obj = {}; - - if (styleOverrides && styleOverrides.match('hook__')) { - overrideStyles(styleOverrides, obj, filtered); - } - - chart.update(obj); - } - - selectors.forEach((selector) => { - selector.attach(filterOnSelect); - }); -} - -export { initFilters, makeSelectFilterDOM }; diff --git a/cfgov/unprocessed/js/routes/on-demand/simple-chart/simple-chart.js b/cfgov/unprocessed/js/routes/on-demand/simple-chart/simple-chart.js index ab56559aed7..e06a6d1c74d 100644 --- a/cfgov/unprocessed/js/routes/on-demand/simple-chart/simple-chart.js +++ b/cfgov/unprocessed/js/routes/on-demand/simple-chart/simple-chart.js @@ -14,7 +14,6 @@ import { makeFormatter, overrideStyles, } from './utils.js'; -import { initFilters } from './data-filters.js'; import { convertEpochToDateString } from './utils'; accessibility(Highcharts); @@ -296,8 +295,6 @@ function buildChart(chartNode) { chart = tilemapChart.init(chartNode, target, data, dataAttributes); } else { chart = Highcharts.chart(target, makeChartOptions(data, dataAttributes)); - - initFilters(dataAttributes, chartNode, chart, data); } // Make sure chart is displayed properly on print diff --git a/cfgov/unprocessed/js/routes/on-demand/simple-chart/tilemap-chart.js b/cfgov/unprocessed/js/routes/on-demand/simple-chart/tilemap-chart.js index 7e1ae359406..b4e5be434f2 100644 --- a/cfgov/unprocessed/js/routes/on-demand/simple-chart/tilemap-chart.js +++ b/cfgov/unprocessed/js/routes/on-demand/simple-chart/tilemap-chart.js @@ -1,10 +1,8 @@ import Highmaps from 'highcharts/highmaps'; import tilemap from 'highcharts/modules/tilemap'; import cloneDeep from 'lodash.clonedeep'; -import chartHooks from './chart-hooks.js'; import defaultTilemap from './tilemap-styles.js'; import usLayout from './us-layout.js'; -import { makeSelectFilterDOM } from './data-filters.js'; import { alignMargin, formatSeries, overrideStyles } from './utils.js'; tilemap(Highmaps); @@ -54,58 +52,6 @@ function makeTilemapOptions(data, dataAttributes) { return defaultObj; } -/** - * Builds the tilemap filter DOM - * @param {object} chartNode - The node where the chart lives - * @param {object} chart - The chart object - * @param {object} data - The data object - * @param {object} transform - Whether data has been transformed - */ -function makeTilemapSelect(chartNode, chart, data, transform) { - let d; - if (transform) d = data.transformed; - else d = data.raw; - - const options = getTilemapDates(d); - const selectNode = makeSelectFilterDOM(options, chartNode, { - key: 'tilemap', - label: 'Select date', - }).nodes[0]; - - attachTilemapFilter(selectNode, chart, data); -} - -/** - * Extracts all dates from an object/csv formatted for tilemap display - * @param {object} data - The data object - * @returns {Array} Extracted dates - */ -function getTilemapDates(data) { - return Object.keys(data[0]) - .filter((k) => !isNaN(new Date(k))) - .sort((a, b) => new Date(b) - new Date(a)); -} - -/** - * Wires up the tilemap filter - * @param {object} select - The select node - * @param {object} chart - The chart object - * @param {object} data - The data object - */ -function attachTilemapFilter(select, chart, data) { - select.addEventListener('change', (evt) => { - const formatted = formatSeries(data); - const updated = getMapConfig(formatted, evt.target.value); - chart.update(updated); - const updatedTitleObj = chart.options.yAxis[0].title; - updateTilemapLegend( - chart.renderTo, - updated, - updatedTitleObj ? updatedTitleObj.text : '', - ); - }); -} - /** * Makes a legend for the tilemap * @param {object} node - The chart node @@ -141,40 +87,21 @@ function updateTilemapLegend(node, data, legendTitle) { } /** - * Intuits the correct object key for state short codes - * @param {object} data - A row of data as an object with headers as keys - * @returns {string} The intuited shortcode - */ -function getShortCode(data) { - const keys = Object.keys(data); - for (let i = 0; i < keys.length; i++) { - if (usLayout[data[keys[i]]]) return keys[i]; - } - /* eslint-disable-next-line no-console */ - return console.error( - 'Unable to determine state shortcode. Data is misformatted for simple-chart.', - ); -} - -/** - * Adds generates a config object to be added to the chart config + * Generates a config object to be added to the chart config * @param {Array} series - The formatted series data - * @param {string} date - The date to use * @returns {Array} series data with a geographic component added */ -function getMapConfig(series, date) { +function getMapConfig(series) { let min = Infinity; let max = -Infinity; const data = series[0].data; - const shortCode = getShortCode(data[0]); - if (!date) date = getTilemapDates(data)[0]; const added = data.map((v) => { - const val = Math.round(Number(v[date]) * 100) / 100; + const val = Math.round(Number(v.value) * 100) / 100; if (val <= min) min = val; if (val >= max) max = val; return { - ...usLayout[v[shortCode]], - state: v[shortCode], + ...usLayout[v.name], + state: v.name, value: val, }; }); @@ -229,7 +156,7 @@ function getMapConfig(series, date) { * @returns {object} The initialized chart object */ function init(chartNode, target, data, dataAttributes) { - const { transform, yAxisLabel } = dataAttributes; + const { yAxisLabel } = dataAttributes; const tilemapOptions = makeTilemapOptions(data, dataAttributes); const chart = Highmaps.mapChart(target, tilemapOptions); @@ -239,8 +166,6 @@ function init(chartNode, target, data, dataAttributes) { legend.style.display = 'block'; updateTilemapLegend(target, tilemapOptions, yAxisLabel); - makeTilemapSelect(chartNode, chart, data, transform && chartHooks[transform]); - /** * Fixes tilemap clipping */ diff --git a/cfgov/unprocessed/js/routes/on-demand/simple-chart/utils.js b/cfgov/unprocessed/js/routes/on-demand/simple-chart/utils.js index 602563316ff..1102c38f04e 100644 --- a/cfgov/unprocessed/js/routes/on-demand/simple-chart/utils.js +++ b/cfgov/unprocessed/js/routes/on-demand/simple-chart/utils.js @@ -141,7 +141,6 @@ function extractSeries(rawData, { series, xAxisSource, chartType }) { name, data: currArr, }; - rawData.forEach((obj) => { let d = Number(obj[key]); if (chartType === 'datetime') { diff --git a/cfgov/v1/jinja2/v1/includes/organisms/simple-chart.html b/cfgov/v1/jinja2/v1/includes/organisms/simple-chart.html index 99117ddb4ce..6ff59ffe3ee 100644 --- a/cfgov/v1/jinja2/v1/includes/organisms/simple-chart.html +++ b/cfgov/v1/jinja2/v1/includes/organisms/simple-chart.html @@ -30,7 +30,7 @@
Figure {{ value.figure_number }}
{% endif %} {% if value.title %} -

{{ value.title }}

+

{{ value.title }}

{% endif %} {% if value.subtitle %} From 8b58360eff183483f7660ef4807383cec1225c94 Mon Sep 17 00:00:00 2001 From: Wyatt Pearsall Date: Wed, 11 Oct 2023 07:35:53 -0400 Subject: [PATCH 2/2] Re-add general filters --- .../on-demand/simple-chart/data-filters.js | 283 ++++++++++++++++++ .../on-demand/simple-chart/simple-chart.js | 4 +- 2 files changed, 286 insertions(+), 1 deletion(-) create mode 100644 cfgov/unprocessed/js/routes/on-demand/simple-chart/data-filters.js diff --git a/cfgov/unprocessed/js/routes/on-demand/simple-chart/data-filters.js b/cfgov/unprocessed/js/routes/on-demand/simple-chart/data-filters.js new file mode 100644 index 00000000000..b42e7876e30 --- /dev/null +++ b/cfgov/unprocessed/js/routes/on-demand/simple-chart/data-filters.js @@ -0,0 +1,283 @@ +import chartHooks from './chart-hooks.js'; +import { extractSeries, overrideStyles } from './utils.js'; + +/** + * Generates an array of filters, bucketed based on key if present + * @param {object} filter - Object with a filter key and possible label + * @param {object} data - The raw chart data + * @param {boolean} isDate - Whether the data should be stored as JS dates + * @returns {Array} All the buckets of data + */ +function getDataBuckets(filter, data, isDate) { + const vals = {}; + const key = filter.key ? filter.key : filter; + data.forEach((d) => { + let item = d[key]; + if (!Array.isArray(item)) item = [item]; + item.forEach((v) => { + vals[v] = 1; + }); + }); + + const options = Object.keys(vals); + if (isDate) return options.map((v) => Number(new Date(v))); + return options; +} + +/** + * @param {number} option - The JS-date formatted option + * @returns {string} Specially formatted date + */ +function processDate(option) { + const [quarter, year] = chartHooks.cci_dateToQuarter(option); + return `${quarter} ${year}`; +} + +/** + * @param {Array} options - List of options to build for the select component + * @param {object} chartNode - The DOM node of the current chart + * @param {object} filter - key and possible label to filter on + * @returns {object} the built select DOM node + */ +function makeSelectFilterDOM(options, chartNode, filter) { + const id = Math.random() + filter.key; + const attachPoint = chartNode.getElementsByClassName( + 'o-simple-chart_filters', + )[0]; + + const wrapper = document.createElement('div'); + wrapper.className = 'filter-wrapper m-form-field m-form-field__select'; + + const label = document.createElement('label'); + label.className = 'a-label a-label__heading'; + label.innerText = filter.label ? filter.label : 'Select ' + filter.key; + label.htmlFor = id; + + const selectDiv = document.createElement('div'); + selectDiv.className = 'a-select'; + + const select = document.createElement('select'); + select.id = id; + + /* Explicitly pass "all" key as part of filter */ + if (filter.all) { + const allOpt = document.createElement('option'); + allOpt.value = 'View all'; + allOpt.innerText = 'View all'; + select.appendChild(allOpt); + } + + options.forEach((option) => { + const opt = document.createElement('option'); + opt.value = option; + + if (filter.key === 'tilemap') opt.innerText = processDate(option); + else opt.innerText = option; + + select.appendChild(opt); + }); + + selectDiv.appendChild(select); + wrapper.appendChild(label); + wrapper.appendChild(selectDiv); + attachPoint.appendChild(wrapper); + + const selector = { + nodes: [select], + filterProp: filter.key, + value: select.value, + attach(filterFn) { + select.addEventListener('change', () => { + selector.value = select.value; + filterFn(); + }); + filterFn(); + }, + }; + + return selector; +} + +/** + * @param {Array} buckets - List of buckets to build radio inputs from + * @param {object} chartNode - The DOM node of the current chart + * @param {object} filter - key and possible label to filter on + * @returns {object} the built select DOM node + */ +function makeRadioFilterDOM(buckets, chartNode, filter) { + const attachPoint = chartNode.getElementsByClassName( + 'o-simple-chart_filters', + )[0]; + const radios = []; + + const wrapper = document.createElement('div'); + wrapper.className = 'filter-wrapper'; + + const bucketLabel = document.createElement('h4'); + bucketLabel.innerText = filter.label ? filter.label : 'Select ' + filter.key; + + wrapper.appendChild(bucketLabel); + + /** + * @param {string} bucket - The bucket on which to filter data + * @param {number} i - Calling order + */ + function makeRadioGroup(bucket, i) { + const id = Math.random() + bucket; + const radioWrapper = document.createElement('div'); + radioWrapper.className = 'm-form-field m-form-field__radio u-mb5'; + let radioGroupName = document.querySelectorAll('.filter-wrapper').length; + radioGroupName = 'radio-group_' + radioGroupName; + + const input = document.createElement('input'); + input.className = 'a-radio'; + input.type = 'radio'; + input.id = id; + input.value = bucket; + input.name = radioGroupName; + if (i === 0) input.checked = true; + + const label = document.createElement('label'); + label.className = 'a-label'; + label.htmlFor = id; + label.innerText = bucket; + + radioWrapper.appendChild(input); + radioWrapper.appendChild(label); + wrapper.appendChild(radioWrapper); + + radios.push(input); + } + + /* Explicitly pass "all" key as part of filter */ + if (filter.all) { + buckets.unshift('View all'); + } + + buckets.forEach(makeRadioGroup); + + attachPoint.appendChild(wrapper); + + const selector = { + nodes: radios, + filterProp: filter.key, + value: radios[0].value, + attach(filterFn) { + radios.forEach((r) => { + r.addEventListener('change', () => { + selector.value = r.value; + filterFn(); + }); + }); + filterFn(); + }, + }; + + return selector; +} + +/** + * Filters raw or transformed data by a select prop. + * @param {Array} data - Transformed or raw chart data. + * @param {object} filterProp - Key on which to filter. + * @param {object} filterVal - Value of the selectNode against + * which we're filtering. + * @returns {Array} Filtered chart data. + */ +function filterData(data, filterProp, filterVal) { + if (filterVal === 'View all') return data; + + return data.filter((d) => { + const match = d[filterProp]; + if (Array.isArray(match)) return match.indexOf(filterVal) >= 0; + return match === filterVal; + }); +} + +/** + * Wires up filter elements when provided filters + * @param {object} dataAttributes - Data passed via data-* tags + * @param {object} chartNode - The DOM node of the current chart + * @param {object} chart - The initialized chart + * @param {object} data - The chart data object, {raw, series, transformed} + */ +function initFilters(dataAttributes, chartNode, chart, data) { + let filters = dataAttributes.filters; + if (!filters) return; + // Allow plain Wagtail strings + if (!filters.match('{') && !filters.match('"')) { + filters = `"${filters}"`; + } + + const rawOrTransformed = data.transformed || data.raw; + + try { + filters = JSON.parse(filters); + if (!Array.isArray(filters)) filters = [filters]; + + const selectors = []; + + filters.forEach((filter) => { + const buckets = getDataBuckets(filter, rawOrTransformed); + if (buckets.length < 6) { + selectors.push(makeRadioFilterDOM(buckets, chartNode, filter)); + } else { + selectors.push(makeSelectFilterDOM(buckets, chartNode, filter)); + } + }); + + if (selectors.length) { + attachFilters(selectors, chart, dataAttributes, rawOrTransformed); + } + } catch (err) { + /* eslint-disable-next-line no-console */ + console.error(err, 'Bad JSON in chart filters ', filters); + } +} + +/** + * @param {object} selectors - List of selectors that need to be run + * @param {object} chart - The Highcharts chart object + * @param {object} dataAttributes - Data passed via data-* tags + * @param {object} data - Chart data, either raw or transformed + */ +function attachFilters(selectors, chart, dataAttributes, data) { + const { styleOverrides } = dataAttributes; + + /** + * Filter data and update the chart on select + */ + function filterOnSelect() { + // filter on all selects + let filtered = data; + + for (let i = 0; i < selectors.length; i++) { + const selector = selectors[i]; + const { filterProp, value } = selector; + filtered = filterData(filtered, filterProp, value); + } + + const filteredSeries = extractSeries(filtered, dataAttributes); + filteredSeries.forEach((dataSeries) => { + chart.series.forEach((chartSeries) => { + if (dataSeries.name === chartSeries.name) { + chartSeries.setData(dataSeries.data, false); + } + }); + }); + + const obj = {}; + + if (styleOverrides && styleOverrides.match('hook__')) { + overrideStyles(styleOverrides, obj, filtered); + } + + chart.update(obj); + } + + selectors.forEach((selector) => { + selector.attach(filterOnSelect); + }); +} + +export { initFilters }; diff --git a/cfgov/unprocessed/js/routes/on-demand/simple-chart/simple-chart.js b/cfgov/unprocessed/js/routes/on-demand/simple-chart/simple-chart.js index e06a6d1c74d..e094e7bcc97 100644 --- a/cfgov/unprocessed/js/routes/on-demand/simple-chart/simple-chart.js +++ b/cfgov/unprocessed/js/routes/on-demand/simple-chart/simple-chart.js @@ -14,6 +14,7 @@ import { makeFormatter, overrideStyles, } from './utils.js'; +import { initFilters } from './data-filters.js'; import { convertEpochToDateString } from './utils'; accessibility(Highcharts); @@ -295,8 +296,9 @@ function buildChart(chartNode) { chart = tilemapChart.init(chartNode, target, data, dataAttributes); } else { chart = Highcharts.chart(target, makeChartOptions(data, dataAttributes)); - } + initFilters(dataAttributes, chartNode, chart, data); + } // Make sure chart is displayed properly on print window.matchMedia('print').addListener(function () { chart.reflow();