From bde29a52fc48f752680284aca580bad847f62a0b Mon Sep 17 00:00:00 2001 From: Priyanka Ayer Date: Wed, 30 Apr 2025 15:20:18 -0500 Subject: [PATCH 01/38] modernization of nav hero components --- .../{AeDrillDown.jsx => AeDrillDown.tsx} | 0 .../{DecadeChart.jsx => DecadeChart.tsx} | 0 .../{Disclaimer.jsx => Disclaimer.tsx} | 16 ++-- src/components/Filter/FilterRadio.jsx | 68 --------------- src/components/Filter/FilterRadio.tsx | 73 ++++++++++++++++ .../{FilterSelect.jsx => FilterSelect.tsx} | 17 ++-- .../Filter/{index.jsx => index.tsx} | 22 +++-- src/components/Hero/EndPointButtons.jsx | 59 ------------- src/components/Hero/EndPointButtons.tsx | 51 +++++++++++ src/components/Hero/{index.jsx => index.tsx} | 24 +++--- src/components/{Nav.jsx => Nav.tsx} | 84 +++++++++++-------- ...llocationPanel.jsx => AllocationPanel.tsx} | 0 ...formancePanel.jsx => PerformancePanel.tsx} | 0 ...{PositionsPanel.jsx => PositionsPanel.tsx} | 0 src/constants/{api.jsx => api.tsx} | 0 src/constants/breakpoints.jsx | 27 ------ src/constants/breakpoints.tsx | 34 ++++++++ ...rContainer.jsx => DisclaimerContainer.tsx} | 24 ++++-- .../{NavContainer.jsx => NavContainer.tsx} | 17 ++-- 19 files changed, 278 insertions(+), 238 deletions(-) rename src/components/{AeDrillDown.jsx => AeDrillDown.tsx} (100%) rename src/components/{DecadeChart.jsx => DecadeChart.tsx} (100%) rename src/components/{Disclaimer.jsx => Disclaimer.tsx} (86%) delete mode 100644 src/components/Filter/FilterRadio.jsx create mode 100644 src/components/Filter/FilterRadio.tsx rename src/components/Filter/{FilterSelect.jsx => FilterSelect.tsx} (71%) rename src/components/Filter/{index.jsx => index.tsx} (52%) delete mode 100644 src/components/Hero/EndPointButtons.jsx create mode 100644 src/components/Hero/EndPointButtons.tsx rename src/components/Hero/{index.jsx => index.tsx} (74%) rename src/components/{Nav.jsx => Nav.tsx} (75%) rename src/components/Panels/{AllocationPanel.jsx => AllocationPanel.tsx} (100%) rename src/components/Panels/{PerformancePanel.jsx => PerformancePanel.tsx} (100%) rename src/components/Panels/{PositionsPanel.jsx => PositionsPanel.tsx} (100%) rename src/constants/{api.jsx => api.tsx} (100%) delete mode 100644 src/constants/breakpoints.jsx create mode 100644 src/constants/breakpoints.tsx rename src/containers/{DisclaimerContainer.jsx => DisclaimerContainer.tsx} (54%) rename src/containers/{NavContainer.jsx => NavContainer.tsx} (83%) diff --git a/src/components/AeDrillDown.jsx b/src/components/AeDrillDown.tsx similarity index 100% rename from src/components/AeDrillDown.jsx rename to src/components/AeDrillDown.tsx diff --git a/src/components/DecadeChart.jsx b/src/components/DecadeChart.tsx similarity index 100% rename from src/components/DecadeChart.jsx rename to src/components/DecadeChart.tsx diff --git a/src/components/Disclaimer.jsx b/src/components/Disclaimer.tsx similarity index 86% rename from src/components/Disclaimer.jsx rename to src/components/Disclaimer.tsx index 0b17e8d73..a852bc62e 100644 --- a/src/components/Disclaimer.jsx +++ b/src/components/Disclaimer.tsx @@ -3,13 +3,12 @@ import React from 'react' import ReactModal from 'react-modal' import Link from 'gatsby-link' -import ComposedDisclaimer from '../containers/DisclaimerContainer' import '../css/components/Disclaimer.scss' type tPROPS = { showModal: boolean, - handleCloseModal: Function, - hideModal: Function + setIsModal: (val: boolean) => void; + validated: boolean }; /** @@ -24,8 +23,7 @@ type tPROPS = { const Disclaimer = (props: tPROPS) => { const { showModal, - handleCloseModal, - hideModal + setIsModal } = props @@ -34,19 +32,19 @@ const Disclaimer = (props: tPROPS) => { isOpen={showModal} className='modal-container' overlayClassName='modal-overlay' - contentLabel="Disclaimer Modal" + contentLabel='Disclaimer Modal' shouldCloseOnOverlayClick={false} ariaHideApp={false} > -

Disclaimer

+

Disclaimer

Do not rely on openFDA to make decisions regarding medical care. Always speak to your health provider about the risks and benefits of FDA-regulated products. We may limit or otherwise restrict your access to the API in line with our Terms of Service

This warning banner provides privacy and security notices consistent with applicable federal laws, directives, and other federal guidance for accessing this Government system, which includes all devices/storage media attached to this system. This system is provided for Government-authorized use only. Unauthorized or improper use of this system is prohibited and may result in disciplinary action and/or civil and criminal penalties. At any time, and for any lawful Government purpose, the government may monitor, record, and audit your system usage and/or intercept, search and seize any communication or data transiting or stored on this system. Therefore, you have no reasonable expectation of privacy. Any communication or data transiting or stored on this system may be disclosed or used for any lawful Government purpose.

- + ) } Disclaimer.displayName = 'components/Disclaimer' -export default ComposedDisclaimer(Disclaimer) +export default Disclaimer diff --git a/src/components/Filter/FilterRadio.jsx b/src/components/Filter/FilterRadio.jsx deleted file mode 100644 index a686be065..000000000 --- a/src/components/Filter/FilterRadio.jsx +++ /dev/null @@ -1,68 +0,0 @@ -/* @flow */ - -import React from 'react' -import {RadioGroup, Radio} from 'react-radio-group' - -type PROPS = { - filters: Array; - onChange: Function; - selected: string; -}; - -/** - * @description [used in Infographic when on desktop] - * @param {Array} filters [list of filters. labels + values] - * @param {Function} onChange [callback for handling click] - * @param {string} selected [selected = the currently active radio] - * @return {React.Element} - */ -const FilterRadio = ({ filters, onChange, selected }: PROPS) => { - const _onChange = value => onChange(value) - - return ( - - { -
- - Filter records using example parameters for current visualization. - - { - filters.map((filter: Object, i) => ( - - )) - } -
- } -
- ) -} - -FilterRadio.displayName = 'components/FilterRadio' -export default FilterRadio diff --git a/src/components/Filter/FilterRadio.tsx b/src/components/Filter/FilterRadio.tsx new file mode 100644 index 000000000..23ca227fe --- /dev/null +++ b/src/components/Filter/FilterRadio.tsx @@ -0,0 +1,73 @@ +/* @flow */ + +import React from 'react' +import {RadioGroup, Radio} from 'react-radio-group' + +type Filter = { + searchParam: string; + title: string; +}; + +type PROPS = { + filters: Array; + onChange: Function; + selected: string; +}; + +/** + * @description [used in Infographic when on desktop] + * @param {Array} filters [list of filters. labels + values] + * @param {Function} onChange [callback for handling click] + * @param {string} selected [selected = the currently active radio] + * @return {React.Element} + */ +const FilterRadio = ({ filters, onChange, selected }: PROPS) => { + const _onChange = (value: any) => onChange(value) + + return ( + + { +
+ + Filter records using example parameters for current visualization. + + { + filters.map((filter: Filter, i) => ( + + )) + } +
+ } +
+ ) +} + +FilterRadio.displayName = 'components/FilterRadio' +export default FilterRadio diff --git a/src/components/Filter/FilterSelect.jsx b/src/components/Filter/FilterSelect.tsx similarity index 71% rename from src/components/Filter/FilterSelect.jsx rename to src/components/Filter/FilterSelect.tsx index 510e4f97f..b34235d11 100644 --- a/src/components/Filter/FilterSelect.jsx +++ b/src/components/Filter/FilterSelect.tsx @@ -2,30 +2,35 @@ import React from 'react' +type Filter = { + query: string; + title: string; +}; + type PROPS = { - filters: Array; + filters: Array; selected: string; onChange: Function; }; /** * @description [used in Infographic when on MOBILE or TABLET] - * @param {Array} filters [list of filters. labels + values] +const FilterSelect = ({ filters, selected, onChange }: PROPS) => { * @param {Function} onChange [callback for handling click] * @param {string} selected [selected = the currently active radio] * @return {React.Element} */ -const FilterSelect = ({ filters, selected, onChange }: tPROPS) => { - const _onChange = e => onChange(e.target.value) +const FilterSelect = ({ filters, selected, onChange }: PROPS) => { + const _onChange = (e: any) => onChange(e.target.value) return (
- {column.label} + {column.label} - ); + ) } } -export default Checkbox; \ No newline at end of file +export default Checkbox diff --git a/src/components/Content.jsx b/src/components/Content.tsx similarity index 99% rename from src/components/Content.jsx rename to src/components/Content.tsx index 0a4b436ce..42e395158 100644 --- a/src/components/Content.jsx +++ b/src/components/Content.tsx @@ -22,7 +22,6 @@ type tPROPS = { }; - // reads in _content.yaml from ideally anywhere // and knows what component to render for each field const Content = (props: tPROPS) => { diff --git a/src/components/ContentAccordion.jsx b/src/components/ContentAccordion.tsx similarity index 52% rename from src/components/ContentAccordion.jsx rename to src/components/ContentAccordion.tsx index 96b4cc0d1..a341da8a9 100644 --- a/src/components/ContentAccordion.jsx +++ b/src/components/ContentAccordion.tsx @@ -22,7 +22,7 @@ type tPROPS = { fields: Object; meta: Object; }; - + // called by Content // normally _content.yaml contains string content that // we either render out directly, or use as a flag @@ -43,7 +43,7 @@ const ContentAccordion = (props: tPROPS) => { explorers, // fields from content.yaml, for rendering a reference fields, - // json data from _infographic_definitions, json configuration + // json data from _infographic_definitions, json configuration // for interactive infographics infographicDefinitions, fieldsMapped, @@ -73,7 +73,7 @@ const ContentAccordion = (props: tPROPS) => { key={`${lowerKey}-${k}`} id={`${lowerKey}-${k}`} className={sectionCx}> -
+
{ key={j} /> ) - } else { - // stringified docs -> html - const html: string = marked(content) - - // kind of a weird way to do this - // but, it might be easier for non-technical - // people to understand that they just type - // 'downloads' to render that section - if (content === 'downloads') { - return ( - - ) - } + } + // stringified docs -> html + const html: string = marked(content) - // as far as i can tell we just - // have the one 'image' for drug/event - if (content === 'datasets') { - return ( - - ) - } + // kind of a weird way to do this + // but, it might be easier for non-technical + // people to understand that they just type + // 'downloads' to render that section + if (content === 'downloads') { + return ( + + ) + } - // as far as i can tell we just - // have the one 'image' for drug/event - if (content === 'multipleProductTable') { - return ( - - ) - } + // as far as i can tell we just + // have the one 'image' for drug/event + if (content === 'datasets') { + return ( + + ) + } - if (content === 'FieldExplorer') { - return ( - - ) - } + // as far as i can tell we just + // have the one 'image' for drug/event + if (content === 'multipleProductTable') { + return ( + + ) + } - if (content === 'infographic' && infographics) { - return ( - - ) - } + if (content === 'FieldExplorer') { + return ( + + ) + } - if (content === 'visualization') { - return ( - - ) - } + if (content === 'infographic' && infographics) { + return ( + + ) + } - if (content.includes("image=")){ - return ( - - ) - } + if (content === 'visualization') { + return ( + + ) + } - if (content.includes('key_facts')){ - return ( - - ) - } + if (content.includes("image=")) { + return ( + + ) + } + if (content.includes('key_facts')) { return ( -
) } + + return ( +
+ ) + }) }
@@ -206,4 +206,4 @@ const ContentAccordion = (props: tPROPS) => { } ContentAccordion.displayName = 'components/RenderContentObject' -export default ContentAccordion \ No newline at end of file +export default ContentAccordion diff --git a/src/components/ContentWrapper.jsx b/src/components/ContentWrapper.tsx similarity index 99% rename from src/components/ContentWrapper.jsx rename to src/components/ContentWrapper.tsx index fd541f7ec..dbed1a13d 100644 --- a/src/components/ContentWrapper.jsx +++ b/src/components/ContentWrapper.tsx @@ -46,7 +46,7 @@ const ContentWrapper = (props: tPROPS) => { } = props useEffect(() => { - }, []); + }, []) let fieldsMapped: Object = {} let fieldsFlattened: Object = {} diff --git a/src/components/DataDictionary.jsx b/src/components/DataDictionary.tsx similarity index 58% rename from src/components/DataDictionary.jsx rename to src/components/DataDictionary.tsx index 1f527c56a..1b57cc9dc 100644 --- a/src/components/DataDictionary.jsx +++ b/src/components/DataDictionary.tsx @@ -1,41 +1,43 @@ import React from 'react' import Select from 'react-select' -import ReactModal from "react-modal"; +import ReactModal from "react-modal" import { default as ReactTable } from "react-table" -import { PieChart, Pie, Cell, Tooltip } from "recharts"; -import FileSaver from "file-saver"; +import { PieChart, Pie, Cell, Tooltip } from "recharts" +import FileSaver from "file-saver" import XLSX from 'xlsx' import dictionary from '../constants/fields/master_fields.yaml' -import { API_LINK } from "../constants/api"; +import { API_LINK } from "../constants/api" import '../css/components/DataDictionary.scss' /* generate a download */ -function s2ab(s) { - var buf = new ArrayBuffer(s.length); - var view = new Uint8Array(buf); - for (var i=0; i!=s.length; ++i) { - view[i] = s.charCodeAt(i) & 0xFF; +function s2ab (s) { + const buf = new ArrayBuffer(s.length) + const view = new Uint8Array(buf) + for (let i = 0; i != s.length; ++i) { + view[i] = s.charCodeAt(i) & 0xFF } - return buf; + return buf } -function flattenJSON(data) { +function flattenJSON (data) { - let flattenedJSON = [] + const flattenedJSON = [] - for (let i = 0; i < data.length; i ++) { - let newRow = {} - for (let item in data[i]){ + for (let i = 0; i < data.length; i++) { + const newRow = {} + for (const item in data[i]) { if (typeof data[i][item] === "object") { if (data[i][item] && data[i][item].constructor === Array) { newRow[item] = data[i][item].join("; ") - } else if (data[i][item]) { + } + else if (data[i][item]) { newRow[item] = flattenJSON(data[i][item]) } - } else { + } + else { newRow[item] = data[i][item] } } @@ -82,17 +84,16 @@ class DataDictionary extends React.Component { } - - let nouns = [] - let endpoint_columns = {} - let endpoint_options = {} + const nouns = [] + const endpoint_columns = {} + const endpoint_options = {} Object.keys(dictionary).forEach(function (noun) { - nouns.push({'label': nounList[noun],'value': noun}) + nouns.push({'label': nounList[noun], 'value': noun}) endpoint_columns[noun] = [] endpoint_options[noun] = [] Object.keys(dictionary[noun]).forEach(function (endpoint) { - endpoint_columns[noun].push({'Header': endpointList[endpoint],'accessor': endpoint}) - endpoint_options[noun].push({'label': endpointList[endpoint],'value': endpoint}) + endpoint_columns[noun].push({'Header': endpointList[endpoint], 'accessor': endpoint}) + endpoint_options[noun].push({'label': endpointList[endpoint], 'value': endpoint}) }) }) @@ -149,16 +150,16 @@ class DataDictionary extends React.Component { this.handleNounChange(this.state.selectedNoun) } - getModalData(rowInfo) { - let col_list = [] - let columns = [] + getModalData (rowInfo) { + const col_list = [] + const columns = [] - let data = {} + const data = {} - this.state.endpoint_columns[this.state.selectedNoun['value']].forEach((endpoint) => { - let endpoint_name = endpoint['Header'] - let endpoint_value = endpoint['accessor'] - if (rowInfo['datasets'].includes(endpoint_value)) { + this.state.endpoint_columns[this.state.selectedNoun.value].forEach((endpoint) => { + const endpoint_name = endpoint.Header + const endpoint_value = endpoint.accessor + if (rowInfo.datasets.includes(endpoint_value)) { data[endpoint_value] = true } if (col_list.indexOf(endpoint_value) === -1) { @@ -166,7 +167,7 @@ class DataDictionary extends React.Component { Header: endpoint_name, accessor: endpoint_value, Cell: row => ( -
{row.value ? : }
+
{row.value ? : }
) }) col_list.push(endpoint_value) @@ -196,7 +197,7 @@ class DataDictionary extends React.Component { handleNounChange (val) { this.setState({ selectedNoun: val, - selectedEndpoint: this.state.endpointOptions[val['value']] + selectedEndpoint: this.state.endpointOptions[val.value] }, () => { this.getData() }) @@ -210,165 +211,169 @@ class DataDictionary extends React.Component { }) } - getNestedValue(rowObj, path) { - var props = path.split('.'); - props.forEach(function(prop){ + getNestedValue (rowObj, path) { + const props = path.split('.') + props.forEach(function (prop) { if (rowObj) { - rowObj = rowObj[prop]; + rowObj = rowObj[prop] } }) - return rowObj; + return rowObj } - exportToXLS() { + exportToXLS () { try { /* export only visible columns */ - var columns = [] - this.state.columns.forEach(function(column) { + const columns = [] + this.state.columns.forEach(function (column) { columns.push(column.accessor) }) - var exportableRows = [] + const exportableRows = [] this.state.data.forEach((row) => { - var truncatedRow = {} - var rowData = "" + const truncatedRow = {} + let rowData = "" columns.forEach((column) => { - var columnValue = this.getNestedValue(row, column) + const columnValue = this.getNestedValue(row, column) truncatedRow[column] = columnValue rowData += columnValue ? columnValue : "" }) - if(rowData) { + if (rowData) { exportableRows.push(truncatedRow) } }) /* make the worksheet */ - var ws = XLSX.utils.json_to_sheet(flattenJSON(exportableRows)); + const ws = XLSX.utils.json_to_sheet(flattenJSON(exportableRows)) /* add to workbook */ - var wb = XLSX.utils.book_new(); - XLSX.utils.book_append_sheet(wb, ws, "data"); + const wb = XLSX.utils.book_new() + XLSX.utils.book_append_sheet(wb, ws, "data") /* write workbook (use type 'binary') */ - var wbout = XLSX.write(wb, {bookType:'xlsx', type:'binary'}); + const wbout = XLSX.write(wb, {bookType: 'xlsx', type: 'binary'}) - FileSaver.saveAs(new Blob([s2ab(wbout)],{type:"application/octet-stream"}), this.state.selectedNoun.label + ".xlsx"); - } catch (err) { - console.error(err); + FileSaver.saveAs(new Blob([s2ab(wbout)], {type: "application/octet-stream"}), this.state.selectedNoun.label + ".xlsx") + } + catch (err) { + console.error(err) } } - getObject(data, parent_name, parent_obj, endpoint) { + getObject (data, parent_name, parent_obj, endpoint) { Object.keys(parent_obj).forEach(function (val) { - let val_name = parent_name + '.' + val - if(!data.hasOwnProperty(val_name) && parent_obj[val].hasOwnProperty('items')) { + const val_name = parent_name + '.' + val + if (!data.hasOwnProperty(val_name) && parent_obj[val].hasOwnProperty('items')) { data[val_name] = { 'dataset': [endpoint], - 'definition': parent_obj[val]['items']['description'], - 'type': 'array of ' + parent_obj[val]['items']['type'] + 's' + 'definition': parent_obj[val].items.description, + 'type': 'array of ' + parent_obj[val].items.type + 's' } - } else if(!data.hasOwnProperty(val_name) && !parent_obj[val].hasOwnProperty('items')) { + } + else if (!data.hasOwnProperty(val_name) && !parent_obj[val].hasOwnProperty('items')) { data[val_name] = { 'dataset': [endpoint], - 'definition': parent_obj[val]['description'], - 'type': parent_obj[val]['type'] + 'definition': parent_obj[val].description, + 'type': parent_obj[val].type } - } else { - data[val_name]['dataset'].push(endpoint) + } + else { + data[val_name].dataset.push(endpoint) } }) } getData () { - let data = {} - let usage_endpoints = {} + const data = {} + const usage_endpoints = {} let hits = 0 - let total_hits = 0 - let noun = this.state.selectedNoun['value'] + let total_hits = 0 + const noun = this.state.selectedNoun.value fetch(API_LINK + '/usage.json?prefix=' + '2/api.fda.gov/' + noun + '/') - .then((response) => { - return response.json() - }).then((usage_data) => { - usage_data.table.forEach((dataset) =>{ - if (dataset.path.includes('/')) { - usage_endpoints[dataset.path.split('/')[2].split('.')[0]] = dataset.hits - total_hits += dataset.hits - } - }) - if (this.state.selectedEndpoint && this.state.selectedEndpoint.length) { - this.state.selectedEndpoint.forEach((endpoint) => { - if (Object.keys(usage_endpoints).includes(endpoint.value)) { - hits += usage_endpoints[endpoint.value] + .then((response) => { + return response.json() + }).then((usage_data) => { + usage_data.table.forEach((dataset) => { + if (dataset.path.includes('/')) { + usage_endpoints[dataset.path.split('/')[2].split('.')[0]] = dataset.hits + total_hits += dataset.hits } - - Object.keys(dictionary[noun][endpoint['value']]['properties']).forEach((val) => { - if(dictionary[noun][endpoint['value']]['properties'][val]['type'] === 'object') { - this.getObject(data, val, dictionary[noun][endpoint['value']]['properties'][val]['properties'], endpoint['value']) - } - else if(dictionary[noun][endpoint['value']]['properties'][val]['type'] === 'array' && - dictionary[noun][endpoint['value']]['properties'][val]['items']['type'] === 'object') - { - this.getObject(data, val, dictionary[noun][endpoint['value']]['properties'][val]['items']['properties'], endpoint['value']) + }) + if (this.state.selectedEndpoint && this.state.selectedEndpoint.length) { + this.state.selectedEndpoint.forEach((endpoint) => { + if (Object.keys(usage_endpoints).includes(endpoint.value)) { + hits += usage_endpoints[endpoint.value] } - else if(!data.hasOwnProperty(val) && dictionary[noun][endpoint['value']]['properties'][val].hasOwnProperty('items')) { - data[val] = { - 'dataset': [endpoint['value']], - 'definition': dictionary[noun][endpoint['value']]['properties'][val]['items']['description'], - 'type': 'array of ' + dictionary[noun][endpoint['value']]['properties'][val]['items']['type'] + 's' + + Object.keys(dictionary[noun][endpoint.value].properties).forEach((val) => { + if (dictionary[noun][endpoint.value].properties[val].type === 'object') { + this.getObject(data, val, dictionary[noun][endpoint.value].properties[val].properties, endpoint.value) } - } else if(!data.hasOwnProperty(val) && !dictionary[noun][endpoint['value']]['properties'][val].hasOwnProperty('items')) { - data[val] = { - 'dataset': [endpoint['value']], - 'definition': dictionary[noun][endpoint['value']]['properties'][val]['description'], - 'type': dictionary[noun][endpoint['value']]['properties'][val]['type'] + else if (dictionary[noun][endpoint.value].properties[val].type === 'array' && + dictionary[noun][endpoint.value].properties[val].items.type === 'object') { + this.getObject(data, val, dictionary[noun][endpoint.value].properties[val].items.properties, endpoint.value) } - } else { - data[val]['dataset'].push(endpoint['value']) - } + else if (!data.hasOwnProperty(val) && dictionary[noun][endpoint.value].properties[val].hasOwnProperty('items')) { + data[val] = { + 'dataset': [endpoint.value], + 'definition': dictionary[noun][endpoint.value].properties[val].items.description, + 'type': 'array of ' + dictionary[noun][endpoint.value].properties[val].items.type + 's' + } + } + else if (!data.hasOwnProperty(val) && !dictionary[noun][endpoint.value].properties[val].hasOwnProperty('items')) { + data[val] = { + 'dataset': [endpoint.value], + 'definition': dictionary[noun][endpoint.value].properties[val].description, + 'type': dictionary[noun][endpoint.value].properties[val].type + } + } + else { + data[val].dataset.push(endpoint.value) + } + }) }) - }) - } - let data_array = [] - Object.keys(data).forEach((field) => { - data_array.push({ - 'field_name': field, - 'datasets': data[field]['dataset'], - 'datatype': data[field]['type'], - 'dataset_number': data[field]['dataset'].length, - 'definition': data[field]['definition'] - }) - }) - data_array.sort((a, b) => (a.dataset_number < b.dataset_number) ? 1 : (a.dataset_number === b.dataset_number) ? ((a.field_name > b.field_name) ? 1 : -1) : -1 ) - let pieData = [] - if (data_array && data_array.length) { - for (let i=0; i<5; i++) { - pieData.push({ - 'name': data_array[i]['field_name'], - 'value': data_array[i]['dataset_number'] + } + const data_array = [] + Object.keys(data).forEach((field) => { + data_array.push({ + 'field_name': field, + 'datasets': data[field].dataset, + 'datatype': data[field].type, + 'dataset_number': data[field].dataset.length, + 'definition': data[field].definition }) + }) + data_array.sort((a, b) => (a.dataset_number < b.dataset_number) ? 1 : (a.dataset_number === b.dataset_number) ? ((a.field_name > b.field_name) ? 1 : -1) : -1) + const pieData = [] + if (data_array && data_array.length) { + for (let i = 0; i < 5; i++) { + pieData.push({ + 'name': data_array[i].field_name, + 'value': data_array[i].dataset_number + }) + } } - } - this.setState({ - 'data': data_array, - 'hits': hits.toLocaleString(), - 'totalHits': total_hits.toLocaleString(), - 'pieData': pieData - }) - }).catch((error) =>{ - console.log("Error fetching response data: ", error) - this.setState({ - 'data': [], - 'hits': hits.toLocaleString(), - 'totalHits': total_hits.toLocaleString(), - 'pieData': [] + this.setState({ + 'data': data_array, + 'hits': hits.toLocaleString(), + 'totalHits': total_hits.toLocaleString(), + 'pieData': pieData + }) + }).catch((error) => { + console.log("Error fetching response data: ", error) + this.setState({ + 'data': [], + 'hits': hits.toLocaleString(), + 'totalHits': total_hits.toLocaleString(), + 'pieData': [] + }) }) - }) } render () { - if(this.state.data === undefined){ + if (this.state.data === undefined) { return () } @@ -377,15 +382,15 @@ class DataDictionary extends React.Component { // } let data = this.state.data - let searchText = this.state.search + const searchText = this.state.search let harmonized = false - if (Object.keys(this.state.selectedRow).includes('field_name') && this.state.selectedRow['field_name'].includes('openfda')) { + if (Object.keys(this.state.selectedRow).includes('field_name') && this.state.selectedRow.field_name.includes('openfda')) { harmonized = true } if (searchText) { - let regex = new RegExp( searchText, "i") + const regex = new RegExp(searchText, "i") data = data.filter(row => { for (let i = 0; i < this.state.columns.length; i++) { if (regex.test(String(this.getNestedValue(row, this.state.columns[i].accessor)))) { @@ -402,21 +407,21 @@ class DataDictionary extends React.Component { isOpen={this.state.showModal} className='help-window' overlayClassName='modal-overlay' - contentLabel="Help Modal" + contentLabel='Help Modal' onRequestClose={this.closeModal} - shouldCloseOnOverlayClick={true} + shouldCloseOnOverlayClick ariaHideApp={false} > -

{this.state.selectedRow['field_name']} ({this.state.selectedRow['datatype']})

+

{this.state.selectedRow.field_name} ({this.state.selectedRow.datatype})

Definition

-

{this.state.selectedRow['definition']}

-

{harmonized ? 'Harmonized' : 'Included'} in these {this.state.selectedNoun['label']} Endpoints

+

{this.state.selectedRow.definition}

+

{harmonized ? 'Harmonized' : 'Included'} in these {this.state.selectedNoun.label} Endpoints

@@ -441,12 +446,12 @@ class DataDictionary extends React.Component { this.setState({search: e.target.value})} - placeholder="Type to Search in Results..." type="search" autoFocus + placeholder='Type to Search in Results...' type='search' autoFocus /> Export to XLSExport to XLS - + src='/img/xls-icon.svg'/>Export to XLS +
{ return { - onClick: () => this.openModal(rowInfo['row']['_original']) + onClick: () => this.openModal(rowInfo.row._original) } }} columns={this.state.columns} pageSize={this.state.pageSize} pageSizeOptions={[10, 25, 50, 100, 200, 250, 500, 1000]} - showPagination={true} + showPagination minRows={10} className='table -striped -highlight' filtered={this.state.filtered} diff --git a/src/components/Dataset.jsx b/src/components/Dataset.tsx similarity index 100% rename from src/components/Dataset.jsx rename to src/components/Dataset.tsx diff --git a/src/components/Datasets.jsx b/src/components/Datasets.tsx similarity index 100% rename from src/components/Datasets.jsx rename to src/components/Datasets.tsx diff --git a/src/components/DecadeChart.tsx b/src/components/DecadeChart.tsx index cba4cc281..431ab37ea 100644 --- a/src/components/DecadeChart.tsx +++ b/src/components/DecadeChart.tsx @@ -2,7 +2,7 @@ import React, {Component} from 'react' import {Bar, BarChart, CartesianGrid, Legend, ResponsiveContainer, Tooltip, XAxis, YAxis} from "recharts" import { API_LINK } from '../constants/api' -const CustomTooltip = ({ active, payload, label }) => { +const CustomTooltip: React.FC<{ active?: boolean; payload?: any[]; label?: string }> = ({ active, payload, label }) => { if (active && payload && payload.length) { return (
@@ -14,8 +14,13 @@ const CustomTooltip = ({ active, payload, label }) => { return null } -class DecadeChart extends Component { - constructor (props) { +interface DecadeChartState { + data: { name: string; total: number }[]; + decade: string; +} + +class DecadeChart extends Component<{}, DecadeChartState> { + constructor (props: any) { super(props) this.state = { data: [], @@ -37,9 +42,9 @@ class DecadeChart extends Component { .then(res => res.json()) .then((json => { if (json.results) { - const aeData = {} - json.results.forEach(line => { - line.adverse_events_mentioned.forEach(x => { + const aeData: Record = {} + json.results.forEach((line: any) => { + line.adverse_events_mentioned.forEach((x: any) => { if (x.meddra_term in aeData) { aeData[x.meddra_term] += x.count } @@ -63,9 +68,9 @@ class DecadeChart extends Component { })) } - handleButtonClickDecade = e => { + handleButtonClickDecade = (e: React.MouseEvent) => { this.setState({ - decade: e.target.value + decade: e.currentTarget.value }, () => { this.getData() }) @@ -150,23 +155,21 @@ class DecadeChart extends Component { 2010's
- - - - - - } /> - + + + + } /> + diff --git a/src/components/DocSidebar.jsx b/src/components/DocSidebar.tsx similarity index 86% rename from src/components/DocSidebar.jsx rename to src/components/DocSidebar.tsx index 7a95026e7..a3890b189 100644 --- a/src/components/DocSidebar.jsx +++ b/src/components/DocSidebar.tsx @@ -1,19 +1,19 @@ import React from "react" import Link from "gatsby-link" import cx from 'classnames' -import ComposedDocSidebar from "../containers/DocSidebarContainer"; +import ComposedDocSidebar from "../containers/DocSidebarContainer" import '../css/components/DocSidebar.scss' const Section = props => { let isHeader = false - let long_title = props.id + const long_title = props.id if (props.collapse === true) { isHeader = true } return (
-

-1 ? ' ': ' collapsed')): ' '} +

-1 ? ' ' : ' collapsed')) : ' '} title={long_title} onClick={props.toggleSection}> {props.title} @@ -30,7 +30,7 @@ const Section = props => { const SectionLinks = props => { return ( -
    -1 ? 'display-block': 'display-none') : ' '}> +
      -1 ? 'display-block' : 'display-none') : ' '}> {props.items.map((item, index) => ( { } const APINavLink = props => { - let isActive = typeof window !== "undefined" ? window.location.href.endsWith(props.to) : false; - let className = isActive ? props.className +" sidebar-item-active" : props.className; + const isActive = typeof window !== "undefined" ? window.location.href.endsWith(props.to) : false + const className = isActive ? props.className + " sidebar-item-active" : props.className - return ( - {props.title} - ); + return ( + {props.title} + ) } const SectionLink = props => { @@ -70,16 +70,17 @@ const SectionLink = props => { )) } - let item = props.node - let title = item.title - let link = item.link + const item = props.node + const title = item.title + const link = item.link let long_title = title let isHeader = false if (Object.keys(item).indexOf("link") === -1) { isHeader = true long_title = item.id - } else { + } + else { long_title = link.split('/').slice(2, -1).join('_') } const itemCx = cx({ @@ -93,7 +94,7 @@ const SectionLink = props => { return (
    • {Object.keys(item).indexOf("link") === -1 ? ( - -1 ? ' ': ' collapsed')} + -1 ? ' ' : ' collapsed')} title={long_title} onClick={props.toggleSection}> {title} @@ -103,18 +104,18 @@ const SectionLink = props => { {title} ) : ( - + /> ) } {childnodes ? -
        -1 ? 'display-block': 'display-none') : ' '}> +
          -1 ? 'display-block' : 'display-none') : ' '}> {childnodes}
        : null} @@ -199,4 +200,4 @@ const DocSidebar = (props: tPROPS) => { } DocSidebar.displayName = 'components/DocSidebar' -export default ComposedDocSidebar(DocSidebar) \ No newline at end of file +export default ComposedDocSidebar(DocSidebar) diff --git a/src/components/Downloads.jsx b/src/components/Downloads.tsx similarity index 100% rename from src/components/Downloads.jsx rename to src/components/Downloads.tsx diff --git a/src/components/DrugQueryTable.jsx b/src/components/DrugQueryTable.tsx similarity index 100% rename from src/components/DrugQueryTable.jsx rename to src/components/DrugQueryTable.tsx diff --git a/src/components/EndpointBox.jsx b/src/components/EndpointBox.tsx similarity index 66% rename from src/components/EndpointBox.jsx rename to src/components/EndpointBox.tsx index 9436e889b..72eb8189e 100644 --- a/src/components/EndpointBox.jsx +++ b/src/components/EndpointBox.tsx @@ -112,39 +112,39 @@ const EndpointBox = (props: tPROPS) => { const icon = { 'animalandveterinary': { - 'event':
        + 'event':
        }, 'food': { - 'enforcement':
        , - 'event':
        + 'enforcement':
        , + 'event':
        }, 'device': { - 'event':
        , - 'classification':
        , - '510k':
        , - 'pma':
        , - 'registrationlisting':
        , - 'recall':
        , - 'enforcement':
        , - 'udi':
        , - 'covid19serology':
        + 'event':
        , + 'classification':
        , + '510k':
        , + 'pma':
        , + 'registrationlisting':
        , + 'recall':
        , + 'enforcement':
        , + 'udi':
        , + 'covid19serology':
        }, 'drug': { - 'event':
        , - 'label':
        , - 'ndc':
        , - 'enforcement':
        , - 'drugsfda':
        , - 'drugshortages':
        + 'event':
        , + 'label':
        , + 'ndc':
        , + 'enforcement':
        , + 'drugsfda':
        , + 'drugshortages':
        }, 'other': { - 'historicaldocument':
        , - 'nsde':
        , - 'substance':
        , - 'unii':
        + 'historicaldocument':
        , + 'nsde':
        , + 'substance':
        , + 'unii':
        }, 'tobacco': { - 'problem':
        + 'problem':
        } } @@ -187,16 +187,16 @@ const EndpointBox = (props: tPROPS) => { } return ( -
        - -
        +
        + +
        {icon[noun_name][endpoint_name]}
        -

        {ep_title[noun_name][endpoint_name]}

        - {description[noun_name][endpoint_name]} +

        {ep_title[noun_name][endpoint_name]}

        + {description[noun_name][endpoint_name]}
        - LEARN MORE + LEARN MORE
        ) diff --git a/src/components/EndpointStatus.jsx b/src/components/EndpointStatus.tsx similarity index 87% rename from src/components/EndpointStatus.jsx rename to src/components/EndpointStatus.tsx index 568156434..a4e638e56 100644 --- a/src/components/EndpointStatus.jsx +++ b/src/components/EndpointStatus.tsx @@ -10,7 +10,7 @@ type tPROPS = { }; const EndpointStatus = ({ data, fullPath, }: tPROPS) => { - var date = new Date(data.last_updated) + const date = new Date(data.last_updated) return (
        @@ -22,12 +22,12 @@ const EndpointStatus = ({ data, fullPath, }: tPROPS) => { { data.status === 'GREEN' ? - OK - + OK + : - DOWN - + DOWN + }
        diff --git a/src/components/FieldDownload.jsx b/src/components/FieldDownload.tsx similarity index 99% rename from src/components/FieldDownload.jsx rename to src/components/FieldDownload.tsx index 7d1a68792..0643f24ee 100644 --- a/src/components/FieldDownload.jsx +++ b/src/components/FieldDownload.tsx @@ -107,4 +107,4 @@ const FieldDownload = (props: tPROPS) => { FieldDownload.displayName = 'components/FieldDownload' -export default FieldDownload \ No newline at end of file +export default FieldDownload diff --git a/src/components/FieldExplorer.jsx b/src/components/FieldExplorer.tsx similarity index 88% rename from src/components/FieldExplorer.jsx rename to src/components/FieldExplorer.tsx index 467562544..6e8f46858 100644 --- a/src/components/FieldExplorer.jsx +++ b/src/components/FieldExplorer.tsx @@ -2,7 +2,7 @@ import React from 'react' import marked from 'marked' import Scrollbars from 'react-custom-scrollbars' import Select from 'react-select' -//import 'react-select/dist/react-select.css' +// import 'react-select/dist/react-select.css' import Values from './RenderContentObject/Values' import yamlGet from '../utils/yamlGet' import FieldDownload from './FieldDownload' @@ -31,7 +31,7 @@ const _renderLi = (props: tLiProps) => { // whether field has .exact let isExact: bool = false // keys in the selected field - let field_keys: Array = Object.keys(field) + const field_keys: Array = Object.keys(field) if (field) { desc = field.description @@ -139,7 +139,7 @@ const _renderLi = (props: tLiProps) => { ) } -function render_object(props) { +function render_object (props) { const { fields, selectedField, @@ -153,17 +153,17 @@ function render_object(props) { { Object.keys(fields).map((v: string, k) => (
      • { - fields[v]['type'] && - {fields[v]['type']} + fields[v].type && + {fields[v].type} }
      • )) @@ -173,20 +173,20 @@ function render_object(props) { ) } -function get_fields(fields, prefix) { - return Object.keys(fields).reduce(function(acc, key){ - let value = fields[key]; +function get_fields (fields, prefix) { + return Object.keys(fields).reduce(function (acc, key) { + const value = fields[key] if (typeof value === 'object' && value !== null && key !== 'possible_values') { if (['properties', 'items'].indexOf(key) === -1) { - acc.push((typeof prefix === 'undefined') ? {label: key, value: key}: {label: prefix + '.' + key, value: prefix + '.' + key}) + acc.push((typeof prefix === 'undefined') ? {label: key, value: key} : {label: prefix + '.' + key, value: prefix + '.' + key}) acc.push.apply(acc, get_fields(value, (typeof prefix === 'undefined') ? key : prefix + '.' + key)) } else { acc.push.apply(acc, get_fields(value, prefix)) } } - return acc; - }, []); + return acc + }, []) } type tPROPS = { @@ -218,21 +218,21 @@ const FieldExplorer = (props: tPROPS) => { updateSelected } = props - let field_names = get_fields(fields.properties) + const field_names = get_fields(fields.properties) return ( -
        +
        } diff --git a/src/components/InteractiveInfographicTour.jsx b/src/components/InteractiveInfographicTour.tsx similarity index 95% rename from src/components/InteractiveInfographicTour.jsx rename to src/components/InteractiveInfographicTour.tsx index 091b1cbf5..d03fc97c1 100644 --- a/src/components/InteractiveInfographicTour.jsx +++ b/src/components/InteractiveInfographicTour.tsx @@ -41,9 +41,9 @@ class InteractiveInfographicTour extends React.Component { } handleJoyrideCallback = data => { - const { status, type } = data; + const { status, type } = data if ([STATUS.FINISHED, STATUS.SKIPPED].includes(status)) { - this.setState({ tourRun: false }); + this.setState({ tourRun: false }) } }; @@ -100,7 +100,7 @@ class InteractiveInfographicTour extends React.Component { { const { cx, - cy, - midAngle, - innerRadius, - outerRadius, - startAngle, - endAngle, - fill, - payload, - percent, - value, - pct, - name, - textLabel - } = props; - - const sin = Math.sin(-RADIAN * midAngle); - const cos = Math.cos(-RADIAN * midAngle); - const sx = cx + (outerRadius + 10) * cos; - const sy = cy + (outerRadius + 10) * sin; - const mx = cx + (outerRadius + 30) * cos; - const my = cy + (outerRadius + 30) * sin; - const ex = mx + (cos >= 0 ? 1 : -1) * 22; - const ey = my; - const textAnchor = cos >= 0 ? 'start' : 'end'; - const subName = name !== undefined && typeof name == 'string' ? name.split('(')[0] : '' + cy, + midAngle, + innerRadius, + outerRadius, + startAngle, + endAngle, + fill, + payload, + percent, + value, + pct, + name, + textLabel + } = props + + const sin = Math.sin(-RADIAN * midAngle) + const cos = Math.cos(-RADIAN * midAngle) + const sx = cx + (outerRadius + 10) * cos + const sy = cy + (outerRadius + 10) * sin + const mx = cx + (outerRadius + 30) * cos + const my = cy + (outerRadius + 30) * sin + const ex = mx + (cos >= 0 ? 1 : -1) * 22 + const ey = my + const textAnchor = cos >= 0 ? 'start' : 'end' + const subName = name !== undefined && typeof name === 'string' ? name.split('(')[0] : '' return ( - + textAnchor='middle' + id='textLabel1' + className='piechart-centered-title' + /> - + textAnchor='middle' + id='textLabel2' + className='piechart-centered-title' + /> { outerRadius={outerRadius + 10} fill={fill} /> - - - = 0 ? 1 : -1) * 12} y={ey} textAnchor={textAnchor} fill="#333" className="piechart-label">{`${subName}`} - = 0 ? 1 : -1) * 12} y={ey+15} textAnchor={textAnchor} fill="#333" className="piechart-label">{`${pct}`} + + + = 0 ? 1 : -1) * 12} y={ey} textAnchor={textAnchor} fill='#333' className='piechart-label'>{`${subName}`} + = 0 ? 1 : -1) * 12} y={ey + 15} textAnchor={textAnchor} fill='#333' className='piechart-label'>{`${pct}`} - ); -}; + ) +} class TwoLevelPieChart extends React.Component { - constructor (props: Object) { + constructor (props: Object) { super(props) this.state = { @@ -98,7 +96,7 @@ class TwoLevelPieChart extends React.Component { } componentDidMount () { - const activeIndex = this.props.default.index || 0; + const activeIndex = this.props.default.index || 0 this.state = { activeIndex: activeIndex } @@ -107,23 +105,23 @@ class TwoLevelPieChart extends React.Component { }) } - onPieEnter(data, index) { + onPieEnter (data, index) { this.setState({ activeIndex: index, - }); - if(this.props.onMouseEnter!== undefined){ + }) + if (this.props.onMouseEnter !== undefined) { this.props.onMouseEnter(data, index) } } - onPieClick(data, index) { + onPieClick (data, index) { this.setState({ activeIndex: index, - }); + }) this.props.parent.setState({ activeIndex: index }) - if(this.props.onClick!== undefined){ + if (this.props.onClick !== undefined) { this.props.onClick(data, index) } } @@ -135,8 +133,8 @@ class TwoLevelPieChart extends React.Component { height={this.props.height} > ) } - - ); + + ) } } diff --git a/src/components/Layout.jsx b/src/components/Layout.tsx similarity index 98% rename from src/components/Layout.jsx rename to src/components/Layout.tsx index 47b4ff636..3365bbe71 100644 --- a/src/components/Layout.jsx +++ b/src/components/Layout.tsx @@ -19,7 +19,7 @@ const hhsACx = 'clr-white relative hhs' const Layout = (props) => { - const [sidebarFixed, setSidebarFixed] = useState(false); + const [sidebarFixed, setSidebarFixed] = useState(false) const toggleSidebarFixed = (fixed) => { @@ -119,4 +119,4 @@ export default props => ( {locationProps => } -); \ No newline at end of file +) diff --git a/src/components/MultipleProductTable.jsx b/src/components/MultipleProductTable.tsx similarity index 100% rename from src/components/MultipleProductTable.jsx rename to src/components/MultipleProductTable.tsx diff --git a/src/components/Nav.tsx b/src/components/Nav.tsx index b76823dbe..3f8a2b66e 100644 --- a/src/components/Nav.tsx +++ b/src/components/Nav.tsx @@ -33,20 +33,6 @@ type tPROPS = { validated: boolean; }; -// type tPROPS = { -// showMobileNav: boolean; -// toggleMobileNav: () => void; -// closeMobileNav: () => void; -// toggleDropdownContent: (e: React.MouseEvent) => void; -// hideDropdownContent: () => void; -// showDropdownContent: (e: React.MouseEvent) => void; -// handleOpenModal: () => void; -// handleCloseModal: () => void; -// activeDropdown: string; -// path: string; -// validated: boolean; -// }; - const Nav = (props: tPROPS) => { const { showMobileNav, diff --git a/src/components/Noun.jsx b/src/components/Noun.tsx similarity index 100% rename from src/components/Noun.jsx rename to src/components/Noun.tsx diff --git a/src/components/NounBox.jsx b/src/components/NounBox.tsx similarity index 100% rename from src/components/NounBox.jsx rename to src/components/NounBox.tsx diff --git a/src/components/Panels/AllocationPanel.tsx b/src/components/Panels/AllocationPanel.tsx index 1a60d44e3..9bd9bb90b 100644 --- a/src/components/Panels/AllocationPanel.tsx +++ b/src/components/Panels/AllocationPanel.tsx @@ -4,6 +4,5 @@ import DecadeChart from '../DecadeChart' export default function AllocationPanel () { return ( - ) } diff --git a/src/components/Panels/PerformancePanel.tsx b/src/components/Panels/PerformancePanel.tsx index fd53639bf..c11efde4d 100644 --- a/src/components/Panels/PerformancePanel.tsx +++ b/src/components/Panels/PerformancePanel.tsx @@ -2,7 +2,11 @@ import React from "react" import AeDrillDown from '../AeDrillDown' import { API_LINK } from '../../constants/api' -class PerformancePanel extends React.Component { +interface PerformancePanelState { + dropDown?: { label: string; value: string }[]; +} + +class PerformancePanel extends React.Component<{}, PerformancePanelState> { constructor (props: Object) { super(props) @@ -19,7 +23,7 @@ class PerformancePanel extends React.Component { .then(res => res.json()) .then((json => { if (json.results) { - const dropdownData = json.results.map(line => { + const dropdownData = json.results.map((line: any) => { return {label: line.term + ' (' + line.count + ')', value: line.term} }) this.setState({ diff --git a/src/components/Panels/PositionsPanel.tsx b/src/components/Panels/PositionsPanel.tsx index cf0733e2d..742725635 100644 --- a/src/components/Panels/PositionsPanel.tsx +++ b/src/components/Panels/PositionsPanel.tsx @@ -1,8 +1,14 @@ -import React from "react" -import { default as ReactTable } from "react-table" +import React, { JSX } from "react" +import { useTable } from "react-table" import { API_LINK } from '../../constants/api' -class PositionsPanel extends React.Component { +type PositionsPanelState = { + columns: Array<{ Header: string; accessor: string; Cell?: (row: any) => JSX.Element }>; + data: Array; + pageSize: number; +}; + +class PositionsPanel extends React.Component<{}, PositionsPanelState> { constructor (props: Object) { super(props) @@ -23,13 +29,13 @@ class PositionsPanel extends React.Component { { 'Header': 'Reactions Mentioned', 'accessor': 'ae', - Cell: row => ( + Cell: (row: any) => (
          {/*
        1. {row.value}
        2. */} - {row.value.map((v, idx) => + {row.value.map((v: any, idx: any) =>
        3. • {v}
        4. )}
        @@ -52,7 +58,7 @@ class PositionsPanel extends React.Component { .then(res => res.json()) .then((json => { if (json.results) { - const data = json.results.map(line => { + const data = json.results.map((line: { decade: string; year: string; doc_file_name: string; adverse_events_mentioned: { meddra_term: string }[] }) => { return { decade: line.decade, pub_year: line.year, @@ -73,29 +79,45 @@ class PositionsPanel extends React.Component { render () { return ( - this.setState({ sorted })} - onPageChange={page => this.setState({ page })} - onPageSizeChange={(pageSize, page) => this.setState({ page, pageSize })} - onResizedChange={resized => this.setState({ resized })} - onFilteredChange={filtered => this.setState({ filtered })} - style={{ - width: '100%', - height: '494px', - position: 'relative' - }} - /> + ) } } +const Table = ({ columns, data }: { columns: any; data: any }) => { + const { + getTableProps, + getTableBodyProps, + headerGroups, + rows, + prepareRow, + } = useTable({ columns, data }) + + return ( +
        + + {headerGroups.map(headerGroup => ( + + {headerGroup.headers.map(column => ( + + ))} + + ))} + + + {rows.map(row => { + prepareRow(row) + return ( + + {row.cells.map(cell => ( + + ))} + + ) + })} + +
        {column.render('Header')}
        {cell.render('Cell')}
        + ) +} + export default PositionsPanel diff --git a/src/components/PieChartInfographic.jsx b/src/components/PieChartInfographic.tsx similarity index 52% rename from src/components/PieChartInfographic.jsx rename to src/components/PieChartInfographic.tsx index cb32df222..0fa04e9b9 100644 --- a/src/components/PieChartInfographic.jsx +++ b/src/components/PieChartInfographic.tsx @@ -2,16 +2,16 @@ import React from 'react' -import {sum, TimeRange, TimeSeries} from "pondjs"; +import {sum, TimeRange, TimeSeries} from "pondjs" import {ChartContainer, ChartRow, Charts, EventMarker, LineChart, styler, YAxis} from "react-timeseries-charts" -import _ from 'lodash'; +import _ from 'lodash' import 'whatwg-fetch' -import PropTypes from 'prop-types'; -import Parser from 'html-react-parser'; -import {default as $} from "jquery"; +import PropTypes from 'prop-types' +import Parser from 'html-react-parser' +import {default as $} from "jquery" -import TwoLevelPieChart from './InteractivePie'; -import Checkbox from "./Checkbox"; +import TwoLevelPieChart from './InteractivePie' +import Checkbox from "./Checkbox" import states from '../pages/apis/states.json' import {API_LINK} from '../constants/api' @@ -19,9 +19,9 @@ import {API_LINK} from '../constants/api' const stringOrNode = PropTypes.oneOfType([ PropTypes.string, PropTypes.node, -]); +]) -/*const GravatarOption = createClass({ +/* const GravatarOption = createClass({ propTypes: { children: PropTypes.node, className: PropTypes.string, @@ -106,27 +106,27 @@ class PieChartInfographic extends React.Component { constructor (props: Object) { super(props) - if( - this.props.api == undefined || + if ( + this.props.api == undefined || this.props.infographicDefinitions === undefined - ) { + ) { throw "Invalid Props" } - const minTime = new Date(this.props.globalDefs.startYear,1,1); + const minTime = new Date(this.props.globalDefs.startYear, 1, 1) this.state = { maxLimit: 1000, API_LINK: API_LINK, minTime: minTime, maxTime: null, - selection: null, + selection: null, selected: [], reverseProductLabels: null, - tracker: null, + tracker: null, trackerValue: null, trackerEvent: null, - sparklineData: null, + sparklineData: null, lineChartColumns: [], trackerInfoValues: "", infoHeight: 0, @@ -135,7 +135,7 @@ class PieChartInfographic extends React.Component { columnStyles: null, lineChartLoaded: 0, width: this.props.globalDefs.lineChartConfig.width - }; + } // functions this.onClick = this.onClick.bind(this) @@ -147,33 +147,33 @@ class PieChartInfographic extends React.Component { this.setTextStyle = this.setTextStyle.bind(this) } - componentWillReceiveProps(){ + componentWillReceiveProps () { this.setState({lineChartLoaded: 0}, this.componentDidMount()) } componentDidMount () { const that = this - let download_url = `${API_LINK}/download.json` + const download_url = `${API_LINK}/download.json` fetch(download_url) - .then(function(download_res) { + .then(function (download_res) { return download_res.json() - }).then(function(download_res) { + }).then(function (download_res) { - const apiParts = that.props.globalDefs.api.split('/'), - latestDataDate = new Date(download_res.results[apiParts[1]][apiParts[2]].export_date), - latestYear = latestDataDate.getFullYear() + const apiParts = that.props.globalDefs.api.split('/') + const latestDataDate = new Date(download_res.results[apiParts[1]][apiParts[2]].export_date) + const latestYear = latestDataDate.getFullYear() const currentDate = new Date() that.setState({ - maxTime: new Date(currentDate.getFullYear(), 12,1) + maxTime: new Date(currentDate.getFullYear(), 12, 1) }) that[that.props.infographicDefinitions.dataFunction]().then((res) => { - let data = res; - if(that.props.infographicDefinitions.pieChartConfig.categoryLimiter !== undefined){ - data = res.slice(0,that.props.infographicDefinitions.pieChartConfig.categoryLimiter) + let data = res + if (that.props.infographicDefinitions.pieChartConfig.categoryLimiter !== undefined) { + data = res.slice(0, that.props.infographicDefinitions.pieChartConfig.categoryLimiter) } that.setState({ data: data, @@ -189,62 +189,62 @@ class PieChartInfographic extends React.Component { return new Promise((resolve, reject) => { $.getJSON(url) .done((json) => resolve(json, url)) - .fail((xhr, status, err) => reject(status + err.message)); - }); + .fail((xhr, status, err) => reject(status + err.message)) + }) } - setPieChartText(){ + setPieChartText () { $("#textLabel1").text(this.props.infographicDefinitions.pieChartConfig.textLabel[0]) $("#textLabel2").text(this.props.infographicDefinitions.pieChartConfig.textLabel[1]) } _getAllDataByFields () { - var urls = [`${this.state.API_LINK}${this.props.api}.json?count=${this.props.infographicDefinitions.fields.subsetField}`] - const itemPromises = urls.map(this.fetchJSON); + const urls = [`${this.state.API_LINK}${this.props.api}.json?count=${this.props.infographicDefinitions.fields.subsetField}`] + const itemPromises = urls.map(this.fetchJSON) return Promise.all(itemPromises).then((results) => { - var that = this; - var terms = {} + const that = this + const terms = {} results[0].results.map((item) => { - terms[item.term] = item.count + terms[item.term] = item.count }) - var total = terms[that.props.infographicDefinitions.fields.subsetValue] + const total = terms[that.props.infographicDefinitions.fields.subsetValue] return { "fieldTotal": total } - }).then( (data) => { - var that = this; - var xUrls = this.props.infographicDefinitions.fields.categories.map((category) => { + }).then((data) => { + const that = this + const xUrls = this.props.infographicDefinitions.fields.categories.map((category) => { return `${this.state.API_LINK}${this.props.api}.json?count=${category}` }) - var final = [] - var yterms = [] + const final = [] + const yterms = [] return Promise.all(xUrls.map(this.fetchJSON)).then((xresults) => { - var that = this; - var dataLocal = data; + const that = this + const dataLocal = data - var res = _.zip(xresults, that.props.infographicDefinitions.fields.categories) + const res = _.zip(xresults, that.props.infographicDefinitions.fields.categories) - var total = res.map( (item) => { + const total = res.map((item) => { return item.count - }).reduce((a, b) => a + b, 0); + }).reduce((a, b) => a + b, 0) - return res.map( (item) => { - var data = dataLocal; - var id = item[1]; - var terms = {} + return res.map((item) => { + const data = dataLocal + const id = item[1] + const terms = {} item[0].results.map((val) => { terms[val.term] = val.count }) - var count = terms[that.props.infographicDefinitions.fields.subsetValue]; + const count = terms[that.props.infographicDefinitions.fields.subsetValue] return { "id": id, "name": that.props.infographicDefinitions.defs[id], "value": count, - "pct": (( count / data['fieldTotal'] ) * 100).toFixed(0) + '%' + "pct": ((count / data.fieldTotal) * 100).toFixed(0) + '%' } }) }) @@ -252,37 +252,39 @@ class PieChartInfographic extends React.Component { } _getAllData () { - const that = this; - var urls = [`${that.state.API_LINK}${that.props.api}.json?count=${that.props.infographicDefinitions.countBy}`] + const that = this + const urls = [`${that.state.API_LINK}${that.props.api}.json?count=${that.props.infographicDefinitions.countBy}`] - return Promise.all(urls.map(this.fetchJSON)).then(function(results) { + return Promise.all(urls.map(this.fetchJSON)).then(function (results) { - /// Order, get total, and filter + // / Order, get total, and filter - /// ordering - let res; - if(that.props.infographicDefinitions.pieChartConfig.sort === undefined){ + // / ordering + let res + if (that.props.infographicDefinitions.pieChartConfig.sort === undefined) { res = results[0].results - } else if (that.props.infographicDefinitions.pieChartConfig.sort === "descending"){ - res = results[0].results.sort((a,b) => b.count - a.count) - } else if (that.props.infographicDefinitions.pieChartConfig.sort === "ascending"){ - res = results[0].results.sort( (a,b) => a.count - b.count) } - /// + else if (that.props.infographicDefinitions.pieChartConfig.sort === "descending") { + res = results[0].results.sort((a, b) => b.count - a.count) + } + else if (that.props.infographicDefinitions.pieChartConfig.sort === "ascending") { + res = results[0].results.sort((a, b) => a.count - b.count) + } + // / - var total = res.map( (item) => { + const total = res.map((item) => { return item.count - }).reduce((a, b) => a + b, 0); + }).reduce((a, b) => a + b, 0) - /// filtering - if(that.props.infographicDefinitions.excludeFields !== undefined){ + // / filtering + if (that.props.infographicDefinitions.excludeFields !== undefined) { res = res.filter(value => { return (that.props.infographicDefinitions.excludeFields.indexOf(value.term) === -1) }) } - /// + // / - return res.map( (item) => { + return res.map((item) => { return { "id": item.term, "name": that.props.infographicDefinitions.defs[item.term], @@ -293,23 +295,24 @@ class PieChartInfographic extends React.Component { }) } - onClick (data, index){ - if(this.state.activeIndex === index && this.state.lineChartLoaded > 0){ + onClick (data, index) { + if (this.state.activeIndex === index && this.state.lineChartLoaded > 0) { return } this.setState({ sparklineData: null }) - const that = this; + const that = this - let searchField; - if(this.props.infographicDefinitions.dataFunction === "_getAllDataByFields"){ + let searchField + if (this.props.infographicDefinitions.dataFunction === "_getAllDataByFields") { searchField = `${data.id}:${this.props.infographicDefinitions.fields.subsetValue}` - } else if (this.props.infographicDefinitions.dataFunction === "_getAllData") { + } + else if (this.props.infographicDefinitions.dataFunction === "_getAllData") { searchField = `${that.props.infographicDefinitions.countBy}:"${data.id}"` - if(this.props.infographicDefinitions.existsField){ + if (this.props.infographicDefinitions.existsField) { searchField += `+AND+_exists_:${this.props.infographicDefinitions.subfield}` } } @@ -321,155 +324,156 @@ class PieChartInfographic extends React.Component { return result.json() }).then((res) => { // clean to original - let terms = {}; + const terms = {} - let localSearchField = searchField; + const localSearchField = searchField - let columns = res.results.filter( (value) => { - let hasInvalidChar = value.term.indexOf("^") === -1 && + let columns = res.results.filter((value) => { + const hasInvalidChar = value.term.indexOf("^") === -1 && value.term.indexOf(",") === -1 && value.term.indexOf("/") === -1 && value.term.indexOf("'") === -1 && value.term.indexOf("&") === -1 && value.term.indexOf("®") === -1 - let isAnAcceptedTerm = that.props.infographicDefinitions.acceptedTerms !== undefined ? - that.props.infographicDefinitions.acceptedTerms[value.term.toUpperCase()] !== undefined : - true; + const isAnAcceptedTerm = that.props.infographicDefinitions.acceptedTerms !== undefined ? + that.props.infographicDefinitions.acceptedTerms[value.term.toUpperCase()] !== undefined : + true - let excludedField = false - if(that.props.infographicDefinitions.excludedTerms !== undefined){ - if (that.props.infographicDefinitions.excludedTerms.includes(value.term.toUpperCase())){ - excludedField = true - } + let excludedField = false + if (that.props.infographicDefinitions.excludedTerms !== undefined) { + if (that.props.infographicDefinitions.excludedTerms.includes(value.term.toUpperCase())) { + excludedField = true } + } - return hasInvalidChar && isAnAcceptedTerm && !excludedField - }).map(value => { - let term = "" - let value_term = value.term.replace('.','') - - /// filter out characters for linechart items that are not useful for frontend users // - if(that.props.infographicDefinitions.subfield_filter){ - value_term = value_term.replace(that.props.infographicDefinitions.subfield_filter, '') - } - /// - - /// split by space and uppercase first letter, lowercase [0:] - value_term.split(" ").forEach( (word,idx) => { - if(idx > 0){ - term += " " - } - if(word && word.length){ - term += word[0].toUpperCase() + word.slice(1,word.length).toLowerCase().replace('.','') - } - }) - // + return hasInvalidChar && isAnAcceptedTerm && !excludedField + }).map(value => { + let term = "" + let value_term = value.term.replace('.', '') - terms[term] = value.term + // / filter out characters for linechart items that are not useful for frontend users // + if (that.props.infographicDefinitions.subfield_filter) { + value_term = value_term.replace(that.props.infographicDefinitions.subfield_filter, '') + } + // / - return term + // / split by space and uppercase first letter, lowercase [0:] + value_term.split(" ").forEach((word, idx) => { + if (idx > 0) { + term += " " + } + if (word && word.length) { + term += word[0].toUpperCase() + word.slice(1, word.length).toLowerCase().replace('.', '') + } }) + // - that.setState({ - terms - }) + terms[term] = value.term + + return term + }) + + that.setState({ + terms + }) - let timeseries_urls = columns.map( value => { - let dirtyValue = that.state.terms[value] + const timeseries_urls = columns.map(value => { + const dirtyValue = that.state.terms[value] return `${that.state.API_LINK}${that.props.api}.json?search=${searchField}+AND+${that.props.infographicDefinitions.subfield}:"${dirtyValue}"&count=${that.props.infographicDefinitions.dateField}` - }).slice(0,that.props.infographicDefinitions.lineLimiter) + }).slice(0, that.props.infographicDefinitions.lineLimiter) - Promise.all(timeseries_urls.map(that.fetchJSON).map(r => r.catch(e => e))).then( results => { + Promise.all(timeseries_urls.map(that.fetchJSON).map(r => r.catch(e => e))).then(results => { - //// ERROR handing ////// - const errors = results.map( (r, index) => { - if(typeof(r) !== "object"){ + // // ERROR handing ////// + const errors = results.map((r, index) => { + if (typeof (r) !== "object") { return index } }).filter(r => r !== undefined) - results = results.filter((r,index) => errors.indexOf(index) === -1) + results = results.filter((r, index) => errors.indexOf(index) === -1) columns = columns.filter((column, index) => errors.indexOf(index) === -1) // columns = - //// End ERROR handing ////// + // // End ERROR handing ////// - /// we only want to graph specific terms defined in acceptedTerms object - if(that.props.infographicDefinitions.acceptedTerms !== undefined){ + // / we only want to graph specific terms defined in acceptedTerms object + if (that.props.infographicDefinitions.acceptedTerms !== undefined) { columns = columns.filter(val => { return that.props.infographicDefinitions.acceptedTerms[val.toUpperCase()] !== undefined }).map(val => { return that.props.infographicDefinitions.acceptedTerms[val.toUpperCase()] }) } - ////// + // //// const useProductCodes = (that.props.globalDefs.productCodes !== undefined && that.props.infographicDefinitions.useProductCodes) const useStatesNames = (that.props.infographicDefinitions.subfield === "state.exact") - columns = columns.slice(0,that.props.infographicDefinitions.lineLimiter).map( column => { + columns = columns.slice(0, that.props.infographicDefinitions.lineLimiter).map(column => { let cleanedColumnName = "" - if(useProductCodes){ - let fullProductCode = that.props.globalDefs.productCodes[column.toUpperCase()] + if (useProductCodes) { + const fullProductCode = that.props.globalDefs.productCodes[column.toUpperCase()] cleanedColumnName = fullProductCode === undefined ? column : fullProductCode - } else if (useStatesNames){ + } + else if (useStatesNames) { cleanedColumnName = states.states[column.toUpperCase()] } cleanedColumnName = !cleanedColumnName ? column : cleanedColumnName - return cleanedColumnName.slice(0,55) + return cleanedColumnName.slice(0, 55) }) const listOfSeries = [] for (let i = 0, len = results.length; i < len; i++) { - let series = new TimeSeries({ + const series = new TimeSeries({ name: "timeseries", - columns: ["time","value"], + columns: ["time", "value"], points: results[i].results.filter(v => { - return parseInt(v.time.slice(0,4)) >= that.props.globalDefs.startYear - }).map(function(i){ - let x = i.time.slice(0,4) + '-' + i.time.slice(4,6) + '-' + i.time.slice(6,8) - let xDate = new Date(x) + return parseInt(v.time.slice(0, 4)) >= that.props.globalDefs.startYear + }).map(function (i) { + const x = i.time.slice(0, 4) + '-' + i.time.slice(4, 6) + '-' + i.time.slice(6, 8) + const xDate = new Date(x) return [new Date(xDate.getTime() - xDate.getTimezoneOffset() * -60000), i.count] }) }).monthlyRollup({ aggregation: { value: { value: sum() } }, - toTimeEvents : true + toTimeEvents: true }).toJSON() - if(series !== undefined){ + if (series !== undefined) { listOfSeries.push(series.points) } } // get all timestamps // use obj to avoid duplicates - let timestamps = {}; + let timestamps = {} // for each column of aggregated points - listOfSeries.forEach( arr => { - arr.forEach( val => { + listOfSeries.forEach(arr => { + arr.forEach(val => { // add timestamp for each series to timestamps with default 0 timestamps[val[0]] = 0 }) }) - timestamps = Object.keys(timestamps).sort(); - let timestampsPosition = {}; - timestamps.forEach( (key, i) => timestampsPosition[key] = i ); - /// + timestamps = Object.keys(timestamps).sort() + const timestampsPosition = {} + timestamps.forEach((key, i) => timestampsPosition[key] = i) + // / // normalize const normalizedSeries = [] - listOfSeries.forEach( arr => { - const normalizedSerie = new Array(timestamps.length).fill(null); + listOfSeries.forEach(arr => { + const normalizedSerie = new Array(timestamps.length).fill(null) - arr.forEach( val => { - let timestamp = val[0], - value = val[1], - index = timestampsPosition[timestamp]; + arr.forEach(val => { + const timestamp = val[0] + const value = val[1] + const index = timestampsPosition[timestamp] // add the value for the timestamp in item array normalizedSerie[index] = value @@ -478,27 +482,27 @@ class PieChartInfographic extends React.Component { }) // transpose..... from list of points per series, to a list of points per timestamp - let res = this.transpose(timestamps, normalizedSeries) + const res = this.transpose(timestamps, normalizedSeries) - let final = res.final, - findMax = res.findMax, - rows = res.rows; + const final = res.final + const findMax = res.findMax + const rows = res.rows - let series = new TimeSeries({ + const series = new TimeSeries({ name: "timeseries", columns: ["time"].concat(columns), - points: final.sort( (a,b) => a[0] - b[0]) + points: final.sort((a, b) => a[0] - b[0]) }) // set style according to categories - let legendStyle = styler(columns.map((column,idx)=> { + const legendStyle = styler(columns.map((column, idx) => { return { key: column, color: that.props.globalDefs.lineChartConfig.colors[idx], width: 2 } })) - let legendStyle1 = legendStyle.lineChartStyle() + const legendStyle1 = legendStyle.lineChartStyle() Object.keys(legendStyle1).forEach(value => { legendStyle1[value].selected.opacity = 0 legendStyle1[value].normal.opacity = 0 @@ -506,10 +510,10 @@ class PieChartInfographic extends React.Component { legendStyle1[value].muted.opacity = 0 }) - let columnStyles = Object.keys(legendStyle1).map(column => { + const columnStyles = Object.keys(legendStyle1).map(column => { let formattedColumn = "" - if(column.length){ - formattedColumn = column[0].toUpperCase() + column.slice(1,column.length) + if (column.length) { + formattedColumn = column[0].toUpperCase() + column.slice(1, column.length) } return { label: formattedColumn, @@ -518,9 +522,9 @@ class PieChartInfographic extends React.Component { } }) - let allMaxes = {} + const allMaxes = {} _.zip(columnStyles.map(s => s.label), normalizedSeries).forEach(value => { - allMaxes[value[0]] = !!value[1] ? Math.max(...value[1]) : 0 + allMaxes[value[0]] = value[1] ? Math.max(...value[1]) : 0 }) this.setState({ @@ -533,51 +537,51 @@ class PieChartInfographic extends React.Component { sparklineDataMax: Math.max(...findMax), lineChartColumns: ["time"].concat(columns), lineChartLoaded: that.state.lineChartLoaded += 1 - },function(){ + }, function () { this.setState({isOpen: true}) }) - columnStyles.slice(0,3).forEach( styl =>{ + columnStyles.slice(0, 3).forEach(styl => { this.onSelectionChange(styl) }) - if(this.refs){ + if (this.refs) { this.refs.child.setState({ activeIndex: index }) } this.setPieChartText() - let vals = $("text").filter(function () { - return $(this).attr("transform") == "rotate(-90)" + const vals = $("text").filter(function () { + return $(this).attr("transform") == "rotate(-90)" }) - if(vals.length){ - $(vals[0]).attr("x",that.props.infographicDefinitions.xLegendCoordinate) + if (vals.length) { + $(vals[0]).attr("x", that.props.infographicDefinitions.xLegendCoordinate) } }) }) } - transpose (timestamps, normalizedSeries){ + transpose (timestamps, normalizedSeries) { // transpose..... from list of points per series, to a list of points per timestamp - let findMax = [], - final = [], - rows = []; + const findMax = [] + const final = [] + const rows = [] for (let i = 0, len_i = normalizedSeries[0].length; i < len_i; i++) { - let row = [] + const row = [] for (let j = 0, len_j = normalizedSeries.length; j < len_j; j++) { - let val = normalizedSeries[j][i] || 0 + const val = normalizedSeries[j][i] || 0 row.push(val) findMax.push(val) } rows.push(row) } - timestamps.forEach( (key, i) => { - let int = parseInt(key) - if(int > 0){ + timestamps.forEach((key, i) => { + const int = parseInt(key) + if (int > 0) { final.push([int].concat(rows[i])) } - }); + }) return { findMax: findMax, final: final, @@ -586,20 +590,20 @@ class PieChartInfographic extends React.Component { } onSelectionChange (selectionObj) { - var selection = selectionObj.label; - var selectionName = this.props.infographicDefinitions.selectionPostFix !== undefined ? - selection + this.props.infographicDefinitions.selectionPostFix : - selection; + const selection = selectionObj.label + const selectionName = this.props.infographicDefinitions.selectionPostFix !== undefined ? + selection + this.props.infographicDefinitions.selectionPostFix : + selection let toggle = null - let selected = [] - let maxes = [] + const selected = [] + const maxes = [] const columnStyles = this.state.columnStyles.map(obj => { - if(obj.label === selection){ - obj.isSelected = obj.isSelected ? false : true - toggle = obj.isSelected ? 1 : 0; + if (obj.label === selection) { + obj.isSelected = !obj.isSelected + toggle = obj.isSelected ? 1 : 0 } - if(obj.isSelected){ + if (obj.isSelected) { selected.push(obj.label) maxes.push(this.state.allMaxes[obj.label]) } @@ -608,16 +612,17 @@ class PieChartInfographic extends React.Component { const legendStyle = {} Object.keys(this.state.legendStyle).map(columnName => { - var obj = this.state.legendStyle[columnName] - if(selection === columnName){ - if(toggle){ - obj.normal.opacity = toggle - obj.selected.opacity = toggle - obj.highlighted.opacity = toggle - } else { - obj.normal.opacity = 0.005 - obj.selected.opacity = 0.005 - obj.highlighted.opacity = 0.005 + const obj = this.state.legendStyle[columnName] + if (selection === columnName) { + if (toggle) { + obj.normal.opacity = toggle + obj.selected.opacity = toggle + obj.highlighted.opacity = toggle + } + else { + obj.normal.opacity = 0.005 + obj.selected.opacity = 0.005 + obj.highlighted.opacity = 0.005 } } @@ -625,7 +630,7 @@ class PieChartInfographic extends React.Component { }) this.setState({ - sparklineDataMax: maxes.length ? Math.max(...maxes)+Math.round(Math.random() * 100, 2)/100 : 10, + sparklineDataMax: maxes.length ? Math.max(...maxes) + Math.round(Math.random() * 100, 2) / 100 : 10, sparklineData: this.state.sparklineData, choosenColumn: selectionObj, selection, @@ -633,29 +638,29 @@ class PieChartInfographic extends React.Component { legendStyle, columnStyles, selectionName: selectionName - }, function(){ + }, function () { this.setTextStyle() this.setPieChartText() }) } - setTextStyle(){ - $("text").css("font-family",this.props.globalDefs.font.fontFamily) + setTextStyle () { + $("text").css("font-family", this.props.globalDefs.font.fontFamily) $('text').css('fill', this.props.globalDefs.font.color) } - handleTrackerChanged(t) { + handleTrackerChanged (t) { if (t) { - const e = this.state.sparklineData.atTime(t); + const e = this.state.sparklineData.atTime(t) const eventTime = new Date( - e.begin().getTime() + e.begin().getTime() ) - const eventData = e.toJSON().data; + const eventData = e.toJSON().data - let infoValues = this.state.selected.map( label => { + let infoValues = this.state.selected.map(label => { return { - label : label.length < 20 ? label : label.slice(0,20) + " ... ", + label: label.length < 20 ? label : label.slice(0, 20) + " ... ", value: eventData[label] } }) @@ -666,8 +671,8 @@ class PieChartInfographic extends React.Component { } ] // Show only 5 labels - infoValues = infoValues.length > 5 ? defaultInfoValues : infoValues; - const infoHeight = infoValues.length <= 5 ? (infoValues.length * 10 + 30) : 0; + infoValues = infoValues.length > 5 ? defaultInfoValues : infoValues + const infoHeight = infoValues.length <= 5 ? (infoValues.length * 10 + 30) : 0 // const eventValue = e.toJSON().data[this.state.selection] // const v = `${eventValue}`; @@ -679,33 +684,33 @@ class PieChartInfographic extends React.Component { infoHeight: infoHeight }, this.setTextStyle()) - } else { - this.setState({ tracker: null, trackerValue: null, trackerEvent: null }); + } + else { + this.setState({ tracker: null, trackerValue: null, trackerEvent: null }) } } - renderOption (option){ + renderOption (option) { return (
        - {} }/> + {} }/>
        -
        - {" "}{option.label} + width: 20, + height: 20, + backgroundColor: option.color, + display: 'inline-block', + paddingTop: 5 + }} /> + {" "}{option.label}
        - ); + ) } - handleChartResize(width){ + handleChartResize (width) { this.setState({width}) } - onClose (){ + onClose () { this.setState({isOpen: true}) } @@ -722,27 +727,27 @@ class PieChartInfographic extends React.Component { render (): ?React.Element { if (!this.state.data) return - var viewBox = this.props.infographicDefinitions.pieChartConfig.viewBox; - $('.recharts-surface').removeAttr('viewBox'); - $('.recharts-surface').each(function () { $(this)[0].setAttribute('viewBox', viewBox) }); - $('.recharts-wrapper').width(this.props.infographicDefinitions.pieChartConfig.widthReset) - this.setPieChartText() + const viewBox = this.props.infographicDefinitions.pieChartConfig.viewBox + $('.recharts-surface').removeAttr('viewBox') + $('.recharts-surface').each(function () { $(this)[0].setAttribute('viewBox', viewBox) }) + $('.recharts-wrapper').width(this.props.infographicDefinitions.pieChartConfig.widthReset) + this.setPieChartText() return ( -
        +

        {Parser(this.props.infographicDefinitions.title)} - {' '}View by:{' '} + {' '}View by:{' '}

        -
        +
        -

        - Each {this.props.infographicDefinitions.pieChartCategoryName}
        - as % of all {this.props.infographicDefinitions.pieChartApiName} +

        + Each {this.props.infographicDefinitions.pieChartCategoryName}
        + as % of all {this.props.infographicDefinitions.pieChartApiName}

        @@ -759,74 +764,74 @@ class PieChartInfographic extends React.Component {

        -
        +
        { !this.state.sparklineData ? -
        - -
        - : - { this.setState({ timerange }) }} - trackerPosition={this.state.tracker} - onTrackerChanged={this.handleTrackerChanged} - minTime={this.state.minTime} - maxTime={this.state.maxTime} - onChartResize={this.handleChartResize} - {...this.props.globalDefs.lineChartConfig.chartContainer} - > - - - - - - - - +
        + +
        + : + { this.setState({ timerange }) }} + trackerPosition={this.state.tracker} + onTrackerChanged={this.handleTrackerChanged} + minTime={this.state.minTime} + maxTime={this.state.maxTime} + onChartResize={this.handleChartResize} + {...this.props.globalDefs.lineChartConfig.chartContainer} + > + + + + + + + + }
        { !this.state.sparklineData ? null : -
        -
        -
        Select {this.props.parent.state.choice.subfieldLabel} to Compare
        -
        - {this.state.columnStyles && this.state.columnStyles.map(this.createCheckbox)} +
        +
        +
        Select {this.props.parent.state.choice.subfieldLabel} to Compare
        +
        + {this.state.columnStyles && this.state.columnStyles.map(this.createCheckbox)}
        } -
        +
        ) } } @@ -835,6 +840,3 @@ class PieChartInfographic extends React.Component { export default PieChartInfographic - - - diff --git a/src/components/QueryExplorer.jsx b/src/components/QueryExplorer.tsx similarity index 91% rename from src/components/QueryExplorer.jsx rename to src/components/QueryExplorer.tsx index e28b30f66..c9ec20529 100644 --- a/src/components/QueryExplorer.jsx +++ b/src/components/QueryExplorer.tsx @@ -51,10 +51,10 @@ const QueryExplorer = (props: tPROPS) => { key={k} className='bg-gray-lightest marg-t-2 pad-2 relative' id={'explorer' + (name ? ('-' + name) : '')}> - +

        Example query

        + id={'title' + (name ? ('-' + name) : '')}> {title}

        { @@ -75,7 +75,7 @@ const QueryExplorer = (props: tPROPS) => { } {params && params.toString() &&
          + id={'params' + (name ? ('-' + name) : '')}> { params.map((param: string, i) => { const html: string = marked(param) @@ -85,8 +85,7 @@ const QueryExplorer = (props: tPROPS) => { key={i} className='marg-t-1 pad-t-1 pad-l-2 pad-r-2 b-b-1 qe-li reverse-pre relative' tabIndex={0} - dangerouslySetInnerHTML={{__html: html}}> - + dangerouslySetInnerHTML={{__html: html}} /> ) }) } diff --git a/src/components/QueryTour.jsx b/src/components/QueryTour.tsx similarity index 95% rename from src/components/QueryTour.jsx rename to src/components/QueryTour.tsx index a49b3ad40..a4561b3fe 100644 --- a/src/components/QueryTour.jsx +++ b/src/components/QueryTour.tsx @@ -40,9 +40,9 @@ class QueryTour extends React.Component { } handleJoyrideCallback = data => { - const { status, type } = data; + const { status, type } = data if ([STATUS.FINISHED, STATUS.SKIPPED].includes(status)) { - this.setState({ tourRun: false }); + this.setState({ tourRun: false }) } }; @@ -89,7 +89,7 @@ class QueryTour extends React.Component { { // array let type: string = '' // one_of, etc - let values: ?Object = null + let values: { value?: Record } | null | undefined = null // of strings let type2: string = '' // description text, can have docs @@ -51,21 +64,21 @@ const _renderLi = (props: tLiProps) => { // query syntax pattern let pattern: string = '' // whether field has .exact - let isExact: bool = false + let isExact: boolean = false if (field) { - desc = field.description - pattern = field.pattern - type = field.type - values = field.possible_values - isExact = field.is_exact && field.is_exact + desc = field.description || '' + pattern = field.pattern || '' + type = field.type || '' + values = field.possible_values || null + isExact = !!field.is_exact } // field is an array (will have subkey of items) if (field && field.items) { - desc = field.items.description - pattern = field.items.pattern - type2 = field.items.type + desc = field.items.description || '' + pattern = field.items.pattern || '' + type2 = field.items.type || '' } const divCx: string = 'col t-range-marg-t-2 t-range-6 d-3' @@ -95,7 +108,7 @@ const _renderLi = (props: tLiProps) => { { desc &&
          } { @@ -124,7 +137,7 @@ const _renderLi = (props: tLiProps) => { } { values && - Object.keys(values.value).length <= 4 && + values.value && Object.keys(values.value).length <= 4 && @@ -147,7 +160,7 @@ const _renderLi = (props: tLiProps) => { } { values && - Object.keys(values.value).length > 4 && + values.value && Object.keys(values.value).length > 4 &&
          { // @TODO we can probably do away // with this intermediate step -const _renderFDA = (key: string, data) => { +const _renderFDA = (key: string, data :any) => { const fieldKeys: Array = data ? Object.keys(data) : [''] - const fieldData: Array = fieldKeys.map((key: string, i) => { + const fieldData: Array = fieldKeys.map((key: string, i) => { const field: void|Object = data && data[key] if (!field) return null @@ -200,7 +213,7 @@ type tPROPS = { * @return {React.Element} [always return an element] */ const Fields = ({ data, fields, k }: tPROPS) => { - const fieldData: Array = data.map((key: string, i) => { + const fieldData: any[] = data.map((key: string, i) => { const field = yamlGet(key, fields) if (!field) { @@ -229,7 +242,7 @@ const Fields = ({ data, fields, k }: tPROPS) => {
            + style={{marginLeft: "0"}}> {fieldData}
        diff --git a/src/components/RenderContentObject/KeyFacts.jsx b/src/components/RenderContentObject/KeyFacts.tsx similarity index 93% rename from src/components/RenderContentObject/KeyFacts.jsx rename to src/components/RenderContentObject/KeyFacts.tsx index 65d284ad9..0feb95fc7 100644 --- a/src/components/RenderContentObject/KeyFacts.jsx +++ b/src/components/RenderContentObject/KeyFacts.tsx @@ -155,8 +155,15 @@ const frequency = { 'problem': 'Quarterly' } } -class KeyFacts extends React.Component { - constructor (props: Object) { +interface KeyFactsProps { + status: string; + noun_name: string; + endpoint_name: string; + harmonized: boolean; +} + +class KeyFacts extends React.Component { + constructor (props: KeyFactsProps) { super(props) this.state = { lastUpdated: null @@ -168,8 +175,8 @@ class KeyFacts extends React.Component { } _getStatus () { - const _handleResponse = data => { - const lastUpdated = data.find(dataset => dataset.endpoint === this.props.status).last_updated + const _handleResponse = (data: any) => { + const lastUpdated = data.find((dataset : any) => dataset.endpoint === this.props.status).last_updated console.log(lastUpdated) this.setState({ lastUpdated, @@ -189,7 +196,7 @@ class KeyFacts extends React.Component {
        Source of the data:
        { - sourceLink[this.props.noun_name].hasOwnProperty(this.props.endpoint_name) && + sourceLink[this.props.noun_name as keyof typeof sourceLink]?.hasOwnProperty(this.props.endpoint_name) && {source[this.props.noun_name][this.props.endpoint_name]} diff --git a/src/components/RenderContentObject/Values.jsx b/src/components/RenderContentObject/Values.tsx similarity index 100% rename from src/components/RenderContentObject/Values.jsx rename to src/components/RenderContentObject/Values.tsx diff --git a/src/components/RenderContentObject/index.jsx b/src/components/RenderContentObject/index.tsx similarity index 86% rename from src/components/RenderContentObject/index.jsx rename to src/components/RenderContentObject/index.tsx index b604a9fe2..331ef0f07 100644 --- a/src/components/RenderContentObject/index.jsx +++ b/src/components/RenderContentObject/index.tsx @@ -12,20 +12,21 @@ import ContentAccordion from '../ContentAccordion' const linkCx: string = 'b-b-1 clr-primary-alt-dark block font-size-5 pad-1 pad-l-2 txt-l row' type tPROPS = { - obj: Object; - k: number; - examples: Object; - explorers: Object; - infographics: Object; - infographicDefinitions: Object; - fieldsMapped: Object; - fieldsFlattened: Object; - fields: Object; - isMenu: boolean; - onClick: Function; - meta: Object; - toggleSection: Function; - activeHeader: Array; + obj?: Object; + k?: number; + examples?: Object; + explorers?: Object; + infographics?: Object; + infographicDefinitions?: Object; + fieldsMapped?: Object; + fieldsFlattened?: Object; + fields?: Object; + isMenu?: boolean; + onClick?: Function; + meta?: Object; + toggleSection?: Function; + activeHeader?: Array; + i?: number; }; // called by Content @@ -90,10 +91,10 @@ const RenderContentObject = (props: tPROPS) => { // if not just a straight string // loop over array and pull out headers if (isList) { - let key_text = key.replace(/(#+ )/, '') + const key_text = key.replace(/(#+ )/, '') // get header level from counting '#' - let level: number = (key.match(/#/g)||[]).length + const level: number = (key.match(/#/g) || []).length const btnCx = cx({ 'menu-item row': true, @@ -128,24 +129,24 @@ const RenderContentObject = (props: tPROPS) => { isMenu /> ) - } else { - if (content.indexOf('##') === -1) return + } + if (content.indexOf('##') === -1) return - const html: string = content.replace(/(#+ )/, '') + const html: string = content.replace(/(#+ )/, '') + + return ( + + ) - return ( - - ) - } }) }
        diff --git a/src/components/ResearchBox.jsx b/src/components/ResearchBox.tsx similarity index 85% rename from src/components/ResearchBox.jsx rename to src/components/ResearchBox.tsx index 756b82090..696e9fb35 100644 --- a/src/components/ResearchBox.jsx +++ b/src/components/ResearchBox.tsx @@ -23,11 +23,11 @@ const ResearchBox = (props: tPROPS) => { } = props return ( -
        + ) diff --git a/src/components/SideBar/CustomMenu.jsx b/src/components/SideBar/CustomMenu.tsx similarity index 100% rename from src/components/SideBar/CustomMenu.jsx rename to src/components/SideBar/CustomMenu.tsx diff --git a/src/components/SideBar/DownloadsMenu.jsx b/src/components/SideBar/DownloadsMenu.tsx similarity index 75% rename from src/components/SideBar/DownloadsMenu.jsx rename to src/components/SideBar/DownloadsMenu.tsx index cb07252f0..c90258215 100644 --- a/src/components/SideBar/DownloadsMenu.jsx +++ b/src/components/SideBar/DownloadsMenu.tsx @@ -13,20 +13,27 @@ import RenderContentObject from '../RenderContentObject' * @param {Object} e [event object] * @return {void} [if element not null, scroll into view] */ -const _scrollIntoView = e => { +const _scrollIntoView = (e: React.MouseEvent) => { if (typeof document === 'undefined') return // ### Responsible use of the data -> responsible-use-of-the-data // some cases like 'What are enforcement reports?' result in an // extra - at the end because the ? gets replaced with a - // but, it works and is easy, and isn't visible to the user - //const id: string = e.target.textContent.replace(/(\s|\W)/g, '-').toLowerCase() - const el: ?Object = document.getElementById(e.target.textContent) + // const id: string = e.target.textContent.replace(/(\s|\W)/g, '-').toLowerCase() + const el: HTMLElement | null = document.getElementById((e.target as HTMLButtonElement).textContent || '') return el && el.scrollIntoView({ behavior: 'smooth', }) } -const DownloadsMenu = (props: Object) => { +interface DownloadsMenuProps { + bottomPos: number; + content: (string | Object)[]; + isBottom: boolean; + isFixed?: boolean; +} + +const DownloadsMenu = (props: DownloadsMenuProps) => { const { bottomPos, content, @@ -44,15 +51,15 @@ const DownloadsMenu = (props: Object) => { className={menuCx} style={{ // stick to bottom if near footer - bottom: isBottom && `${bottomPos}px`, - height: isBottom && 'initial', + bottom: isBottom ? `${bottomPos}px` : undefined, + height: isBottom ? 'initial' : undefined, // to account for the gradient overflow - paddingBottom: !isBottom && '75px', + paddingBottom: !isBottom ? '75px' : undefined, }}> { content.map((c: string|Object, i: number) => { let header: number = 0 - if (c === 'Animal Drug' || c === 'Food' || c === 'Human Drug' || c === 'Medical Device' || c=== 'Tobacco' || c === 'Other'){ + if (c === 'Animal Drug' || c === 'Food' || c === 'Human Drug' || c === 'Medical Device' || c === 'Tobacco' || c === 'Other') { header = 1 } diff --git a/src/components/SideBar/ReferenceMenu.jsx b/src/components/SideBar/ReferenceMenu.tsx similarity index 81% rename from src/components/SideBar/ReferenceMenu.jsx rename to src/components/SideBar/ReferenceMenu.tsx index 4790820ba..f093ce1d8 100644 --- a/src/components/SideBar/ReferenceMenu.jsx +++ b/src/components/SideBar/ReferenceMenu.tsx @@ -7,7 +7,7 @@ import $ from 'jquery' import ARIA from '../../constants/aria' import RenderContentObject from '../RenderContentObject' import ApiKey from '../ApiKey' -var scrollIntoView = require('scroll-into-view'); +const scrollIntoView = require('scroll-into-view') /** * @description [semantic menu means no links. we don't leave the page] @@ -15,30 +15,36 @@ var scrollIntoView = require('scroll-into-view'); * @param {Object} e [event object] * @return {void} [if element not null, scroll into view] */ -const _scrollIntoView = e => { +const _scrollIntoView = (e: any) => { if (typeof document === 'undefined') return // ### Responsible use of the data -> responsible-use-of-the-data // some cases like 'What are enforcement reports?' result in an // extra - at the end because the ? gets replaced with a - // but, it works and is easy, and isn't visible to the user const id: string = e.target.textContent.replace(/(\s|\W)/g, '-').toLowerCase() - const el: ?Object = document.getElementById(id) + const el: Object | null | undefined = document.getElementById(id) scrollIntoView(el, { - align:{ + align: { top: 0.16, left: 0 }, - ease: function(value){ - return 1 - Math.pow(1 - value, value / 5); + ease: function (value: number) { + return 1 - Math.pow(1 - value, value / 5) } }) } -const _scrollToTop = e => { +const _scrollToTop = (e: any) => { return $('html, body').animate({ scrollTop: 0 }, 'smooth') } -const ReferenceMenu = (props: Object) => { +interface ReferenceMenuProps { + content: (string | Object)[]; + isBottom?: boolean; + isFixed?: boolean; +} + +const ReferenceMenu = (props: ReferenceMenuProps) => { const { content, isBottom, @@ -59,7 +65,7 @@ const ReferenceMenu = (props: Object) => { // stick to bottom if near footer height: 'initial', // to account for the gradient overflow - paddingTop: !isFixed && !isBottom && '40px' + paddingTop: !isFixed && !isBottom ? '40px' : undefined }}> { content.map((c: string|Object, i: number) => { @@ -88,7 +94,7 @@ const ReferenceMenu = (props: Object) => { if (c.startsWith('#') === false) return // get header level from counting '#' - const level: number = (c.match(/#/g)||[]).length + const level: number = (c.match(/#/g) || []).length const btnCx = cx({ 'menu-item row': true, @@ -109,9 +115,9 @@ const ReferenceMenu = (props: Object) => { } {
        { [].forEach.call(elements, (sticky) => { - sticky.setAttribute('data-sticky-initial', sticky.getBoundingClientRect().top); - }); - }; + sticky.setAttribute('data-sticky-initial', sticky.getBoundingClientRect().top) + }) + } - const stickies = document.querySelectorAll('[data-sticky]'); - setInitialHeights(stickies); + const stickies = document.querySelectorAll('[data-sticky]') + setInitialHeights(stickies) document.addEventListener('scroll', this.scrollListener) } - componentWillUnmount() { - document.removeEventListener('scroll', this.scrollListener); + componentWillUnmount () { + document.removeEventListener('scroll', this.scrollListener) } - scrollListener() { - const stickies = document.querySelectorAll('[data-sticky]'); - const top = document.documentElement.scrollTop || document.body.scrollTop; + scrollListener () { + const stickies = document.querySelectorAll('[data-sticky]') + const top = document.documentElement.scrollTop || document.body.scrollTop const bottom = document.documentElement.scrollHeight || document.body.scrollHeight; [].forEach.call(stickies, (sticky) => { - const stickyInitial = parseInt(sticky.getAttribute('data-sticky-initial'), 10); - const stickyEnter = parseInt(sticky.getAttribute('data-sticky-enter'), 10) || stickyInitial; - const stickyExit = parseInt(sticky.getAttribute('data-sticky-exit'), 10) || bottom; + const stickyInitial = parseInt(sticky.getAttribute('data-sticky-initial'), 10) + const stickyEnter = parseInt(sticky.getAttribute('data-sticky-enter'), 10) || stickyInitial + const stickyExit = parseInt(sticky.getAttribute('data-sticky-exit'), 10) || bottom let stickySidebar = false @@ -46,12 +46,14 @@ export class StickySidebar extends React.Component { sticky.classList.add('sticky-sidebar') !this.props.sidebarFixed && this.props.toggleFixed(true) - } else { + } + else { sticky.classList.remove('sticky-sidebar') this.props.sidebarFixed && this.props.toggleFixed(false) } - } else if (sticky.classList.contains('sticky-sidebar')) { + } + else if (sticky.classList.contains('sticky-sidebar')) { sticky.classList.remove('sticky-sidebar') this.props.sidebarFixed && this.props.toggleFixed(false) @@ -60,8 +62,8 @@ export class StickySidebar extends React.Component { }) } - render() { - const { className, enter, exit, children } = this.props; + render () { + const { className, enter, exit, children } = this.props return (
        - {children} + {children}
        ) } @@ -82,6 +84,6 @@ StickySidebar.propTypes = { enter: PropTypes.string, exit: PropTypes.string, children: PropTypes.node, -}; +} export default StickySidebar diff --git a/src/components/Table.jsx b/src/components/Table.jsx deleted file mode 100644 index 0f5589fc6..000000000 --- a/src/components/Table.jsx +++ /dev/null @@ -1,63 +0,0 @@ -import React from 'react'; -import PropTypes from 'prop-types'; - -type tPROPS = { - cols : PropTypes.array, - rows: PropTypes.array -}; - -const Table = (props:tPROPS) => { - - class HtmlTable extends React.Component { - - constructor(props:tPROPS) { - super(props); - this.rows = props.rows; - this.cols = props.cols; - this.labels = props.labels ? props.labels : props.cols; - this.formatters = props.formatters || {}; - } - format(cell, row, col, rowIndex, colIndex) { - let formatter = this.formatters[col]; - if(formatter) { - return formatter(cell, row); - } - return cell; - } - render() { - var head = ( - - { - - this.labels.map((cell, j) => {cell}) - } - - ); - - var body = this.rows.map((row, i) => { - return ( - - { - this.cols.map((col, j) => - {this.format(row[col], row , col, i, j)}) - } - - ); - }); - - return ( - - {head} - - - {body} - -
        ); - } - } - - return ; -} - -Table.displayName = 'component/Table'; -export default Table; diff --git a/src/components/Table.tsx b/src/components/Table.tsx new file mode 100644 index 000000000..98af6be07 --- /dev/null +++ b/src/components/Table.tsx @@ -0,0 +1,63 @@ +import React from 'react' +import PropTypes from 'prop-types' + +type tPROPS = { + cols : PropTypes.array, + rows: PropTypes.array +}; + +const Table = (props:tPROPS) => { + + class HtmlTable extends React.Component { + + constructor (props:tPROPS) { + super(props) + this.rows = props.rows + this.cols = props.cols + this.labels = props.labels ? props.labels : props.cols + this.formatters = props.formatters || {} + } + format (cell, row, col, rowIndex, colIndex) { + const formatter = this.formatters[col] + if (formatter) { + return formatter(cell, row) + } + return cell + } + render () { + const head = ( + + { + + this.labels.map((cell, j) => {cell}) + } + + ) + + const body = this.rows.map((row, i) => { + return ( + + { + this.cols.map((col, j) => + {this.format(row[col], row, col, i, j)}) + } + + ) + }) + + return ( + + {head} + + + {body} + +
        ) + } + } + + return +} + +Table.displayName = 'component/Table' +export default Table diff --git a/src/components/UpdateStatus.jsx b/src/components/UpdateStatus.tsx similarity index 98% rename from src/components/UpdateStatus.jsx rename to src/components/UpdateStatus.tsx index 6f344c508..874ef6cce 100644 --- a/src/components/UpdateStatus.jsx +++ b/src/components/UpdateStatus.tsx @@ -29,7 +29,8 @@ class UpdateStatus extends React.Component { if (endpointData.meta.last_updated === moment().format("YYYY-MM-DD") || endpointData.meta.last_updated === moment().subtract(1, 'days').format("YYYY-MM-DD")) { status = "updated" - } else { + } + else { status = "delayed" } this.setState({ diff --git a/src/constants/aria.jsx b/src/constants/aria.jsx deleted file mode 100644 index e20b62c82..000000000 --- a/src/constants/aria.jsx +++ /dev/null @@ -1,9 +0,0 @@ -const ARIA: Object = Object.freeze({ - hide: { - 'aria-hidden': true, - role: 'presentation', - tabIndex: -1, - }, -}) - -export default ARIA diff --git a/src/constants/aria.tsx b/src/constants/aria.tsx new file mode 100644 index 000000000..342614889 --- /dev/null +++ b/src/constants/aria.tsx @@ -0,0 +1,15 @@ +const ARIA: Readonly<{ + hide: { + 'aria-hidden': boolean; + role: string; + tabIndex: number; + }; +}> = Object.freeze({ + hide: { + 'aria-hidden': true, + role: 'presentation', + tabIndex: -1, + }, +}); + +export default ARIA; \ No newline at end of file diff --git a/src/constants/types/content.jsx b/src/constants/types/content.tsx similarity index 100% rename from src/constants/types/content.jsx rename to src/constants/types/content.tsx diff --git a/src/constants/types/examples.jsx b/src/constants/types/examples.tsx similarity index 100% rename from src/constants/types/examples.jsx rename to src/constants/types/examples.tsx diff --git a/src/constants/types/fields.jsx b/src/constants/types/fields.tsx similarity index 100% rename from src/constants/types/fields.jsx rename to src/constants/types/fields.tsx diff --git a/src/constants/types/menu.jsx b/src/constants/types/menu.tsx similarity index 100% rename from src/constants/types/menu.jsx rename to src/constants/types/menu.tsx diff --git a/src/constants/types/template.jsx b/src/constants/types/template.tsx similarity index 100% rename from src/constants/types/template.jsx rename to src/constants/types/template.tsx diff --git a/src/containers/ApiStatusContainer.jsx b/src/containers/ApiStatusContainer.tsx similarity index 100% rename from src/containers/ApiStatusContainer.jsx rename to src/containers/ApiStatusContainer.tsx diff --git a/src/containers/BreadcrumbsContainer.jsx b/src/containers/BreadcrumbsContainer.tsx similarity index 91% rename from src/containers/BreadcrumbsContainer.jsx rename to src/containers/BreadcrumbsContainer.tsx index 1d79b505b..b3c26183a 100644 --- a/src/containers/BreadcrumbsContainer.jsx +++ b/src/containers/BreadcrumbsContainer.tsx @@ -23,12 +23,13 @@ const BreadcrumbsContainer = function (ComposedBreadcrumbs: ReactClass): ReactCl } _toggleDropdownContent (e) { - let title = e.target.getAttribute('title') + const title = e.target.getAttribute('title') if (this.state.activeDropdown != title) { this.setState({ activeDropdown: title }) - } else { + } + else { this.setState({ activeDropdown: ' ' }) @@ -45,7 +46,7 @@ const BreadcrumbsContainer = function (ComposedBreadcrumbs: ReactClass): ReactCl _showDropdownContent (e) { if (this.state.showMobileNav == false) { - let title = e.target.getAttribute('title') + const title = e.target.getAttribute('title') this.setState({ activeDropdown: title }) diff --git a/src/containers/BreakpointContainer.jsx b/src/containers/BreakpointContainer.tsx similarity index 100% rename from src/containers/BreakpointContainer.jsx rename to src/containers/BreakpointContainer.tsx diff --git a/src/containers/DocSidebarContainer.jsx b/src/containers/DocSidebarContainer.tsx similarity index 79% rename from src/containers/DocSidebarContainer.jsx rename to src/containers/DocSidebarContainer.tsx index 2599060eb..f3969acea 100644 --- a/src/containers/DocSidebarContainer.jsx +++ b/src/containers/DocSidebarContainer.tsx @@ -8,12 +8,12 @@ type tSTATE = { showMobileSidebar: boolean; }; -function checkArray(arr, path) { - let activeHeader = [] - for (var i = 0; i < arr.length; i++) { +function checkArray (arr, path) { + const activeHeader = [] + for (let i = 0; i < arr.length; i++) { if (Array.isArray(arr[i].items)) { - let header = checkObject(arr[i], path)[0] - if (typeof header != 'undefined') { + const header = checkObject(arr[i], path)[0] + if (typeof header !== 'undefined') { activeHeader.push(header) } } @@ -24,10 +24,10 @@ function checkArray(arr, path) { return [false, activeHeader] } -function checkObject(obj, path) { +function checkObject (obj, path) { const activeHeaders = [] - let active = checkArray(obj.items, path) - let id = obj.id + const active = checkArray(obj.items, path) + const id = obj.id if (active[0] === true) { activeHeaders.push(id) } @@ -52,11 +52,11 @@ const DocSidebarContainer = function (ComposedDocSidebar: ReactClass): ReactClas componentDidMount () { let activeHeaders = this.state.activeHeader const yaml = this.props.yaml - let returnedHeaders = [] + const returnedHeaders = [] for (let i = 0; i < yaml.length; i++) { returnedHeaders.push(checkObject(yaml[i], window.location.pathname)) } - for (let i = 0; i < returnedHeaders.length; i++ ) { + for (let i = 0; i < returnedHeaders.length; i++) { returnedHeaders[i].length > 0 && (activeHeaders = activeHeaders.concat(returnedHeaders[i])) } this.setState({ @@ -69,11 +69,11 @@ const DocSidebarContainer = function (ComposedDocSidebar: ReactClass): ReactClas if (this.state.path !== window.location.pathname) { let activeHeaders = this.state.activeHeader const yaml = this.props.yaml - let returnedHeaders = [] + const returnedHeaders = [] for (let i = 0; i < yaml.length; i++) { returnedHeaders.push(checkObject(yaml[i], window.location.pathname)) } - for (let i = 0; i < returnedHeaders.length; i++ ) { + for (let i = 0; i < returnedHeaders.length; i++) { returnedHeaders[i].length > 0 && (activeHeaders = activeHeaders.concat(returnedHeaders[i])) } this.setState({ @@ -93,21 +93,22 @@ const DocSidebarContainer = function (ComposedDocSidebar: ReactClass): ReactClas } _toggleSection (e) { - let activeHeader = this.state.activeHeader.slice() - let title = e.target.getAttribute('title') + const activeHeader = this.state.activeHeader.slice() + const title = e.target.getAttribute('title') if (activeHeader.indexOf(title) === -1) { activeHeader.push(title) this.setState({ activeHeader: activeHeader }) - } else { + } + else { this.setState({ activeHeader: activeHeader.filter(t => t !== title) }) } } - render(): React.Element { + render (): React.Element { return ( { - + this.setState({ data, }) diff --git a/src/containers/EndpointStatusContainer.jsx b/src/containers/EndpointStatusContainer.tsx similarity index 100% rename from src/containers/EndpointStatusContainer.jsx rename to src/containers/EndpointStatusContainer.tsx diff --git a/src/containers/FieldExplorerContainer.jsx b/src/containers/FieldExplorerContainer.tsx similarity index 87% rename from src/containers/FieldExplorerContainer.jsx rename to src/containers/FieldExplorerContainer.tsx index f3668d414..aea61eb6b 100644 --- a/src/containers/FieldExplorerContainer.jsx +++ b/src/containers/FieldExplorerContainer.tsx @@ -13,19 +13,20 @@ const FieldExplorerContainer = function (ComposedFieldExplorer: ReactClass): Rea }; _updateField (val) { - if ( val !== 'fields' ) { + if (val !== 'fields') { this.setState({ selectedField: val.value, }) - } else ( + } + else ( this.setState({ selectedField: val }) ) } - _updateSelected(e) { - let title = e.target.getAttribute('title') + _updateSelected (e) { + const title = e.target.getAttribute('title') if (this.state.selectedField !== title) { this.setState({ selectedField: title diff --git a/src/containers/InfographicContainer.jsx b/src/containers/InfographicContainer.tsx similarity index 93% rename from src/containers/InfographicContainer.jsx rename to src/containers/InfographicContainer.tsx index 4cd0a1f93..ce7708806 100644 --- a/src/containers/InfographicContainer.jsx +++ b/src/containers/InfographicContainer.tsx @@ -157,12 +157,12 @@ class InfographicContainer extends React.Component { * @returns {Promise} [like all async methods, returns a promise] */ _fetchQueryAndUpdate (searchParam: string, countParam: string) { - var that = this - let download_url = API_LINK + '/download.json' + const that = this + const download_url = API_LINK + '/download.json' fetch(download_url) .then(function (res) { return res.json() - }).then(function(res) { + }).then(function (res) { const range: string = that._getFilterRange(res) const search: string = that._getFilterSearch(searchParam, range) const query: string = API_LINK + that.props.meta.api_path + '.json?' + search + 'count=' + countParam @@ -170,8 +170,8 @@ class InfographicContainer extends React.Component { const urls = [ API_LINK + that.props.meta.api_path + '.json?' + search, query - ]; - Promise.all(urls.map($.getJSON)).then(function(results) { + ] + Promise.all(urls.map($.getJSON)).then(function (results) { const recordsTotal: number = results[0].meta.results.total // Get total number of records on the first call @@ -199,14 +199,13 @@ class InfographicContainer extends React.Component { // but we also fall back to the current explorer default type: that.props.fieldsFlattened[countParam] || that.state.current.type, }) - }).catch(function(res) { + }).catch(function (res) { that.setState({ data: res.responseJSON}) }) }) } - /** * @description [when typing in search field in info explorer * @param {string|Object} e [an event object, or the string value] @@ -338,21 +337,21 @@ class InfographicContainer extends React.Component { const infographicKeys: Array = Object.keys(this.state.infographics) return ( -
        - -
        +
        + +
        ) } } diff --git a/src/containers/QueryExplorerContainer.jsx b/src/containers/QueryExplorerContainer.tsx similarity index 100% rename from src/containers/QueryExplorerContainer.jsx rename to src/containers/QueryExplorerContainer.tsx diff --git a/src/containers/SideBarContainer.jsx b/src/containers/SideBarContainer.tsx similarity index 100% rename from src/containers/SideBarContainer.jsx rename to src/containers/SideBarContainer.tsx diff --git a/src/css/font-awesome/scss/_core.scss b/src/css/font-awesome/scss/_core.scss index 7425ef85f..04ab1e5e3 100644 --- a/src/css/font-awesome/scss/_core.scss +++ b/src/css/font-awesome/scss/_core.scss @@ -1,9 +1,11 @@ // Base Class Definition // ------------------------- +@use "sass:list"; + .#{$fa-css-prefix} { display: inline-block; - font: normal normal normal #{$fa-font-size-base}/#{$fa-line-height-base} FontAwesome; // shortening font declaration + font: normal normal normal list.slash($fa-font-size-base, $fa-line-height-base) FontAwesome; // shortening font declaration font-size: inherit; // can't have font-size inherit on line above, so need to override text-rendering: auto; // optimizelegibility throws things off #1094 -webkit-font-smoothing: antialiased; diff --git a/src/css/font-awesome/scss/_fixed-width.scss b/src/css/font-awesome/scss/_fixed-width.scss index b221c9813..5411f9538 100644 --- a/src/css/font-awesome/scss/_fixed-width.scss +++ b/src/css/font-awesome/scss/_fixed-width.scss @@ -1,6 +1,8 @@ // Fixed Width Icons // ------------------------- +@use "sass:math"; + .#{$fa-css-prefix}-fw { - width: (18em / 14); + width: math.div(18em, 14); text-align: center; } diff --git a/src/css/font-awesome/scss/_larger.scss b/src/css/font-awesome/scss/_larger.scss index 41e9a8184..a4065941f 100644 --- a/src/css/font-awesome/scss/_larger.scss +++ b/src/css/font-awesome/scss/_larger.scss @@ -2,9 +2,11 @@ // ------------------------- /* makes the font 33% larger relative to the icon container */ +@use "sass:math"; + .#{$fa-css-prefix}-lg { - font-size: (4em / 3); - line-height: (3em / 4); + font-size: math.div(4em, 3); + line-height: (3em * 0.25); vertical-align: -15%; } .#{$fa-css-prefix}-2x { font-size: 2em; } diff --git a/src/css/font-awesome/scss/_list.scss b/src/css/font-awesome/scss/_list.scss index 7d1e4d54d..988e6f87a 100644 --- a/src/css/font-awesome/scss/_list.scss +++ b/src/css/font-awesome/scss/_list.scss @@ -1,6 +1,8 @@ // List Icons // ------------------------- +@use "sass:math"; + .#{$fa-css-prefix}-ul { padding-left: 0; margin-left: $fa-li-width; @@ -11,9 +13,9 @@ position: absolute; left: -$fa-li-width; width: $fa-li-width; - top: (2em / 14); + top: math.div(2em, 14); text-align: center; &.#{$fa-css-prefix}-lg { - left: -$fa-li-width + (4em / 14); + left: -$fa-li-width + math.div(4em, 14); } } diff --git a/src/css/font-awesome/scss/_mixins.scss b/src/css/font-awesome/scss/_mixins.scss index c3bbd5745..86f51385b 100644 --- a/src/css/font-awesome/scss/_mixins.scss +++ b/src/css/font-awesome/scss/_mixins.scss @@ -1,9 +1,11 @@ // Mixins // -------------------------- +@use "sass:list"; + @mixin fa-icon() { display: inline-block; - font: normal normal normal #{$fa-font-size-base}/#{$fa-line-height-base} FontAwesome; // shortening font declaration + font: normal normal normal list.slash($fa-font-size-base, $fa-line-height-base) FontAwesome; // shortening font declaration font-size: inherit; // can't have font-size inherit on line above, so need to override text-rendering: auto; // optimizelegibility throws things off #1094 -webkit-font-smoothing: antialiased; diff --git a/src/css/font-awesome/scss/_variables.scss b/src/css/font-awesome/scss/_variables.scss index 498fc4a08..61062f971 100644 --- a/src/css/font-awesome/scss/_variables.scss +++ b/src/css/font-awesome/scss/_variables.scss @@ -1,6 +1,8 @@ // Variables // -------------------------- +@use "sass:math"; + $fa-font-path: "../fonts" !default; $fa-font-size-base: 14px !default; $fa-line-height-base: 1 !default; @@ -9,7 +11,7 @@ $fa-css-prefix: fa !default; $fa-version: "4.7.0" !default; $fa-border-color: #eee !default; $fa-inverse: #fff !default; -$fa-li-width: (30em / 14) !default; +$fa-li-width: math.div(30em, 14) !default; $fa-var-500px: "\f26e"; $fa-var-address-book: "\f2b9"; diff --git a/src/html.jsx b/src/html.jsx index 0b58e67c0..5117040ee 100644 --- a/src/html.jsx +++ b/src/html.jsx @@ -69,7 +69,7 @@ const HTML = ({ title = 'openFDA', favicon, body, postBodyComponents, headCompon { // We participate in the US government's analytics program. See the data at analytics.usa.gov.} } -