main/DataTracker.js

import populateTemplate from '../utils/populateTemplate';

/**
 * Data Tracker Widget
 *
 * @module DataTracker
 * @prop {string} selectors - DOM selector
 * @prop {string} states - CSS class names for different states
 */
const DataTracker = {
	selectors: {
		main: '.c-dataTracker',
		title: '.c-dataTracker__title',
		lastUpdated: '.c-dataTracker__lastUpdated',
		entries: '.c-dataTracker__entries',
		source: '.c-dataTracker__source',
		footnote: '.c-dataTracker__footnote',
	},

	states: {
		loading: 'c-dataTracker--loading',
		hidden: 'is-hidden',
		entry: 'c-dataTracker__entry',
	},

	templates: {
		entry: `
			<span class="c-dataTracker__label">{{label}}</span>
			<span class="c-dataTracker__value">{{value}}</span>
			<span class="c-dataTracker__diff">{{diff}}</span>
		`,
		chart: `
			<svg class="c-dataTracker__svg" width="{{width}}" height="{{height}}">
				<defs>
					<marker id="datatracker-{{metric}}" viewBox="0 0 10 10"
					refX="3" refY="3" markerUnits="strokeWidth" markerWidth="3"
					markerHeight="3">
						<circle class="c-dataTracker__marker" cx="5" cy="5" r="5"></circle>
					</marker>
				</defs>
				<path class="c-dataTracker__line" d={{path}} marker-end="url(#datatracker-{{metric}})"></path>
			</svg>
			<div class="c-dataTracker__pctChange">Daily {{label}} {{direction}} {{change}}% over past 14 days</div>
		`,
		chartFooter: `
			<div class="c-dataTracker__pctChange">Daily {{label}} {{direction}} {{change}}% over past 14 days</div>
		`,
	},

	/**
	 * Get data for data tracker to display
	 *
	 * @method init
	 */
	init() {
		const $targets = document.querySelectorAll( this.selectors.main );

		[].forEach.call( $targets, ( $target, index ) => {
			if ( $target.dataset.dataTrackerType ) {
				const id = `c-dataTracker${index}`;
				$target.id = id; // eslint-disable-line no-param-reassign
				$target.classList.add( this.states.loading );
				$target.classList.remove( this.states.hidden );

				this.getData( $target,
					$target.dataset.dataTrackerType,
					$target.dataset.dataTrackerParams );
			}
		});
	},

	/**
	 * Get data for specified ajax request type and params
	 *
	 * @method getData
	 * @param {element} $target
	 * @param {string} type
	 * @param {object} params
	 */
	getData( $target, type, params ) {
		fetch( `/gnca-ajax-redesign/${type}/${params}` )
			.then( response => response.text() )
			.then( ( content ) => {
				const data = JSON.parse( content );
				this.populate( $target, data );
				$target.classList.remove( this.states.loading );
			});
	},

	/**
	 * Populate data into target element
	 *
	 * @method populate
	 * @param {element} $target
	 * @param {object} data
	 */
	populate( $target, data ) {
		const $title = $target.querySelector( this.selectors.title );
		$title.textContent = data.title;

		const $lastUpdated = $target.querySelector( this.selectors.lastUpdated );
		$lastUpdated.textContent = `Last Updated: ${data.lastUpdated}`;

		const $source = $target.querySelector( this.selectors.source );
		$source.textContent = `Source: ${data.attribution}`;

		const $footnote = $target.querySelector( this.selectors.footnote );
		$footnote.innerHTML = data.footnote;

		const $entries = $target.querySelector( this.selectors.entries );
		// Populate data entries
		[].forEach.call( data.entry, ( entry ) => {
			const $entry = document.createElement( 'div' );
			let direction = '';
			let prefix = '';
			let $chart = false;

			if ( 0 !== entry.diff ) {
				prefix = entry.diff > 0 ? '+' : '-';
				direction = entry.diff > 0 ? 'growth' : 'decline';
				entry.diff = `(${entry.streak} ${prefix}${Math.abs( entry.diff )})`;	// eslint-disable-line no-param-reassign
			} else {
				entry.diff = ''; // eslint-disable-line no-param-reassign
			}

			if ( false !== entry.trend
				&& false !== entry.trend.path
				&& false !== entry.trend.theta
				&& false !== entry.trend.width
				&& false !== entry.trend.height ) {
				const trendData = entry.trend;
				trendData.metric = entry.metric;
				trendData.label = entry.label.toLowerCase();
				$chart = this.draw_trend( trendData, entry.showChart );
			}

			// Apply custom css
			$entry.classList.add( this.states.entry );
			$entry.classList.add( `${this.states.entry}--${entry.metric}` );
			if ( direction ) {
				$entry.classList.add( `${this.states.entry}--${direction}` );
			}

			$entry.innerHTML = populateTemplate( this.templates.entry, entry, true );

			if ( $chart ) {
				$entry.appendChild( $chart );
			}

			$entries.appendChild( $entry );
		});
	},

	/**
	 * Draw trendline SVG chart
	 *
	 * @method draw_trend
	 * @param {object} trendData - data to populate the chart template
	 * @param {boolean} showChart - whether to show the chart or just the summary text
	 * @return {element} And SVG chart object with trend data populated
	 */
	draw_trend( trendData, showChart ) {
		const $chart = document.createElement( 'div' );
		$chart.classList.add( 'c-dataTracker__trend' );

		if ( showChart ) {
			$chart.innerHTML = populateTemplate( this.templates.chart, trendData );
		} else {
			$chart.innerHTML = populateTemplate( this.templates.chartFooter, trendData );
		}
		return $chart;
	},
};

export default DataTracker;