diff --git a/.env.development b/.env.development index acdc7287..356f1d47 100644 --- a/.env.development +++ b/.env.development @@ -15,6 +15,7 @@ REACT_APP_NEON_PATH_MANIFEST_API="/api/download/v0" REACT_APP_NEON_PATH_DOWNLOAD_API="/api/download/v0" REACT_APP_NEON_PATH_AOP_DOWNLOAD_API="/browse-data" REACT_APP_NEON_PATH_DATA_API="/data" +REACT_APP_NEON_PATH_PROTOTYPE_DATA_API="/prototype" REACT_APP_NEON_PATH_FILE_NAMING_CONVENTIONS="/file-naming-conventions" REACT_APP_NEON_PATH_PUBLIC_GRAPHQL="/graphql" diff --git a/.env.production b/.env.production index 12b669ad..31561396 100644 --- a/.env.production +++ b/.env.production @@ -14,6 +14,7 @@ REACT_APP_NEON_PATH_MANIFEST_API="/api/download/v0" REACT_APP_NEON_PATH_DOWNLOAD_API="/api/download/v0" REACT_APP_NEON_PATH_AOP_DOWNLOAD_API="/browse-data" REACT_APP_NEON_PATH_DATA_API="/data" +REACT_APP_NEON_PATH_PROTOTYPE_DATA_API="/prototype" REACT_APP_NEON_PATH_FILE_NAMING_CONVENTIONS="/file-naming-conventions" REACT_APP_NEON_PATH_PUBLIC_GRAPHQL="/graphql" diff --git a/lib/components/DataProductAvailability/AvailabilityUtils.js b/lib/components/DataProductAvailability/AvailabilityUtils.js index 4b6bcf37..32eddf52 100644 --- a/lib/components/DataProductAvailability/AvailabilityUtils.js +++ b/lib/components/DataProductAvailability/AvailabilityUtils.js @@ -163,7 +163,7 @@ var TIME = { }; exports.TIME = TIME; TIME.MIN_YEAR_MONTH = "".concat(TIME.START_YEAR, "-01"); -TIME.MAX_YEAR_MONTH = "".concat(TIME.endYear, "-12"); +TIME.MAX_YEAR_MONTH = "".concat(TIME.END_YEAR, "-12"); TIME.YEARS = Array(TIME.END_YEAR - TIME.START_YEAR + 1).fill(0).map(function (val, idx) { return TIME.START_YEAR + idx; }); diff --git a/lib/components/DataProductAvailability/BasicAvailabilityGrid.js b/lib/components/DataProductAvailability/BasicAvailabilityGrid.js index 2568b2fd..e0e2561f 100644 --- a/lib/components/DataProductAvailability/BasicAvailabilityGrid.js +++ b/lib/components/DataProductAvailability/BasicAvailabilityGrid.js @@ -185,7 +185,7 @@ function BasicAvailabilityGrid(config) { var getYearStartX = function getYearStartX(year) { var intYear = parseInt(year, 10); - return getLabelWidth() + _AvailabilityUtils.SVG.END_PADDING + _AvailabilityUtils.TIME.YEARS.indexOf(intYear) * _AvailabilityUtils.SVG.YEAR_WIDTH + _AvailabilityUtils.TIME.YEARS.indexOf(intYear) * _AvailabilityUtils.SVG.YEAR_PADDING; + return getLabelWidth() + _AvailabilityUtils.SVG.END_PADDING + _AvailabilityUtils.TIME.YEARS.indexOf(intYear) * (_AvailabilityUtils.SVG.YEAR_WIDTH + _AvailabilityUtils.SVG.YEAR_PADDING); }; var getYearCenterX = function getYearCenterX(year) { @@ -382,40 +382,74 @@ function BasicAvailabilityGrid(config) { } }; // Get the yearMonth string that's next to a given yearMonth on either side. // Stays within the selectable range unless selectable is false, in which case - // is stays within the chart's global min and max. - - - var getAdjacentYearMonth = function getAdjacentYearMonth(yearMonth) { - var side = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : 'left'; - var selectable = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : true; - var year = parseInt(yearMonth.substr(0, 4), 10); - var month = parseInt(yearMonth.substr(5, 2), 10); - var bounds = selectable ? dateRange.validValues : [_AvailabilityUtils.TIME.MIN_YEAR_MONTH, _AvailabilityUtils.TIME.MAX_YEAR_MONTH]; - var adjacent = yearMonth; + // it stays within the chart's global min and max. + /* + const getAdjacentYearMonth = (yearMonth, side = 'left', selectable = true) => { + const year = parseInt(yearMonth.substr(0, 4), 10); + const month = parseInt(yearMonth.substr(5, 2), 10); + const bounds = selectable ? dateRange.validValues : [TIME.MIN_YEAR_MONTH, TIME.MAX_YEAR_MONTH]; + let adjacent = yearMonth; switch (side) { case 'left': if (month === 1) { - adjacent = "".concat(year - 1, "-12"); + adjacent = `${year - 1}-12`; } else { - adjacent = "".concat(year, "-").concat((month - 1).toString().padStart(2, '0')); + adjacent = `${year}-${(month - 1).toString().padStart(2, '0')}`; } - return adjacent < bounds[0] ? bounds[0] : adjacent; - case 'right': if (month === 12) { - adjacent = "".concat(year + 1, "-01"); + adjacent = `${year + 1}-01`; } else { - adjacent = "".concat(year, "-").concat((month + 1).toString().padStart(2, '0')); + adjacent = `${year}-${(month + 1).toString().padStart(2, '0')}`; } - return adjacent > bounds[1] ? bounds[1] : adjacent; - default: return adjacent; } }; + */ + // Get the string for the yearmonth closest to a given pixel x-offset on either side. + // Stays within the selectable range unless selectable is false, in which case + // it stays within the chart's global min and max. + + + var getXOffsetYearMonth = function getXOffsetYearMonth(xOffset, dragOffset) { + var side = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : 'left'; + var selectable = arguments.length > 3 && arguments[3] !== undefined ? arguments[3] : true; + + /***/ + var basis = Math.min(Math.max(xOffset, getLabelWidth()), svgWidth) - dragOffset; + var yearIdx = Math.floor((basis - getLabelWidth()) / (_AvailabilityUtils.SVG.YEAR_WIDTH + _AvailabilityUtils.SVG.YEAR_PADDING)); + var boundedYearIdx = Math.max(Math.min(yearIdx, _AvailabilityUtils.TIME.YEARS.length - 1), 0); + var year = _AvailabilityUtils.TIME.YEARS[boundedYearIdx]; + var yearStartX = getYearStartX(year); + + if (basis < yearStartX && side === 'left' && boundedYearIdx !== 0) { + year = parseInt(year, 10) - 1; + yearStartX = getYearStartX(year); + } else if (basis > yearStartX + _AvailabilityUtils.SVG.YEAR_WIDTH && side === 'right' && boundedYearIdx !== _AvailabilityUtils.TIME.YEARS.length - 1) { + year = parseInt(year, 10) + 1; + yearStartX = getYearStartX(year); + } + + var roundFunc = side === 'left' ? 'floor' : 'ceil'; + var month = Math[roundFunc]((basis - yearStartX) / _AvailabilityUtils.SVG.YEAR_MONTH_WIDTH) + 1; + var boundedMonth = Math.max(Math.min(month, 12), 1); + var yearMonth = "".concat(year, "-").concat(boundedMonth.toString().padStart(2, '0')); + var bounds = selectable ? dateRange.validValues : [_AvailabilityUtils.TIME.MIN_YEAR_MONTH, _AvailabilityUtils.TIME.MAX_YEAR_MONTH]; + + if (yearMonth < bounds[0]) { + return bounds[0]; + } + + if (yearMonth > bounds[1]) { + return bounds[1]; + } + + return yearMonth; + }; /** SVG: Row Hover */ @@ -730,7 +764,8 @@ function BasicAvailabilityGrid(config) { return "dateRange".concat(d === 0 ? 'Start' : 'End', "MaskRect"); }).attr('x', function (d) { return getYearMonthGutterX(dateRange.value[d] || _AvailabilityUtils.TIME.MIN_YEAR_MONTH, d === 0 ? 'left' : 'right') - _AvailabilityUtils.SVG.DATE_RANGE_MASK_WIDTH / 2 + getTimeOffset(); - }).attr('y', 0).attr('width', _AvailabilityUtils.SVG.DATE_RANGE_MASK_WIDTH).attr('height', svgHeight).style('cursor', 'ew-resize').style('outline', 'none').attr('fill', 'red').style('opacity', 0).style('display', sites.value.length ? null : 'none'); + }).attr('y', 0).attr('width', _AvailabilityUtils.SVG.DATE_RANGE_MASK_WIDTH).attr('height', svgHeight).style('cursor', 'crosshair') // 'ew-resize' + .style('outline', 'none').attr('fill', 'red').style('opacity', 0).style('display', sites.value.length ? null : 'none'); }; /** SVG: Selections @@ -902,16 +937,17 @@ function BasicAvailabilityGrid(config) { draggingDateRange[0].centerDragX = parseFloat(dragDateRangeStartMask.attr('x'), 10) + _AvailabilityUtils.SVG.DATE_RANGE_MASK_WIDTH / 2; }).on('drag', function () { draggingDateRange[0].centerDragX += _d3Selection.event.dx; - dragDateRangeStartMask.attr('x', draggingDateRange[0].centerDragX - _AvailabilityUtils.SVG.DATE_RANGE_MASK_WIDTH / 2); - var adjacentYearMonth = getAdjacentYearMonth(dateRange.value[0], _d3Selection.event.dx > 0 ? 'right' : 'left'); - var adjacentYearMonthStartX = getYearMonthGutterX(adjacentYearMonth, 'left'); + var centerDragX = draggingDateRange[0].centerDragX; + dragDateRangeStartMask.attr('x', centerDragX - _AvailabilityUtils.SVG.DATE_RANGE_MASK_WIDTH / 2); + var nextYearMonth = getXOffsetYearMonth(centerDragX, getTimeOffset(), 'right'); + var nextYearMonthStartX = getYearMonthGutterX(nextYearMonth, 'left'); var currentYearMonthStartX = getYearMonthGutterX(dateRange.value[0], 'left'); var insideClipCenterDragX = draggingDateRange[0].centerDragX - getTimeOffset(); - var distanceToAdjacent = Math.abs(insideClipCenterDragX - adjacentYearMonthStartX); + var distanceToNext = Math.abs(insideClipCenterDragX - nextYearMonthStartX); var distanceToCurrent = Math.abs(insideClipCenterDragX - currentYearMonthStartX); - if (adjacentYearMonth !== dateRange.value[0] && distanceToAdjacent < distanceToCurrent) { - dateRange.value[0] = adjacentYearMonth; + if (nextYearMonth !== dateRange.value[0] && distanceToNext < distanceToCurrent) { + dateRange.value[0] = nextYearMonth; redrawSelections(); } }).on('end', function () { @@ -937,16 +973,17 @@ function BasicAvailabilityGrid(config) { draggingDateRange[1].centerDragX = parseFloat(dragDateRangeEndMask.attr('x'), 10) + _AvailabilityUtils.SVG.DATE_RANGE_MASK_WIDTH / 2; }).on('drag', function () { draggingDateRange[1].centerDragX += _d3Selection.event.dx; - dragDateRangeEndMask.attr('x', draggingDateRange[1].centerDragX - _AvailabilityUtils.SVG.DATE_RANGE_MASK_WIDTH / 2); - var adjacentYearMonth = getAdjacentYearMonth(dateRange.value[1], _d3Selection.event.dx > 0 ? 'right' : 'left'); - var adjacentYearMonthEndX = getYearMonthGutterX(adjacentYearMonth, 'right'); + var centerDragX = draggingDateRange[1].centerDragX; + dragDateRangeEndMask.attr('x', centerDragX - _AvailabilityUtils.SVG.DATE_RANGE_MASK_WIDTH / 2); + var nextYearMonth = getXOffsetYearMonth(centerDragX, getTimeOffset(), 'left'); + var nextYearMonthEndX = getYearMonthGutterX(nextYearMonth, 'right'); var currentYearMonthEndX = getYearMonthGutterX(dateRange.value[1], 'right'); var insideClipCenterDragX = draggingDateRange[1].centerDragX - getTimeOffset(); - var distanceToAdjacent = Math.abs(insideClipCenterDragX - adjacentYearMonthEndX); + var distanceToNext = Math.abs(insideClipCenterDragX - nextYearMonthEndX); var distanceToCurrent = Math.abs(insideClipCenterDragX - currentYearMonthEndX); - if (adjacentYearMonth !== dateRange.value[1] && distanceToAdjacent < distanceToCurrent) { - dateRange.value[1] = adjacentYearMonth; + if (nextYearMonth !== dateRange.value[1] && distanceToNext < distanceToCurrent) { + dateRange.value[1] = nextYearMonth; redrawSelections(); } }).on('end', function () { diff --git a/lib/components/DataThemeIcon/DataThemeIcon.js b/lib/components/DataThemeIcon/DataThemeIcon.js index 9434b788..2d519fee 100644 --- a/lib/components/DataThemeIcon/DataThemeIcon.js +++ b/lib/components/DataThemeIcon/DataThemeIcon.js @@ -61,7 +61,7 @@ var dataThemes = { }, landcover: { title: 'Land Cover & Processes', - aliases: ['landuse', 'Land Use, Land Cover, and Land Processes'], + aliases: ['landuse', 'Land Cover and Processes', 'Land Use, Land Cover, and Land Processes'], src: _landcover.default }, organisms: { @@ -81,6 +81,11 @@ var DataThemeIcon = function DataThemeIcon(props) { var dataTheme = dataThemes[theme] || Object.values(dataThemes).find(function (entry) { return theme === entry.title || entry.aliases.includes(theme); }); + + if (!dataTheme) { + return null; + } + var elementProps = { src: dataTheme.src, alt: dataTheme.title, diff --git a/lib/components/DownloadDataContext/DownloadDataContext.d.ts b/lib/components/DownloadDataContext/DownloadDataContext.d.ts index 136d2d99..67c92088 100644 --- a/lib/components/DownloadDataContext/DownloadDataContext.d.ts +++ b/lib/components/DownloadDataContext/DownloadDataContext.d.ts @@ -13,6 +13,8 @@ export function getTestableItems(): { regenerateS3FilesFiltersAndValidValues?: undefined; getAndValidateNewState?: undefined; ALL_POSSIBLE_VALID_DATE_RANGE?: undefined; + ALL_POSSIBLE_VALID_DOCUMENTATION?: undefined; + ALL_POSSIBLE_VALID_PACKAGE_TYPE?: undefined; } | { productDataIsValid: (productData: any) => boolean; yearMonthIsValid: (yearMonth?: string) => boolean; @@ -102,6 +104,8 @@ export function getTestableItems(): { regenerateS3FilesFiltersAndValidValues: (state: any) => any; getAndValidateNewState: (previousState: any, action: any, broadcast?: boolean) => any; ALL_POSSIBLE_VALID_DATE_RANGE: string[]; + ALL_POSSIBLE_VALID_DOCUMENTATION: string[]; + ALL_POSSIBLE_VALID_PACKAGE_TYPE: string[]; }; declare namespace DownloadDataContext { export { Provider }; diff --git a/lib/components/DownloadDataContext/DownloadDataContext.js b/lib/components/DownloadDataContext/DownloadDataContext.js index bec318ae..6b2c59fe 100644 --- a/lib/components/DownloadDataContext/DownloadDataContext.js +++ b/lib/components/DownloadDataContext/DownloadDataContext.js @@ -1307,7 +1307,9 @@ var getTestableItems = function getTestableItems() { getAndValidateNewS3FilesState: getAndValidateNewS3FilesState, regenerateS3FilesFiltersAndValidValues: regenerateS3FilesFiltersAndValidValues, getAndValidateNewState: getAndValidateNewState, - ALL_POSSIBLE_VALID_DATE_RANGE: ALL_POSSIBLE_VALID_DATE_RANGE + ALL_POSSIBLE_VALID_DATE_RANGE: ALL_POSSIBLE_VALID_DATE_RANGE, + ALL_POSSIBLE_VALID_DOCUMENTATION: ALL_POSSIBLE_VALID_DOCUMENTATION, + ALL_POSSIBLE_VALID_PACKAGE_TYPE: ALL_POSSIBLE_VALID_PACKAGE_TYPE }; }; diff --git a/lib/components/NeonApi/NeonApi.d.ts b/lib/components/NeonApi/NeonApi.d.ts index 13d0cab7..3513f87a 100644 --- a/lib/components/NeonApi/NeonApi.d.ts +++ b/lib/components/NeonApi/NeonApi.d.ts @@ -15,6 +15,10 @@ declare namespace NeonApi { export function getJson(url: string, callback: any, errorCallback: any, cancellationSubject$: any, headers?: Object | undefined): import("rxjs").Subscription; export function getProductsObservable(): import("rxjs").Observable; export function getProductObservable(productCode: string, release?: string): import("rxjs").Observable; + export function getPrototypeDatasetsObservable(): import("rxjs").Observable; + export function getPrototypeDatasetObservable(uuid: any): import("rxjs").Observable; + export function getPrototypeManifestRollupObservable(uuid: any): import("rxjs").Observable; + export function getPrototypeDataFileObservable(uuid: any, fileName: any): import("rxjs").Observable; export function getReleasesObservable(): import("rxjs").Observable; export function getReleaseObservable(release: string): import("rxjs").Observable; export function getSitesJsonObservable(): import("rxjs").Observable; diff --git a/lib/components/NeonApi/NeonApi.js b/lib/components/NeonApi/NeonApi.js index 2a21720e..188ec43b 100644 --- a/lib/components/NeonApi/NeonApi.js +++ b/lib/components/NeonApi/NeonApi.js @@ -186,6 +186,25 @@ var NeonApi = { return _getJsonObservable(path); }, + /** + * Gets the prototype data endpoint RxJS Observable. + * @return The RxJS Ajax Observable + */ + getPrototypeDatasetsObservable: function getPrototypeDatasetsObservable() { + return _getJsonObservable("".concat(_NeonEnvironment.default.getFullApiPath('prototype'), "/datasets")); + }, + getPrototypeDatasetObservable: function getPrototypeDatasetObservable(uuid) { + return _getJsonObservable("".concat(_NeonEnvironment.default.getFullApiPath('prototype'), "/datasets/").concat(uuid)); + }, + getPrototypeManifestRollupObservable: function getPrototypeManifestRollupObservable(uuid) { + return (// eslint-disable-next-line max-len + _getJsonObservable("".concat(_NeonEnvironment.default.getFullApiPath('manifest'), "/prototype/manifest/rollup?uuid=").concat(uuid)) + ); + }, + getPrototypeDataFileObservable: function getPrototypeDataFileObservable(uuid, fileName) { + return _getJsonObservable("".concat(_NeonEnvironment.default.getFullApiPath('prototype'), "/data/").concat(uuid, "/").concat(fileName)); + }, + /** * Gets the release endpoint RxJS Observable. * @return The RxJS Ajax Observable diff --git a/lib/components/NeonContext/NeonContext.d.ts b/lib/components/NeonContext/NeonContext.d.ts index ef6ab5f5..9152d8d0 100644 --- a/lib/components/NeonContext/NeonContext.d.ts +++ b/lib/components/NeonContext/NeonContext.d.ts @@ -33,8 +33,8 @@ declare namespace Provider { export { ProviderPropTypes as propTypes }; export namespace defaultProps { export const children: null; + export const fetchPartials: boolean; export const useCoreAuth: boolean; - export const useCoreHeader: boolean; export function whenFinal(): void; } } @@ -1048,10 +1048,10 @@ declare function getWrappedComponent(Component: any): (props: any) => JSX.Elemen declare namespace ProviderPropTypes { const children_1: PropTypes.Requireable; export { children_1 as children }; + const fetchPartials_1: PropTypes.Requireable; + export { fetchPartials_1 as fetchPartials }; const useCoreAuth_1: PropTypes.Requireable; export { useCoreAuth_1 as useCoreAuth }; - const useCoreHeader_1: PropTypes.Requireable; - export { useCoreHeader_1 as useCoreHeader }; const whenFinal_1: PropTypes.Requireable<(...args: any[]) => any>; export { whenFinal_1 as whenFinal }; } diff --git a/lib/components/NeonContext/NeonContext.js b/lib/components/NeonContext/NeonContext.js index d9f455bb..cbbad13a 100644 --- a/lib/components/NeonContext/NeonContext.js +++ b/lib/components/NeonContext/NeonContext.js @@ -300,8 +300,8 @@ var Provider = function Provider(props) { var _fetchMethods; var children = props.children, + fetchPartials = props.fetchPartials, useCoreAuth = props.useCoreAuth, - useCoreHeader = props.useCoreHeader, whenFinal = props.whenFinal; var initialState = (0, _cloneDeep.default)(DEFAULT_STATE); initialState.isActive = true; @@ -311,7 +311,7 @@ var Provider = function Provider(props) { initialState.fetches.auth.status = FETCH_STATUS.AWAITING_CALL; } - if (!useCoreHeader) { + if (fetchPartials) { initialState.fetches[DRUPAL_HEADER_HTML].status = FETCH_STATUS.AWAITING_CALL; initialState.fetches[DRUPAL_FOOTER_HTML].status = FETCH_STATUS.AWAITING_CALL; } @@ -467,15 +467,15 @@ var Provider = function Provider(props) { var ProviderPropTypes = { children: _propTypes.default.oneOfType([_propTypes.default.arrayOf(_propTypes.default.oneOfType([_propTypes.default.node, _propTypes.default.string])), _propTypes.default.node, _propTypes.default.string]), + fetchPartials: _propTypes.default.bool, useCoreAuth: _propTypes.default.bool, - useCoreHeader: _propTypes.default.bool, whenFinal: _propTypes.default.func }; Provider.propTypes = ProviderPropTypes; Provider.defaultProps = { children: null, + fetchPartials: false, useCoreAuth: false, - useCoreHeader: false, whenFinal: function whenFinal() {} }; /** diff --git a/lib/components/NeonEnvironment/NeonEnvironment.d.ts b/lib/components/NeonEnvironment/NeonEnvironment.d.ts index 364fbd00..3ca244f3 100644 --- a/lib/components/NeonEnvironment/NeonEnvironment.d.ts +++ b/lib/components/NeonEnvironment/NeonEnvironment.d.ts @@ -57,8 +57,8 @@ declare namespace NeonEnvironment { export namespace route { export function home(): string; export function account(): string; - export function getFullRoute(route: any): string; - export function buildRouteFromHost(route: any): string; + export function getFullRoute(route?: string): string; + export function buildRouteFromHost(route?: string): string; export function buildHomeRoute(): string; export function buildAccountRoute(): string; } diff --git a/lib/components/NeonEnvironment/NeonEnvironment.js b/lib/components/NeonEnvironment/NeonEnvironment.js index 705b3c32..42b720a4 100644 --- a/lib/components/NeonEnvironment/NeonEnvironment.js +++ b/lib/components/NeonEnvironment/NeonEnvironment.js @@ -19,7 +19,7 @@ var requiredEnvironmentVars = ['REACT_APP_NEON_API_NAME', 'REACT_APP_NEON_API_VE // this module will ever reference. exports.requiredEnvironmentVars = requiredEnvironmentVars; -var optionalEnvironmentVars = ['REACT_APP_NEON_PATH_LD_API', 'REACT_APP_NEON_PATH_LD_REPO_API', 'REACT_APP_NEON_PATH_AOP_DOWNLOAD_API', 'REACT_APP_NEON_PATH_DATA_API', 'REACT_APP_NEON_PATH_DOCUMENTS_API', 'REACT_APP_NEON_PATH_DOWNLOAD_API', 'REACT_APP_NEON_PATH_MANIFEST_API', 'REACT_APP_NEON_PATH_PRODUCTS_API', 'REACT_APP_NEON_PATH_RELEASES_API', 'REACT_APP_NEON_PATH_SITES_API', 'REACT_APP_NEON_PATH_LOCATIONS_API', 'REACT_APP_NEON_PATH_FILE_NAMING_CONVENTIONS', 'REACT_APP_NEON_SHOW_AOP_VIEWER', 'REACT_APP_NEON_VISUS_PRODUCTS_BASE_URL', 'REACT_APP_NEON_VISUS_IFRAME_BASE_URL', 'REACT_APP_NEON_HOST_OVERRIDE', 'REACT_APP_NEON_WS_HOST_OVERRIDE', 'REACT_APP_FOREIGN_LOCATION', 'REACT_APP_NEON_AUTH_DISABLE_WS', 'REACT_APP_NEON_ROUTER_NEON_HOME', 'REACT_APP_NEON_ROUTER_NEON_MYACCOUNT']; // Temporary paths that shouldn't need to propogate to environment files until made more permanent +var optionalEnvironmentVars = ['REACT_APP_NEON_PATH_LD_API', 'REACT_APP_NEON_PATH_LD_REPO_API', 'REACT_APP_NEON_PATH_AOP_DOWNLOAD_API', 'REACT_APP_NEON_PATH_DATA_API', 'REACT_APP_NEON_PATH_DOCUMENTS_API', 'REACT_APP_NEON_PATH_DOWNLOAD_API', 'REACT_APP_NEON_PATH_MANIFEST_API', 'REACT_APP_NEON_PATH_PRODUCTS_API', 'REACT_APP_NEON_PATH_PROTOTYPE_DATA_API', 'REACT_APP_NEON_PATH_RELEASES_API', 'REACT_APP_NEON_PATH_SITES_API', 'REACT_APP_NEON_PATH_LOCATIONS_API', 'REACT_APP_NEON_PATH_FILE_NAMING_CONVENTIONS', 'REACT_APP_NEON_SHOW_AOP_VIEWER', 'REACT_APP_NEON_VISUS_PRODUCTS_BASE_URL', 'REACT_APP_NEON_VISUS_IFRAME_BASE_URL', 'REACT_APP_NEON_HOST_OVERRIDE', 'REACT_APP_NEON_WS_HOST_OVERRIDE', 'REACT_APP_FOREIGN_LOCATION', 'REACT_APP_NEON_AUTH_DISABLE_WS', 'REACT_APP_NEON_ROUTER_NEON_HOME', 'REACT_APP_NEON_ROUTER_NEON_MYACCOUNT']; // Temporary paths that shouldn't need to propogate to environment files until made more permanent exports.optionalEnvironmentVars = optionalEnvironmentVars; var REACT_APP_NEON_PATH_ARCGIS_ASSETS_API = '/arcgis-assets'; @@ -62,6 +62,9 @@ var NeonEnvironment = { data: function data() { return process.env.REACT_APP_NEON_PATH_DATA_API; }, + prototype: function prototype() { + return process.env.REACT_APP_NEON_PATH_PROTOTYPE_DATA_API; + }, documents: function documents() { return process.env.REACT_APP_NEON_PATH_DOCUMENTS_API; }, @@ -155,11 +158,13 @@ var NeonEnvironment = { account: function account() { return process.env.REACT_APP_NEON_ROUTER_NEON_MYACCOUNT || '/myaccount'; }, - getFullRoute: function getFullRoute(route) { + getFullRoute: function getFullRoute() { + var route = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : ''; return "".concat(NeonEnvironment.getRouterBasePath()).concat(route); }, - buildRouteFromHost: function buildRouteFromHost(route) { - return "".concat(NeonEnvironment.getHost()).concat(NeonEnvironment.getFullRoute(route)); + buildRouteFromHost: function buildRouteFromHost() { + var route = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : ''; + return "".concat(NeonEnvironment.getHost()).concat(NeonEnvironment.route.getFullRoute(route)); }, buildHomeRoute: function buildHomeRoute() { return "".concat(NeonEnvironment.getHost()).concat(NeonEnvironment.route.home()); @@ -263,7 +268,14 @@ var NeonEnvironment = { var path = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : ''; var host = NeonEnvironment.getHost(); var root = NeonEnvironment.getRootJsonLdPath(); - var appliedPath = ['products'].includes(path) ? NeonEnvironment.getApiPath[path]() : NeonEnvironment.getApiLdPath[path](); + var appliedPath = ''; + + if (['products'].includes(path)) { + appliedPath = NeonEnvironment.getApiPath[path](); + } else if (typeof NeonEnvironment.getApiLdPath[path] === 'function') { + appliedPath = NeonEnvironment.getApiLdPath[path](); + } + return appliedPath ? "".concat(host).concat(root).concat(appliedPath) : "".concat(host).concat(root); }, getFullPagePath: function getFullPagePath() { diff --git a/lib/components/NeonHeader/NeonHeader.js b/lib/components/NeonHeader/NeonHeader.js index 224b5acd..bdbbd884 100644 --- a/lib/components/NeonHeader/NeonHeader.js +++ b/lib/components/NeonHeader/NeonHeader.js @@ -68,7 +68,7 @@ var useStyles = (0, _styles.makeStyles)(function (theme) { // common styles textAlign: 'right', position: 'absolute', - zIndex: 10 + zIndex: 15 }, _defineProperty(_coreAuthContainer, theme.breakpoints.up('lg'), { padding: '0px', top: '-1px', @@ -94,10 +94,10 @@ var useStyles = (0, _styles.makeStyles)(function (theme) { '& > header': _defineProperty({ position: 'unset !important', '& label[for="nav-trigger"]': { - zIndex: '3 !important' + zIndex: '9 !important' }, '& div.header__site-navigation': { - zIndex: '2 !important' + zIndex: '8 !important' } }, theme.breakpoints.down('sm'), { '& .header__site-navigation': { diff --git a/lib/components/NeonJsonLd/NeonJsonLd.d.ts b/lib/components/NeonJsonLd/NeonJsonLd.d.ts index f4ed43bb..f8307813 100644 --- a/lib/components/NeonJsonLd/NeonJsonLd.d.ts +++ b/lib/components/NeonJsonLd/NeonJsonLd.d.ts @@ -1,10 +1,13 @@ export default NeonJsonLd; declare namespace NeonJsonLd { + export const CITATION_AUTHOR: string; export function getJsonLdObservable(url: string): import("rxjs").Observable; export function getRepoJsonLdObservable(): import("rxjs").Observable; export function getProductJsonLdObservable(productCode: string, release: string): import("rxjs").Observable; - export function inject(data: Object | null | undefined): void; - export function getJsonLdWithInjection(url: string): void; + export function inject(data: Object | null | undefined, injectReleaseMeta?: boolean): void; + export function injectReleaseMeta(data: Object | null | undefined): void; + export function removeReleaseMeta(): void; + export function getJsonLdWithInjection(url: string, injectReleaseMeta?: boolean): void; export function injectRepo(): void; - export function injectProduct(productCode: string, release: string): void; + export function injectProduct(productCode: string, release: string, injectReleaseMeta?: boolean, onNotExistsOnly?: boolean): void | null; } diff --git a/lib/components/NeonJsonLd/NeonJsonLd.js b/lib/components/NeonJsonLd/NeonJsonLd.js index b2485749..ec159273 100644 --- a/lib/components/NeonJsonLd/NeonJsonLd.js +++ b/lib/components/NeonJsonLd/NeonJsonLd.js @@ -19,6 +19,8 @@ function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { de * Container for supplying common NEON API JSON-LD request handlers. */ var NeonJsonLd = { + CITATION_AUTHOR: 'NEON (National Ecological Observatory Network)', + /** * Convenience wrapper for obtaining an AJAX Observable from NeonApi. * @param {string} url The URL to build from. @@ -57,8 +59,10 @@ var NeonJsonLd = { * into the DOM head. * Assumes the object is a valid JSON object. * @param {Object|null|undefined} data The JSON-LD object to inject. + * @param {boolean} injectReleaseMeta Optionally inject release meta tags */ inject: function inject(data) { + var injectReleaseMeta = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : false; if (!data) return; try { @@ -71,19 +75,116 @@ var NeonJsonLd = { console.error(e); // eslint-disable-line no-console } + if (injectReleaseMeta) { + NeonJsonLd.injectReleaseMeta(data); + } + var block = document.createElement('script'); block.setAttribute('type', 'application/ld+json'); block.innerText = JSON.stringify(data); document.head.appendChild(block); }, + /** + * Injects the release meta tags into the DOM head from the supplied + * JSON-LD object. + * Assumes the object is a valid JSON object. + * @param {Object|null|undefined} data The JSON-LD object to inject. + */ + injectReleaseMeta: function injectReleaseMeta(data) { + if (!data) return; + + try { + var currentDoiBlock = document.head.querySelector('meta[name="citation_doi"]'); + var doiUrl = new URL(data.identifier); + var doiBlock = document.createElement('meta'); + doiBlock.setAttribute('name', 'citation_doi'); + doiBlock.setAttribute('content', doiUrl.pathname.slice(1, doiUrl.pathname.length)); + + if (typeof currentDoiBlock !== 'undefined' && currentDoiBlock !== null) { + document.head.replaceChild(doiBlock, currentDoiBlock); + } else { + document.head.appendChild(doiBlock); + } + + var currentTitleBlock = document.head.querySelector('meta[name="citation_title"]'); + var titleBlock = document.createElement('meta'); + titleBlock.setAttribute('name', 'citation_title'); + var addTitle = false; + + if (typeof data.name === 'string' && data.name.length > 0) { + if (data.name.indexOf(NeonJsonLd.CITATION_AUTHOR) >= 0) { + var titleContent = data.name.replace(NeonJsonLd.CITATION_AUTHOR, '').trim(); + titleBlock.setAttribute('content', titleContent); + } else { + titleBlock.setAttribute('content', data.name); + } + + addTitle = true; + } + + if (!addTitle) { + if (typeof currentTitleBlock !== 'undefined' && currentTitleBlock !== null) { + currentTitleBlock.remove(); + } + } else if (typeof currentTitleBlock !== 'undefined' && currentTitleBlock !== null) { + document.head.replaceChild(titleBlock, currentTitleBlock); + } else { + document.head.appendChild(titleBlock); + } + + var currentAuthorBlock = document.head.querySelector('meta[name="citation_author"]'); + var authorBlock = document.createElement('meta'); + authorBlock.setAttribute('name', 'citation_author'); + authorBlock.setAttribute('content', NeonJsonLd.CITATION_AUTHOR); + + if (typeof currentAuthorBlock !== 'undefined' && currentAuthorBlock !== null) { + document.head.replaceChild(authorBlock, currentAuthorBlock); + } else { + document.head.appendChild(authorBlock); + } + } catch (e) { + console.error(e); // eslint-disable-line no-console + } + }, + + /** + * Removes the release meta tags from the DOM head element when exists. + */ + removeReleaseMeta: function removeReleaseMeta() { + try { + var currentDoiBlock = document.head.querySelector('meta[name="citation_doi"]'); + + if (typeof currentDoiBlock !== 'undefined' && currentDoiBlock !== null) { + currentDoiBlock.remove(); + } + + var currentTitleBlock = document.head.querySelector('meta[name="citation_title"]'); + + if (typeof currentTitleBlock !== 'undefined' && currentTitleBlock !== null) { + currentTitleBlock.remove(); + } + + var currentAuthorBlock = document.head.querySelector('meta[name="citation_author"]'); + + if (typeof currentAuthorBlock !== 'undefined' && currentAuthorBlock !== null) { + currentAuthorBlock.remove(); + } + } catch (e) { + console.error(e); // eslint-disable-line no-console + } + }, + /** * Retrieves and injects the JDON-LD data from the specified URL. * @param {string} url The URL to query. + * @param {boolean} injectReleaseMeta Optionally inject release meta tags */ getJsonLdWithInjection: function getJsonLdWithInjection(url) { + var injectReleaseMeta = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : false; + var observable = _NeonApi.default.getJsonObservable(url).pipe((0, _operators.map)(function (response) { - return NeonJsonLd.inject(response); + return NeonJsonLd.inject(response, injectReleaseMeta); }), (0, _operators.catchError)(function (err) { console.error(err); // eslint-disable-line no-console @@ -104,14 +205,37 @@ var NeonJsonLd = { * Fetches and injects the product JSON-LD based on the specified product code. * @param {string} productCode The product code to query with. * @param {string} release The release to build the URL from. + * @param {boolean} injectReleaseMeta Optionally inject release meta tags + * @param {boolean} onNotExistsOnly Inject only if JSON-LD is not already injected */ injectProduct: function injectProduct(productCode, release) { + var injectReleaseMeta = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : false; + var onNotExistsOnly = arguments.length > 3 && arguments[3] !== undefined ? arguments[3] : false; + var shouldFetch = true; + + if (onNotExistsOnly) { + try { + var existingBlock = document.head.querySelector('script[type="application/ld+json"]'); + + if (typeof existingBlock !== 'undefined' && existingBlock !== null) { + shouldFetch = false; + } + } catch (e) { + console.error(e); // eslint-disable-line no-console + } + } + + if (!shouldFetch) { + return null; + } + var hasRelease = typeof release !== 'undefined' && release !== null && typeof release === 'string' && release.length > 0; if (hasRelease) { - return NeonJsonLd.getJsonLdWithInjection("".concat(_NeonEnvironment.default.getFullJsonLdApiPath('products'), "/").concat(productCode, "?release=").concat(release)); + return NeonJsonLd.getJsonLdWithInjection("".concat(_NeonEnvironment.default.getFullJsonLdApiPath('products'), "/").concat(productCode, "?release=").concat(release), injectReleaseMeta); } + NeonJsonLd.removeReleaseMeta(); return NeonJsonLd.getJsonLdWithInjection("".concat(_NeonEnvironment.default.getFullJsonLdApiPath('products'), "/").concat(productCode)); } }; diff --git a/lib/components/NeonPage/NeonPage.d.ts b/lib/components/NeonPage/NeonPage.d.ts index c1a97ac7..fd2d649a 100644 --- a/lib/components/NeonPage/NeonPage.d.ts +++ b/lib/components/NeonPage/NeonPage.d.ts @@ -36,8 +36,8 @@ declare namespace NeonPage { export const unstickyDrupalHeader: PropTypes.Requireable; export const NeonContextProviderProps: PropTypes.Requireable; + fetchPartials: PropTypes.Requireable; useCoreAuth: PropTypes.Requireable; - useCoreHeader: PropTypes.Requireable; whenFinal: PropTypes.Requireable<(...args: any[]) => any>; }>>; const children_1: PropTypes.Validator; diff --git a/lib/components/NeonPage/NeonPage.js b/lib/components/NeonPage/NeonPage.js index 99c8273a..d4d040d2 100644 --- a/lib/components/NeonPage/NeonPage.js +++ b/lib/components/NeonPage/NeonPage.js @@ -47,6 +47,8 @@ var _Paper = _interopRequireDefault(require("@material-ui/core/Paper")); var _Typography = _interopRequireDefault(require("@material-ui/core/Typography")); +var _Clear = _interopRequireDefault(require("@material-ui/icons/Clear")); + var _ExpandLess = _interopRequireDefault(require("@material-ui/icons/ExpandLess")); var _Warning = _interopRequireDefault(require("@material-ui/icons/Warning")); @@ -185,7 +187,7 @@ var useStyles = (0, _styles.makeStyles)(function () { position: 'sticky', top: '-2px', boxShadow: '0px 1px 3px rgba(0, 0, 0, 0.25), 0px 1px 1px rgba(0, 0, 0, 0.25)', - zIndex: 1 + zIndex: 2 }), _defineProperty(_sidebarContainer, _Theme.default.breakpoints.down('xs'), { padding: _Theme.default.spacing(1.5) }), _sidebarContainer), @@ -264,7 +266,7 @@ var useStyles = (0, _styles.makeStyles)(function () { justifyContent: 'center', textAlign: 'center', borderRadius: '4px', - padding: _Theme.default.spacing(3, 3, 4, 3), + padding: _Theme.default.spacing(3), position: 'sticky', top: _Theme.default.spacing(12), left: 0, @@ -304,6 +306,11 @@ var useStyles = (0, _styles.makeStyles)(function () { height: '6em', marginTop: _Theme.default.spacing(3), marginBottom: _Theme.default.spacing(4) + }, + dismissOverlay: { + width: '100%', + textAlign: 'right', + marginTop: _Theme.default.spacing(2) } }; }); @@ -407,11 +414,17 @@ var NeonPage = function NeonPage(props) { var sidebarRef = (0, _react.useRef)(null); var sidebarLinksContainerRef = (0, _react.useRef)(null); var belowMd = (0, _useMediaQuery.default)(_Theme.default.breakpoints.down('sm')); + + var _useState = (0, _react.useState)(false), + _useState2 = _slicedToArray(_useState, 2), + overlayDismissed = _useState2[0], + setOverlayDismissed = _useState2[1]; /** Sidebar Setup */ // Sidebar can have content OR links, not both. If both are set then content wins. + var hasSidebarContent = sidebarContent !== null; var hasSidebarLinks = !sidebarContent && Array.isArray(sidebarLinks) && sidebarLinks.length > 0; var hasSidebar = hasSidebarContent || hasSidebarLinks; // sidebarLinksAsStandaloneChildren can only be true if all sidebar links have a defined component @@ -424,20 +437,20 @@ var NeonPage = function NeonPage(props) { })); var initialCurrentSidebarHash = hasSidebarLinks ? sidebarLinks[0].hash || '#' : '#'; - var _useState = (0, _react.useState)(initialCurrentSidebarHash), - _useState2 = _slicedToArray(_useState, 2), - currentSidebarHash = _useState2[0], - setCurrentSidebarHash = _useState2[1]; - - var _useState3 = (0, _react.useState)(false), + var _useState3 = (0, _react.useState)(initialCurrentSidebarHash), _useState4 = _slicedToArray(_useState3, 2), - hashInitialized = _useState4[0], - setHashInitialized = _useState4[1]; + currentSidebarHash = _useState4[0], + setCurrentSidebarHash = _useState4[1]; var _useState5 = (0, _react.useState)(false), _useState6 = _slicedToArray(_useState5, 2), - sidebarExpanded = _useState6[0], - setSidebarExpanded = _useState6[1]; // for small viewports only + hashInitialized = _useState6[0], + setHashInitialized = _useState6[1]; + + var _useState7 = (0, _react.useState)(false), + _useState8 = _slicedToArray(_useState7, 2), + sidebarExpanded = _useState8[0], + setSidebarExpanded = _useState8[1]; // for small viewports only // Get the vertical pixel offset for the content associated to any sidebar link by hash @@ -537,10 +550,10 @@ var NeonPage = function NeonPage(props) { Effect - Load Drupal CSS */ - var _useState7 = (0, _react.useState)(_NeonContext.FETCH_STATUS.AWAITING_CALL), - _useState8 = _slicedToArray(_useState7, 2), - drupalCssStatus = _useState8[0], - setDrupalCssStatus = _useState8[1]; + var _useState9 = (0, _react.useState)(_NeonContext.FETCH_STATUS.AWAITING_CALL), + _useState10 = _slicedToArray(_useState9, 2), + drupalCssStatus = _useState10[0], + setDrupalCssStatus = _useState10[1]; (0, _react.useEffect)(function () { if (useCoreHeader || drupalCssStatus !== _NeonContext.FETCH_STATUS.AWAITING_CALL) { @@ -590,15 +603,15 @@ var NeonPage = function NeonPage(props) { }]; } - var _useState9 = (0, _react.useState)(initialFetchStatus), - _useState10 = _slicedToArray(_useState9, 2), - fetchNotificationsStatus = _useState10[0], - setFetchNotificationsStatus = _useState10[1]; - - var _useState11 = (0, _react.useState)(initialNotifications), + var _useState11 = (0, _react.useState)(initialFetchStatus), _useState12 = _slicedToArray(_useState11, 2), - notifications = _useState12[0], - setNotifications = _useState12[1]; // Handle a successful response from the notifications endpoint + fetchNotificationsStatus = _useState12[0], + setFetchNotificationsStatus = _useState12[1]; + + var _useState13 = (0, _react.useState)(initialNotifications), + _useState14 = _slicedToArray(_useState13, 2), + notifications = _useState14[0], + setNotifications = _useState14[1]; // Handle a successful response from the notifications endpoint var handleFetchNotificationsSuccess = function handleFetchNotificationsSuccess(response) { @@ -728,10 +741,19 @@ var NeonPage = function NeonPage(props) { var renderOverlay = function renderOverlay(overlayChildren) { return /*#__PURE__*/_react.default.createElement(_Backdrop.default, { - open: true + open: !overlayDismissed }, /*#__PURE__*/_react.default.createElement(_Paper.default, { className: classes.backdropPaper - }, overlayChildren)); + }, overlayChildren, /*#__PURE__*/_react.default.createElement("div", { + className: classes.dismissOverlay + }, /*#__PURE__*/_react.default.createElement(_Button.default, { + size: "small", + startIcon: /*#__PURE__*/_react.default.createElement(_Clear.default, null), + variant: "outlined", + onClick: function onClick() { + setOverlayDismissed(true); + } + }, "Dismiss")))); }; var renderLoading = function renderLoading() { @@ -942,7 +964,7 @@ var NeonPage = function NeonPage(props) { var renderedPage = neonContextIsActive ? renderNeonPage() : /*#__PURE__*/_react.default.createElement(_NeonContext.default.Provider, _extends({ useCoreAuth: true, - useCoreHeader: useCoreHeader + fetchPartials: !useCoreHeader }, NeonContextProviderProps), renderNeonPage()); return /*#__PURE__*/_react.default.createElement(_reactErrorBoundary.ErrorBoundary, { FallbackComponent: NeonErrorPage, diff --git a/lib/components/PopupBase/PopupBase.js b/lib/components/PopupBase/PopupBase.js index 5d6011da..7188f89d 100644 --- a/lib/components/PopupBase/PopupBase.js +++ b/lib/components/PopupBase/PopupBase.js @@ -31,7 +31,7 @@ function _possibleConstructorReturn(self, call) { if (call && (_typeof(call) === function _assertThisInitialized(self) { if (self === void 0) { throw new ReferenceError("this hasn't been initialised - super() hasn't been called"); } return self; } -function _isNativeReflectConstruct() { if (typeof Reflect === "undefined" || !Reflect.construct) return false; if (Reflect.construct.sham) return false; if (typeof Proxy === "function") return true; try { Date.prototype.toString.call(Reflect.construct(Date, [], function () {})); return true; } catch (e) { return false; } } +function _isNativeReflectConstruct() { if (typeof Reflect === "undefined" || !Reflect.construct) return false; if (Reflect.construct.sham) return false; if (typeof Proxy === "function") return true; try { Boolean.prototype.valueOf.call(Reflect.construct(Boolean, [], function () {})); return true; } catch (e) { return false; } } function _getPrototypeOf(o) { _getPrototypeOf = Object.setPrototypeOf ? Object.getPrototypeOf : function _getPrototypeOf(o) { return o.__proto__ || Object.getPrototypeOf(o); }; return _getPrototypeOf(o); } diff --git a/lib/components/PopupLoading/PopupLoading.js b/lib/components/PopupLoading/PopupLoading.js index 6551cde0..83993744 100644 --- a/lib/components/PopupLoading/PopupLoading.js +++ b/lib/components/PopupLoading/PopupLoading.js @@ -35,7 +35,7 @@ function _possibleConstructorReturn(self, call) { if (call && (_typeof(call) === function _assertThisInitialized(self) { if (self === void 0) { throw new ReferenceError("this hasn't been initialised - super() hasn't been called"); } return self; } -function _isNativeReflectConstruct() { if (typeof Reflect === "undefined" || !Reflect.construct) return false; if (Reflect.construct.sham) return false; if (typeof Proxy === "function") return true; try { Date.prototype.toString.call(Reflect.construct(Date, [], function () {})); return true; } catch (e) { return false; } } +function _isNativeReflectConstruct() { if (typeof Reflect === "undefined" || !Reflect.construct) return false; if (Reflect.construct.sham) return false; if (typeof Proxy === "function") return true; try { Boolean.prototype.valueOf.call(Reflect.construct(Boolean, [], function () {})); return true; } catch (e) { return false; } } function _getPrototypeOf(o) { _getPrototypeOf = Object.setPrototypeOf ? Object.getPrototypeOf : function _getPrototypeOf(o) { return o.__proto__ || Object.getPrototypeOf(o); }; return _getPrototypeOf(o); } diff --git a/lib/components/SiteMap/SiteMapContainer.js b/lib/components/SiteMap/SiteMapContainer.js index aae9ee58..6ea9d177 100644 --- a/lib/components/SiteMap/SiteMapContainer.js +++ b/lib/components/SiteMap/SiteMapContainer.js @@ -130,6 +130,9 @@ function _arrayWithHoles(arr) { if (Array.isArray(arr)) return arr; } var boxShadow = '0px 2px 1px -1px rgba(0,0,0,0.2), 0px 1px 1px 0px rgba(0,0,0,0.14), 0px 1px 3px 0px rgba(0,0,0,0.12)'; var useStyles = (0, _styles.makeStyles)(function (theme) { return { + ':root': { + fontSize: '24px' + }, outerContainer: { zIndex: 0, width: '100%', @@ -140,7 +143,7 @@ var useStyles = (0, _styles.makeStyles)(function (theme) { height: '0px', // Necessary to set a fixed aspect ratio from props (using paddingBottom) position: 'relative', - backgroundColor: theme.colors.NEON_BLUE[200], + backgroundColor: _Theme.default.colors.NEON_BLUE[200], overflow: 'hidden', display: 'flex', justifyContent: 'center', @@ -228,23 +231,23 @@ var useStyles = (0, _styles.makeStyles)(function (theme) { height: '26px', padding: 'unset', borderRadius: '2px 0px 2px 0px', - border: "1px solid ".concat(theme.colors.LIGHT_BLUE[500]), + border: "1px solid ".concat(_Theme.default.colors.LIGHT_BLUE[500]), cursor: 'grab', '&:hover, &:active': { - color: theme.colors.LIGHT_BLUE[400], - borderColor: theme.colors.LIGHT_BLUE[400], + color: _Theme.default.colors.LIGHT_BLUE[400], + borderColor: _Theme.default.colors.LIGHT_BLUE[400], backgroundColor: theme.palette.grey[50] }, '&:active': { cursor: 'row-resize !important' }, '& svg': { - fontSize: '1.15rem !important' + fontSize: '17px !important' } }, resizeBorder: { position: 'absolute', - border: "3px solid ".concat(theme.colors.LIGHT_BLUE[500]), + border: "3px solid ".concat(_Theme.default.colors.LIGHT_BLUE[500]), top: '0px', left: '0px', width: '100%', @@ -404,7 +407,8 @@ var SiteMapContainer = function SiteMapContainer(props) { selectionValid = _state$selection.valid, selectionLimit = _state$selection.limit, hideUnselectable = _state$selection.hideUnselectable, - showSummary = _state$selection.showSummary; + showSummary = _state$selection.showSummary, + manualLocationData = state.manualLocationData; var contentDivProps = { className: classes.contentContainer, style: tableFullHeight ? { @@ -599,6 +603,10 @@ var SiteMapContainer = function SiteMapContainer(props) { var renderMapTableToggleButtonGroup = function renderMapTableToggleButtonGroup() { var _viewTooltips; + if (state.view.current === _SiteMapUtils.VIEWS.SPLIT) { + return null; + } + var viewTooltips = (_viewTooltips = {}, _defineProperty(_viewTooltips, _SiteMapUtils.VIEWS.MAP, 'Show the observatory map'), _defineProperty(_viewTooltips, _SiteMapUtils.VIEWS.TABLE, 'Show a table of all locations currently visible in the map'), _viewTooltips); return /*#__PURE__*/_react.default.createElement(_ToggleButtonGroup.default, { exclusive: true, @@ -612,7 +620,9 @@ var SiteMapContainer = function SiteMapContainer(props) { }); }, className: fullscreen ? classes.mapTableToggleButtonGroupFullscreen : classes.mapTableToggleButtonGroup - }, Object.keys(_SiteMapUtils.VIEWS).map(function (key) { + }, Object.keys(_SiteMapUtils.VIEWS).filter(function (key) { + return key !== _SiteMapUtils.VIEWS.SPLIT; + }).map(function (key) { return /*#__PURE__*/_react.default.createElement(_Tooltip.default, { key: key, title: viewTooltips[key], @@ -901,7 +911,7 @@ var SiteMapContainer = function SiteMapContainer(props) { placement: "left", enterDelay: 500, enterNextDelay: 200, - title: "Resize ".concat(view === _SiteMapUtils.VIEWS.MAP ? 'map' : 'table', " vertically") + title: "Resize ".concat(view === _SiteMapUtils.VIEWS.TABLE ? 'table' : 'map', " vertically") }, /*#__PURE__*/_react.default.createElement(_IconButton.default, { draggable: true, type: "button", @@ -932,7 +942,13 @@ var SiteMapContainer = function SiteMapContainer(props) { featureStyle = _feature$style === void 0 ? {} : _feature$style, description = feature.description, descriptionFromParentDataFeatureKey = feature.descriptionFromParentDataFeatureKey, - parentDataFeatureKey = feature.parentDataFeatureKey; + parentDataFeatureKey = feature.parentDataFeatureKey, + dataSource = feature.dataSource; // Special case: do not include any features with a dataSource of MANUAL_LOCATIONS if + // manualLocationData is not also defined + + if (dataSource === _SiteMapUtils.FEATURE_DATA_SOURCES.MANUAL_LOCATIONS && !manualLocationData) { + return null; + } var handleChange = function handleChange(event) { dispatch({ @@ -1385,11 +1401,11 @@ var SiteMapContainer = function SiteMapContainer(props) { /* eslint-enable max-len */ } - return /*#__PURE__*/_react.default.createElement("div", _extends({}, containerProps, { + var ret = /*#__PURE__*/_react.default.createElement("div", _extends({}, containerProps, { "aria-describedby": progressId }), /*#__PURE__*/_react.default.createElement("div", _extends({ ref: contentDivRef - }, contentDivProps), view === _SiteMapUtils.VIEWS.MAP ? /*#__PURE__*/_react.default.createElement(_SiteMapLeaflet.default, null) : null, view === _SiteMapUtils.VIEWS.TABLE ? /*#__PURE__*/_react.default.createElement(_SiteMapTable.default, null) : null, renderVerticalResizeButton(), /*#__PURE__*/_react.default.createElement("div", { + }, contentDivProps), view === _SiteMapUtils.VIEWS.MAP || view === _SiteMapUtils.VIEWS.SPLIT ? /*#__PURE__*/_react.default.createElement(_SiteMapLeaflet.default, null) : null, view === _SiteMapUtils.VIEWS.TABLE ? /*#__PURE__*/_react.default.createElement(_SiteMapTable.default, null) : null, renderVerticalResizeButton(), /*#__PURE__*/_react.default.createElement("div", { ref: legendRef, className: legendContainerClassName, style: { @@ -1400,7 +1416,9 @@ var SiteMapContainer = function SiteMapContainer(props) { }, renderUnselectablesButton(), renderMapTableToggleButtonGroup(), renderLegendButton()), renderSelectionSummary()), fullscreen ? null : /*#__PURE__*/_react.default.createElement("div", { ref: resizeBorderRef, className: classes.resizeBorder - })); + }), view === _SiteMapUtils.VIEWS.SPLIT ? /*#__PURE__*/_react.default.createElement(_SiteMapTable.default, null) : null); + + return ret; }; SiteMapContainer.propTypes = { diff --git a/lib/components/SiteMap/SiteMapContext.d.ts b/lib/components/SiteMap/SiteMapContext.d.ts index 33786380..52013da5 100644 --- a/lib/components/SiteMap/SiteMapContext.d.ts +++ b/lib/components/SiteMap/SiteMapContext.d.ts @@ -59,6 +59,9 @@ declare namespace Provider { onSelectionChange: PropTypes.Requireable<(...args: any[]) => any>; search: PropTypes.Requireable; features: PropTypes.Requireable[]>; + manualLocationData: PropTypes.Requireable<(PropTypes.InferProps<{ + manualLocationType: PropTypes.Validator; + }> | null | undefined)[]>; }; export { SITE_MAP_DEFAULT_PROPS as defaultProps }; } @@ -66,7 +69,7 @@ declare function useSiteMapContext(): any[] | { view: { current: null; initialized: { - [k: string]: boolean; + [x: string]: boolean; }; }; neonContextHydrated: boolean; @@ -151,6 +154,7 @@ declare function useSiteMapContext(): any[] | { }; }; fullscreen: boolean; + manualLocationData: null; }; import { SORT_DIRECTIONS } from "./SiteMapUtils"; import { VIEWS } from "./SiteMapUtils"; diff --git a/lib/components/SiteMap/SiteMapContext.js b/lib/components/SiteMap/SiteMapContext.js index c0575140..7c691145 100644 --- a/lib/components/SiteMap/SiteMapContext.js +++ b/lib/components/SiteMap/SiteMapContext.js @@ -183,7 +183,21 @@ var centerIsValid = function centerIsValid(center) { var calculateFeatureDataFetches = function calculateFeatureDataFetches(state) { var requiredSites = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : []; - var sitesInMap = (0, _SiteMapUtils.calculateLocationsInBounds)(state.sites, state.map.bounds, true, 0.06); + // Determine which sites are in the map from the set of all current sites unless + // manualLocationData is non-empty and contains prototpye sites + var fetchablePrototypeSites = (state.manualLocationData || []).filter(function (manualLocation) { + return manualLocation.manualLocationType === _SiteMapUtils.MANUAL_LOCATION_TYPES.PROTOTYPE_SITE && state.sites[manualLocation.siteCode]; + }); + var sitesToConsider = state.sites; + + if (state.manualLocationData && fetchablePrototypeSites.length) { + sitesToConsider = {}; + fetchablePrototypeSites.forEach(function (manualLocation) { + sitesToConsider[manualLocation.siteCode] = state.sites[manualLocation.siteCode]; + }); + } + + var sitesInMap = (0, _SiteMapUtils.calculateLocationsInBounds)(sitesToConsider, state.map.bounds, true, 0.06); var requiredSitesArray = []; if (requiredSites && requiredSites.length) { @@ -912,9 +926,9 @@ var reducer = function reducer(state, action) { }; newState.focusLocation.current = action.location; newState.focusLocation.data = null; - newState.overallFetch.expected += 1; + newState.overallFetch.expected += 1; // Switch view to MAP if we're on TABLE (and not on SPLIT) - if (newState.view.current !== _SiteMapUtils.VIEWS.MAP) { + if (newState.view.current === _SiteMapUtils.VIEWS.TABLE) { newState.view.current = _SiteMapUtils.VIEWS.MAP; } @@ -1178,6 +1192,7 @@ var Provider = function Provider(props) { selectionLimit = props.selectionLimit, onSelectionChange = props.onSelectionChange, tableFullHeight = props.tableFullHeight, + manualLocationData = props.manualLocationData, children = props.children; // Neon Context State var _NeonContext$useNeonC = _NeonContext.default.useNeonContextState(), @@ -1247,6 +1262,10 @@ var Provider = function Provider(props) { } } + if (Array.isArray(manualLocationData) && manualLocationData.length > 0) { + initialState.manualLocationData = manualLocationData; + } + if (neonContextIsFinal && !neonContextHasError) { initialState = (0, _SiteMapUtils.hydrateNeonContextData)(initialState, neonContextData); } @@ -1258,7 +1277,7 @@ var Provider = function Provider(props) { var canFetchFeatureData = state.neonContextHydrated && !(state.focusLocation.current && state.focusLocation.fetch.status !== _SiteMapUtils.FETCH_STATUS.SUCCESS); /** - Effect - trigger focusLocation fetch or short circuit if found in NeonContext data + Effect - trigger focusLocation fetch or short circuit if found in NeonContext or manual data */ (0, _react.useEffect)(function () { @@ -1337,6 +1356,33 @@ var Provider = function Provider(props) { return function () { return window.clearTimeout(_timeout2); }; + } // If the location is found in manualLocationData then pull from there + + + if (state.manualLocationData) { + var manualSite = state.manualLocationData.find(function (ml) { + return ml.siteCode === current; + }); + + if (manualSite) { + var _latitude3 = manualSite.latitude, + _longitude3 = manualSite.longitude; + + var _timeout3 = window.setTimeout(function () { + dispatch({ + type: 'setFocusLocationFetchSucceeded', + data: { + type: 'SITE', + latitude: _latitude3, + longitude: _longitude3 + } + }); + }, 0); + + return function () { + return window.clearTimeout(_timeout3); + }; + } } // Trigger focus location fetch @@ -1355,7 +1401,7 @@ var Provider = function Provider(props) { }); }); return noop; - }, [state.sites, state.focusLocation, state.focusLocation.fetch.status, state.neonContextHydrated, state.featureData]); + }, [state.sites, state.focusLocation, state.focusLocation.fetch.status, state.neonContextHydrated, state.manualLocationData, state.featureData]); /** Effect - trigger all data fetches and imports */ diff --git a/lib/components/SiteMap/SiteMapFeature.js b/lib/components/SiteMap/SiteMapFeature.js index 8956103e..2198b8dc 100644 --- a/lib/components/SiteMap/SiteMapFeature.js +++ b/lib/components/SiteMap/SiteMapFeature.js @@ -294,6 +294,7 @@ var SiteMapFeature = function SiteMapFeature(props) { var neonContextHydrated = state.neonContextHydrated, + manualLocationData = state.manualLocationData, mapBounds = state.map.bounds, focusLocation = state.focusLocation.current, featureData = state.featureData[parentFeature ? parentFeature.type : featureType][parentFeature ? parentFeature.KEY : featureKey]; @@ -1223,6 +1224,95 @@ var SiteMapFeature = function SiteMapFeature(props) { "data-selenium": "sitemap-map-popup-domainLink" }, markerIcon, "".concat(site.domainCode, " - ").concat(domain.name))))), renderActions()); }; + /** + Render: Decommissioned Site Popup + */ + + + var renderDecommissionedSitePopup = function renderDecommissionedSitePopup(siteCode) { + var site = featureData[siteCode] || {}; + var _state$featureData$FE3 = state.featureData[_SiteMapUtils.FEATURE_TYPES.STATES.KEY][_SiteMapUtils.FEATURES.STATES.KEY][site.state], + usState = _state$featureData$FE3 === void 0 ? {} : _state$featureData$FE3; + var _state$featureData$FE4 = state.featureData[_SiteMapUtils.FEATURE_TYPES.DOMAINS.KEY][_SiteMapUtils.FEATURES.DOMAINS.KEY][site.domain], + domain = _state$featureData$FE4 === void 0 ? {} : _state$featureData$FE4; + var stateFieldTitle = site.stateCode === 'PR' ? 'Territory' : 'State'; + return /*#__PURE__*/_react.default.createElement(_reactLeaflet.Popup, popupProps, renderPopupTitle("".concat(site.siteName, " (").concat(site.siteCode, ")"), false), /*#__PURE__*/_react.default.createElement(_Link.default, { + variant: "caption", + component: "button", + onClick: function onClick() { + return jumpTo(site.siteCode); + }, + style: { + marginLeft: '-2px', + marginBottom: '8px' + }, + "data-selenium": "sitemap-map-popup-siteLink" + }, markerIcon, "Jump to ".concat(site.siteCode, " on the map")), /*#__PURE__*/_react.default.createElement(_Grid.default, { + container: true, + spacing: 1, + style: { + marginBottom: _Theme.default.spacing(1) + } + }, /*#__PURE__*/_react.default.createElement(_Grid.default, { + item: true, + xs: 8 + }, /*#__PURE__*/_react.default.createElement(_Typography.default, { + variant: "subtitle2" + }, feature.nameSingular), /*#__PURE__*/_react.default.createElement(_Typography.default, { + variant: "caption" + }, /*#__PURE__*/_react.default.createElement("i", null, featureDescription))), /*#__PURE__*/_react.default.createElement(_Grid.default, { + item: true, + xs: 4, + style: { + textAlign: 'right' + } + }, /*#__PURE__*/_react.default.createElement(_Typography.default, { + variant: "subtitle2" + }, stateFieldTitle), selectionActive ? /*#__PURE__*/_react.default.createElement(_Typography.default, { + variant: "caption" + }, usState.name) : /*#__PURE__*/_react.default.createElement(_Tooltip.default, { + title: "Jump to ".concat(usState.name, " on the map") + }, /*#__PURE__*/_react.default.createElement(_Link.default, { + variant: "caption", + component: "button", + style: { + textAlign: 'right' + }, + onClick: function onClick() { + return jumpTo(site.state); + }, + "data-selenium": "sitemap-map-popup-stateLink" + }, markerIcon, usState.name))), /*#__PURE__*/_react.default.createElement(_Grid.default, { + item: true, + xs: 5, + style: { + display: 'flex', + alignItems: 'flex-end' + } + }, renderLatLon(site.latitude, site.longitude)), /*#__PURE__*/_react.default.createElement(_Grid.default, { + item: true, + xs: 7, + style: { + textAlign: 'right' + } + }, /*#__PURE__*/_react.default.createElement(_Typography.default, { + variant: "subtitle2" + }, "Domain"), selectionActive ? /*#__PURE__*/_react.default.createElement(_Typography.default, { + variant: "caption" + }, "".concat(site.domain, " - ").concat(domain.name)) : /*#__PURE__*/_react.default.createElement(_Tooltip.default, { + title: "Jump to ".concat(site.domain, " on the map") + }, /*#__PURE__*/_react.default.createElement(_Link.default, { + variant: "caption", + component: "button", + style: { + textAlign: 'right' + }, + onClick: function onClick() { + return jumpTo(site.domain); + }, + "data-selenium": "sitemap-map-popup-domainLink" + }, markerIcon, "".concat(site.domain, " - ").concat(domain.name)))))); + }; /** Render - All the Rest of the Popups Convention is alphabetical listing of keys since order here doesn't matter @@ -1249,6 +1339,7 @@ var SiteMapFeature = function SiteMapFeature(props) { AQUATIC_SENSOR_STATIONS: renderLocationPopup, AQUATIC_STAFF_GAUGES: renderLocationPopup, AQUATIC_WET_DEPOSITION_POINTS: renderLocationPopup, + DECOMMISSIONED_SITES: renderDecommissionedSitePopup, DISTRIBUTED_BASE_PLOTS: function DISTRIBUTED_BASE_PLOTS(siteCode, location) { return renderLocationPopup(siteCode, location, [renderPlotSizeAndSlope, renderPlotSamplingModules]); }, @@ -1640,14 +1731,31 @@ var SiteMapFeature = function SiteMapFeature(props) { */ - return /*#__PURE__*/_react.default.createElement(_reactLeaflet.FeatureGroup, null, Object.keys(featureData) // Valid items should render above unselecatbles + var renderableKeys = Object.keys(featureData); + + if (Array.isArray(manualLocationData) && manualLocationData.length) { + var hasPrototypeSites = manualLocationData.some(function (ml) { + return ml.manualLocationType === _SiteMapUtils.MANUAL_LOCATION_TYPES.PROTOTYPE_SITE; + }); + + if (hasPrototypeSites && featureType === _SiteMapUtils.FEATURE_TYPES.SITES.KEY) { + var allKeys = Object.keys(featureData); + renderableKeys = manualLocationData.filter(function (ml) { + return ml.manualLocationType === _SiteMapUtils.MANUAL_LOCATION_TYPES.PROTOTYPE_SITE && allKeys.includes(ml.siteCode); + }).map(function (ml) { + return ml.siteCode; + }); + } + } + + return /*#__PURE__*/_react.default.createElement(_reactLeaflet.FeatureGroup, null, renderableKeys // Valid items should render above unselecatbles .sort(function (a) { if (!validItems) { return 0; } return validItems.has(a) ? 1 : -1; - }) // Focus lcoation should render above all others + }) // Focus location should render above all others .sort(function (a) { return a === state.focusLocation.current ? 1 : -1; }).flatMap(function (keyA) { diff --git a/lib/components/SiteMap/SiteMapLeaflet.js b/lib/components/SiteMap/SiteMapLeaflet.js index 9b79b65e..e1ddeffb 100644 --- a/lib/components/SiteMap/SiteMapLeaflet.js +++ b/lib/components/SiteMap/SiteMapLeaflet.js @@ -225,10 +225,10 @@ var useStyles = (0, _styles.makeStyles)(function (theme) { height: '26px', padding: 'unset', borderRadius: '2px 0px 2px 0px', - border: "1px solid ".concat(theme.colors.LIGHT_BLUE[500]), + border: "1px solid ".concat(_Theme.default.colors.LIGHT_BLUE[500]), '&:hover, &:active': { - color: theme.colors.LIGHT_BLUE[400], - borderColor: theme.colors.LIGHT_BLUE[400], + color: _Theme.default.colors.LIGHT_BLUE[400], + borderColor: _Theme.default.colors.LIGHT_BLUE[400], backgroundColor: theme.palette.grey[50] }, '& svg': { @@ -261,8 +261,8 @@ var useStyles = (0, _styles.makeStyles)(function (theme) { areaSelection: { position: 'absolute', pointerEvents: 'none', - border: "3px dotted ".concat(theme.colors.LIGHT_BLUE[500]), - backgroundColor: theme.colors.LIGHT_BLUE[100], + border: "3px dotted ".concat(_Theme.default.colors.LIGHT_BLUE[500]), + backgroundColor: _Theme.default.colors.LIGHT_BLUE[100], opacity: 0.6, zIndex: 999 } diff --git a/lib/components/SiteMap/SiteMapTable.d.ts b/lib/components/SiteMap/SiteMapTable.d.ts index fe0c27c2..ef584a24 100644 --- a/lib/components/SiteMap/SiteMapTable.d.ts +++ b/lib/components/SiteMap/SiteMapTable.d.ts @@ -1,2 +1,15 @@ export default SiteMapTable; +export function getTestableItems(): { + ucWord?: undefined; + parseSearchTerms?: undefined; + searchOnAttribs?: undefined; + calculateMaxBodyHeight?: undefined; + getFeatureName?: undefined; +} | { + ucWord: (word?: string) => string; + parseSearchTerms: (input: string) => string[]; + searchOnAttribs: (searchString: any, searchableAttribs?: any[]) => boolean; + calculateMaxBodyHeight: (tableRef: any) => number; + getFeatureName: (featureKey: any) => any; +}; declare function SiteMapTable(): JSX.Element | null; diff --git a/lib/components/SiteMap/SiteMapTable.js b/lib/components/SiteMap/SiteMapTable.js index 5135de6f..c5d8312e 100644 --- a/lib/components/SiteMap/SiteMapTable.js +++ b/lib/components/SiteMap/SiteMapTable.js @@ -5,7 +5,7 @@ function _typeof(obj) { "@babel/helpers - typeof"; if (typeof Symbol === "functi Object.defineProperty(exports, "__esModule", { value: true }); -exports.default = void 0; +exports.getTestableItems = exports.default = void 0; var _react = _interopRequireWildcard(require("react")); @@ -13,6 +13,8 @@ var _styles = require("@material-ui/core/styles"); var _Box = _interopRequireDefault(require("@material-ui/core/Box")); +var _Button = _interopRequireDefault(require("@material-ui/core/Button")); + var _Link = _interopRequireDefault(require("@material-ui/core/Link")); var _IconButton = _interopRequireDefault(require("@material-ui/core/IconButton")); @@ -23,10 +25,6 @@ var _Typography = _interopRequireDefault(require("@material-ui/core/Typography") var _InfoOutlined = _interopRequireDefault(require("@material-ui/icons/InfoOutlined")); -var _LocationOn = _interopRequireDefault(require("@material-ui/icons/LocationOn")); - -var _InsertChartOutlined = _interopRequireDefault(require("@material-ui/icons/InsertChartOutlined")); - var _materialTable = _interopRequireWildcard(require("material-table")); var _MaterialTableIcons = _interopRequireDefault(require("../MaterialTableIcons/MaterialTableIcons")); @@ -61,23 +59,76 @@ function _arrayWithHoles(arr) { if (Array.isArray(arr)) return arr; } function _defineProperty(obj, key, value) { if (key in obj) { Object.defineProperty(obj, key, { value: value, enumerable: true, configurable: true, writable: true }); } else { obj[key] = value; } return obj; } -var ucWord = function ucWord(word) { +var ucWord = function ucWord() { + var word = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : ''; return "".concat(word.slice(0, 1).toUpperCase()).concat(word.slice(1).toLowerCase()); }; +/** + * Parse an input search string into discrete terms. + * Supports quoting words together as a single term. + * Example: '"foo bar" baz' => ['foo bar', 'baz'] + * @param {string} input - string to parse into discrete terms + */ + + +var parseSearchTerms = function parseSearchTerms(input) { + var terms = input.replace(/[^\w\s."]/g, '').match(/(".*?"|[^" \s]+)(?=\s* |\s*$)/g); + return (terms || []).map(function (term) { + return term.replace(/"/g, '').toLowerCase(); + }); +}; +/** + * Apply a searchString to a list of string attributes; return boolean for a match + */ + + +var searchOnAttribs = function searchOnAttribs(searchString) { + var searchableAttribs = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : []; + var searchTerms = parseSearchTerms(searchString); + + if (!searchTerms.length) { + return true; + } + + return searchTerms.some(function (term) { + return searchableAttribs.some(function (attrib) { + return (attrib || '').toLowerCase().includes(term); + }); + }); +}; + +var getFeatureName = function getFeatureName(featureKey) { + if (_SiteMapUtils.FEATURES[featureKey]) { + return _SiteMapUtils.FEATURES[featureKey].nameSingular || _SiteMapUtils.FEATURES[featureKey].name || featureKey; + } + + return featureKey; +}; + +var calculateMaxBodyHeight = function calculateMaxBodyHeight(tableRef) { + if (!tableRef || !tableRef.current) { + return _SiteMapUtils.MIN_TABLE_MAX_BODY_HEIGHT; + } + + var containerHeight = tableRef.current.clientHeight || 0; + var toolbarHeight = tableRef.current.children[0].children[0].clientHeight || 0; + var pagerHeight = tableRef.current.children[0].children[2].clientHeight || 0; + return Math.max(containerHeight - toolbarHeight - pagerHeight, _SiteMapUtils.MIN_TABLE_MAX_BODY_HEIGHT); +}; var useStyles = (0, _styles.makeStyles)(function (theme) { var _toolbarContainer; return { tableContainer: { - position: 'absolute', - width: '100%', - height: '100%', backgroundColor: 'white', overflowWrap: 'normal', '& table': { margin: '0px !important', borderCollapse: 'separate', + '& tr.MuiTableRow-root:empty': { + height: '0px !important' + }, '& tr.MuiTableRow-head': { backgroundColor: theme.palette.primary.main, '& th:first-child span.MuiCheckbox-root': { @@ -99,6 +150,13 @@ var useStyles = (0, _styles.makeStyles)(function (theme) { borderBottom: 'none' } }, + tableContainerIntegrated: { + position: 'absolute', + width: '100%', + height: '100%' + }, + tableContainerStandalone: {// ... + }, featureIcon: { width: theme.spacing(3), height: theme.spacing(3), @@ -114,15 +172,12 @@ var useStyles = (0, _styles.makeStyles)(function (theme) { justifyContent: 'flex-start' }, toolbarContainer: (_toolbarContainer = { - backgroundColor: theme.palette.grey[50], - borderBottom: "1px dotted ".concat(theme.palette.grey[300]) + backgroundColor: theme.palette.grey[50] }, _defineProperty(_toolbarContainer, theme.breakpoints.down('xs'), { paddingTop: theme.spacing(4.5) }), _defineProperty(_toolbarContainer, '& div.MuiToolbar-root', { padding: theme.spacing(0, 2), backgroundColor: theme.palette.grey[50] - }), _defineProperty(_toolbarContainer, '& div.MuiToolbar-root > div:not(:nth-last-child(-n+2))', { - display: 'none' }), _defineProperty(_toolbarContainer, '& button', { color: theme.palette.primary.main, '&:hover, &:active': { @@ -132,6 +187,13 @@ var useStyles = (0, _styles.makeStyles)(function (theme) { padding: '11px' } }), _toolbarContainer), + toolbarContainerNoSplit: { + // This hides all but the search input and show columns buttons. + // No other way to have material table NOT show a selection title in the toolbar. + '& div.MuiToolbar-root > div:not(:nth-last-child(-n+2))': { + display: 'none' + } + }, toggleButtonGroup: { height: theme.spacing(4) }, @@ -160,8 +222,16 @@ var useStyles = (0, _styles.makeStyles)(function (theme) { display: 'flex', alignItems: 'center', justifyContent: 'flex-start', - margin: _Theme.default.spacing(1, 0, 0.5, 0), - minWidth: '200px' + margin: theme.spacing(1, 0, 0.5, 0), + minWidth: '200px', + textAlign: 'left' + }, + siteLinksDivider: { + margin: theme.spacing(0, 1, 0, 1) + }, + siteDetailsLink: { + fontSize: '80%', + fontStyle: 'italic' }, nlcdClassContainer: { display: 'flex', @@ -172,21 +242,16 @@ var useStyles = (0, _styles.makeStyles)(function (theme) { height: '14px', border: '1px solid black', marginRight: theme.spacing(1.5) + }, + tableTitle: { + '& h6': { + fontSize: '1.2rem', + fontWeight: 500 + } } }; }); -var calculateMaxBodyHeight = function calculateMaxBodyHeight(tableRef) { - if (!tableRef || !tableRef.current) { - return _SiteMapUtils.MIN_TABLE_MAX_BODY_HEIGHT; - } - - var containerHeight = tableRef.current.clientHeight || 0; - var toolbarHeight = tableRef.current.children[0].children[0].clientHeight || 0; - var pagerHeight = tableRef.current.children[0].children[2].clientHeight || 0; - return Math.max(containerHeight - toolbarHeight - pagerHeight, _SiteMapUtils.MIN_TABLE_MAX_BODY_HEIGHT); -}; - var SiteMapTable = function SiteMapTable() { var classes = useStyles(_Theme.default); var tableRef = (0, _react.useRef)(null); // Neon Context State @@ -204,6 +269,10 @@ var SiteMapTable = function SiteMapTable() { state = _SiteMapContext$useSi2[0], dispatch = _SiteMapContext$useSi2[1]; + var manualLocationData = state.manualLocationData, + _state$view = state.view, + view = _state$view.current, + viewsInitialized = _state$view.initialized; var _state$table = state.table, focus = _state$table.focus, fullHeight = _state$table.fullHeight, @@ -221,7 +290,7 @@ var SiteMapTable = function SiteMapTable() { */ (0, _react.useEffect)(function () { - if (!tableRef || !tableRef.current || state.view.current !== _SiteMapUtils.VIEWS.TABLE || state.view.initialized[_SiteMapUtils.VIEWS.TABLE]) { + if (!tableRef || !tableRef.current || view !== _SiteMapUtils.VIEWS.TABLE || viewsInitialized[_SiteMapUtils.VIEWS.TABLE]) { return; } @@ -232,19 +301,105 @@ var SiteMapTable = function SiteMapTable() { type: 'setTableMaxBodyHeight', height: calculateMaxBodyHeight(tableRef) }); - }, [tableRef, state.view, dispatch]); + }, [tableRef, view, viewsInitialized, dispatch]); /** Effect - Recalculate the max body height from an aspect ratio change (e.g. page resize) */ (0, _react.useEffect)(function () { - if (state.view.current === _SiteMapUtils.VIEWS.TABLE && state.view.initialized[_SiteMapUtils.VIEWS.TABLE] && maxBodyHeightUpdateFromAspectRatio) { + if (view === _SiteMapUtils.VIEWS.TABLE && viewsInitialized[_SiteMapUtils.VIEWS.TABLE] && maxBodyHeightUpdateFromAspectRatio) { dispatch({ type: 'setTableMaxBodyHeight', height: calculateMaxBodyHeight(tableRef) }); } - }, [tableRef, state.view, dispatch, maxBodyHeightUpdateFromAspectRatio]); + }, [tableRef, view, viewsInitialized, dispatch, maxBodyHeightUpdateFromAspectRatio]); + /** + Layout Effect - Inject a second horizontal scrollbar above the table linked to the main + */ + + (0, _react.useLayoutEffect)(function () { + var noop = function noop() {}; // This all only applies to full height table and/or split view (which behaves as full height) + + + if (!fullHeight && view !== _SiteMapUtils.VIEWS.SPLIT) { + return noop; + } // Collect the nodes we pay attention to. Each one has a distinct purpose. + + + var tableNode = tableRef.current.querySelector('table'); + + if (!tableNode) { + return noop; + } + + var tbodyNode = tableRef.current.querySelector('tbody'); + + if (!tbodyNode) { + return noop; + } + + var scrollingNode = (tableNode.parentElement || {}).parentElement; + + if (!scrollingNode) { + return noop; + } + + var containerNode = scrollingNode.parentElement; + + if (!containerNode) { + return noop; + } // Initialize the new scrollbar in this scope + + + var scrollbar = null; // Function to do the initial injection fo the scrollbar node + + var injectScrollbar = function injectScrollbar() { + scrollbar = document.createElement('div'); + scrollbar.appendChild(document.createElement('div')); + scrollbar.style.overflow = 'auto'; + scrollbar.style.overflowY = 'hidden'; // eslint-disable-next-line prefer-destructuring + + scrollbar.style.backgroundColor = _Theme.default.palette.grey[50]; + scrollbar.firstChild.style.width = "".concat(tableNode.scrollWidth || 0, "px"); + scrollbar.firstChild.style.paddingTop = '1px'; + + scrollbar.onscroll = function () { + scrollingNode.scrollLeft = scrollbar.scrollLeft; + }; + + scrollingNode.onscroll = function () { + scrollbar.scrollLeft = scrollingNode.scrollLeft; + }; + + containerNode.parentNode.insertBefore(scrollbar, containerNode); + }; // Function to resize the scrollbar. We can't rely on the scrollWidth being accurate when we + // inject as not-yet-fully-rendered table rows may expand the scrollWidth. + + + var resizeScrollbar = function resizeScrollbar() { + if (!scrollbar) { + return; + } + + scrollbar.firstChild.style.width = "".concat(tableNode.scrollWidth || 0, "px"); + scrollbar.scrollLeft = scrollingNode.scrollLeft; + }; // Inject the scrollbar one step removed from the initial render cycle (with a 0-sec timeout) + + + var timeout = window.setTimeout(injectScrollbar, 0); // Observe the childList of the tbody - i.e. rows being added or removed - to trigger a resize + // of the injected scrollbar + + var observer = new MutationObserver(resizeScrollbar); + observer.observe(tbodyNode, { + childList: true + }); // Clear any pending timeouts and disconnect our observer on unload to avoid memory leaks + + return function () { + observer.disconnect(); + window.clearTimeout(timeout); + }; + }, [tableRef, fullHeight, view]); if (!canRender) { return null; @@ -299,9 +454,19 @@ var SiteMapTable = function SiteMapTable() { var source = null; if (type === 'SITE') { + var data = state.sites; + + if (Array.isArray(manualLocationData) && manualLocationData.length) { + data = {}; + manualLocationData.forEach(function (ml) { + var siteCode = ml.siteCode; + data[siteCode] = Object.keys(state.sites).includes(siteCode) ? state.sites[siteCode] : ml; + }); + } + source = { code: 'siteCode', - data: state.sites + data: data }; } else if (type === 'STATE') { source = { @@ -342,14 +507,6 @@ var SiteMapTable = function SiteMapTable() { return getParent('DOMAIN', location); }; - var getFeatureName = function getFeatureName(featureKey) { - if (_SiteMapUtils.FEATURES[featureKey]) { - return _SiteMapUtils.FEATURES[featureKey].nameSingular || _SiteMapUtils.FEATURES[featureKey].name || featureKey; - } - - return featureKey; - }; - var renderFeatureIcon = function renderFeatureIcon(featureKey) { var unselectable = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : false; @@ -408,6 +565,19 @@ var SiteMapTable = function SiteMapTable() { }); } + var hasPrototypeSites = (manualLocationData || []).some(function (ml) { + return ml.manualLocationType === _SiteMapUtils.MANUAL_LOCATION_TYPES.PROTOTYPE_SITE; + }); + + if (hasPrototypeSites) { + var visibleSites = manualLocationData.map(function (ml) { + return ml.siteCode; + }); + initialRows = initialRows.filter(function (item) { + return visibleSites.includes(item); + }); + } + var rows = initialRows.map(function (key) { return locations[key]; }); @@ -455,14 +625,29 @@ var SiteMapTable = function SiteMapTable() { title: 'Site', sorting: true, defaultSort: 'desc', + searchable: true, lookup: Object.fromEntries(Array.from(sitesInMap).map(function (siteCode) { return [siteCode, siteCode]; })), + customFilterAndSearch: function customFilterAndSearch(input, row) { + if (typeof input === 'string') { + return searchOnAttribs(input, [row.siteCode, row.description]); + } + + if (Array.isArray(input)) { + return input.includes(row.siteCode); + } + + return false; + }, customSort: function customSort(rowA, rowB) { var siteA = getSite(rowA); var siteB = getSite(rowB); - return siteA.description > siteB.description ? -1 : 1; + var aName = siteA.description; + var bName = siteB.description; + return aName > bName ? -1 : 1; }, + // eslint-disable-next-line arrow-body-style render: function render(row) { var site = getSite(row); @@ -470,51 +655,27 @@ var SiteMapTable = function SiteMapTable() { return null; } - var featureKey = "".concat(site.terrain.toUpperCase(), "_").concat(site.type.toUpperCase(), "_SITES"); + var isDecommissioned = site.manualLocationType === _SiteMapUtils.MANUAL_LOCATION_TYPES.PROTOTYPE_SITE; + var featureKey = isDecommissioned ? _SiteMapUtils.FEATURES.DECOMMISSIONED_SITES.KEY : "".concat(site.terrain.toUpperCase(), "_").concat(site.type.toUpperCase(), "_SITES"); var unselectable = selectionActive && !rowIsSelectable(row); - return /*#__PURE__*/_react.default.createElement("div", null, /*#__PURE__*/_react.default.createElement("div", { - className: classes.siteName - }, renderFeatureIcon(featureKey, unselectable), /*#__PURE__*/_react.default.createElement("span", null, "".concat(site.description, " (").concat(site.siteCode, ")"))), /*#__PURE__*/_react.default.createElement("div", { - className: classes.startFlex, - style: { - marginLeft: _Theme.default.spacing(-0.75) - } - }, /*#__PURE__*/_react.default.createElement(_Tooltip.default, { - title: "Jump to ".concat(site.siteCode, " on the map") - }, /*#__PURE__*/_react.default.createElement(_IconButton.default, { - size: "small", - color: "primary", - className: classes.iconButton, + return /*#__PURE__*/_react.default.createElement("div", null, /*#__PURE__*/_react.default.createElement(_Link.default, { + component: "button", + className: classes.siteName, onClick: function onClick() { - return jumpTo(site.siteCode); + return jumpTo(row.siteCode); }, - "data-selenium": "sitemap-table-site-button-jumpTo", - "aria-label": "Jump to site on map" - }, /*#__PURE__*/_react.default.createElement(_LocationOn.default, { - fontSize: "inherit" - }))), /*#__PURE__*/_react.default.createElement(_Tooltip.default, { - title: "Visit the ".concat(site.siteCode, " site details page") - }, /*#__PURE__*/_react.default.createElement(_IconButton.default, { - size: "small", - color: "primary", - className: classes.iconButton, - href: "".concat((0, _SiteMapUtils.getHref)('SITE_DETAILS', site.siteCode)), - "data-selenium": "sitemap-table-site-button-siteDetails", - "aria-label": "Visit site details page" - }, /*#__PURE__*/_react.default.createElement(_InfoOutlined.default, { - fontSize: "inherit" - }))), /*#__PURE__*/_react.default.createElement(_Tooltip.default, { - title: "Explore data products for ".concat(site.siteCode) - }, /*#__PURE__*/_react.default.createElement(_IconButton.default, { - size: "small", - color: "primary", - className: classes.iconButton, - href: "".concat((0, _SiteMapUtils.getHref)('EXPLORE_DATA_PRODUCTS_BY_SITE', site.siteCode)), - "data-selenium": "sitemap-table-site-button-exploreDataProducts", - "aria-label": "Explore data products for this site" - }, /*#__PURE__*/_react.default.createElement(_InsertChartOutlined.default, { - fontSize: "inherit" - }))))); + title: "Click to view ".concat(row.siteCode, " on the map") + }, renderFeatureIcon(featureKey, unselectable), /*#__PURE__*/_react.default.createElement("span", null, "".concat(site.description || 'Unnamed Site', " (").concat(site.siteCode, ")"))), /*#__PURE__*/_react.default.createElement("div", { + className: classes.startFlex + }, /*#__PURE__*/_react.default.createElement(_Link.default, { + className: classes.siteDetailsLink, + href: "".concat((0, _SiteMapUtils.getHref)('SITE_DETAILS', site.siteCode)) + }, "Site Details"), /*#__PURE__*/_react.default.createElement("span", { + className: classes.siteLinksDivider + }, "|"), /*#__PURE__*/_react.default.createElement(_Link.default, { + className: classes.siteDetailsLink, + href: "".concat((0, _SiteMapUtils.getHref)('EXPLORE_DATA_PRODUCTS_BY_SITE', site.siteCode)) + }, "Explore Data"))); } }, domain: { @@ -522,6 +683,7 @@ var SiteMapTable = function SiteMapTable() { title: 'Domain', sorting: true, defaultSort: 'desc', + searchable: true, lookup: Object.fromEntries(Array.from(domainsInMap).map(function (domainCode) { return [domainCode, domainCode]; })), @@ -532,40 +694,14 @@ var SiteMapTable = function SiteMapTable() { }, render: function render(row) { var domain = getDomain(row); - return !domain ? null : /*#__PURE__*/_react.default.createElement("div", null, /*#__PURE__*/_react.default.createElement("div", { - style: { - marginBottom: _Theme.default.spacing(1) - } - }, domain.domainCode), /*#__PURE__*/_react.default.createElement("div", { - className: classes.startFlex, - style: { - marginLeft: _Theme.default.spacing(-0.75) - } - }, /*#__PURE__*/_react.default.createElement(_Tooltip.default, { - title: "Jump to ".concat(domain.domainCode, " on the map") - }, /*#__PURE__*/_react.default.createElement(_IconButton.default, { - size: "small", - color: "primary", - className: classes.iconButton, + return !domain ? null : /*#__PURE__*/_react.default.createElement("div", null, /*#__PURE__*/_react.default.createElement(_Link.default, { + component: "button", + className: classes.linkButton, onClick: function onClick() { return jumpTo(domain.domainCode); }, - "data-selenium": "sitemap-table-domain-button-jumpTo", - "aria-label": "Jump to domain on map" - }, /*#__PURE__*/_react.default.createElement(_LocationOn.default, { - fontSize: "inherit" - }))), /*#__PURE__*/_react.default.createElement(_Tooltip.default, { - title: "Explore data products for ".concat(domain.domainCode) - }, /*#__PURE__*/_react.default.createElement(_IconButton.default, { - size: "small", - color: "primary", - className: classes.iconButton, - href: "".concat((0, _SiteMapUtils.getHref)('EXPLORE_DATA_PRODUCTS_BY_DOMAIN', domain.domainCode)), - "data-selenium": "sitemap-table-domain-button-exploreDataProducts", - "aria-label": "Explore data products for this domain" - }, /*#__PURE__*/_react.default.createElement(_InsertChartOutlined.default, { - fontSize: "inherit" - }))))); + title: "Click to view ".concat(domain.domainCode, " on the map") + }, domain.domainCode)); } }, state: { @@ -573,6 +709,7 @@ var SiteMapTable = function SiteMapTable() { title: 'State', sorting: true, defaultSort: 'desc', + searchable: true, lookup: Object.fromEntries(Array.from(statesInMap).map(function (stateCode) { return [stateCode, state.featureData.STATES.STATES[stateCode].name]; })), @@ -581,42 +718,38 @@ var SiteMapTable = function SiteMapTable() { var stateB = getState(rowB); return stateA.name > stateB.name ? -1 : 1; }, + customFilterAndSearch: function customFilterAndSearch(input, row) { + if (typeof input === 'string') { + var rowState = getState(row); + return searchOnAttribs(input, [row.stateCode, rowState.name]); + } + + if (Array.isArray(input)) { + return input.includes(row.stateCode); + } + + return false; + }, render: function render(row) { var usstate = getState(row); - return !usstate ? null : /*#__PURE__*/_react.default.createElement("div", null, /*#__PURE__*/_react.default.createElement("div", { - style: { - marginBottom: _Theme.default.spacing(1) - } - }, usstate.name), /*#__PURE__*/_react.default.createElement("div", { - className: classes.startFlex, - style: { - marginLeft: _Theme.default.spacing(-0.75) - } - }, /*#__PURE__*/_react.default.createElement(_Tooltip.default, { - title: "Jump to ".concat(usstate.name, " on the map") - }, /*#__PURE__*/_react.default.createElement(_IconButton.default, { - size: "small", - color: "primary", - className: classes.iconButton, + return !usstate ? null : /*#__PURE__*/_react.default.createElement("div", null, /*#__PURE__*/_react.default.createElement(_Link.default, { + component: "button", + className: classes.linkButton, onClick: function onClick() { - return jumpTo(usstate.stateCode); + return jumpTo(row.stateCode); }, - "data-selenium": "sitemap-table-state-button-jumpTo", - "aria-label": "Jump to state on map" - }, /*#__PURE__*/_react.default.createElement(_LocationOn.default, { - fontSize: "inherit" - }))), /*#__PURE__*/_react.default.createElement(_Tooltip.default, { - title: "Explore data products for ".concat(usstate.stateCode) - }, /*#__PURE__*/_react.default.createElement(_IconButton.default, { - size: "small", - color: "primary", - className: classes.iconButton, - href: "".concat((0, _SiteMapUtils.getHref)('EXPLORE_DATA_PRODUCTS_BY_STATE', usstate.stateCode)), - "data-selenium": "sitemap-table-state-button-exploreDataProducts", - "aria-label": "Explore data products for this state" - }, /*#__PURE__*/_react.default.createElement(_InsertChartOutlined.default, { - fontSize: "inherit" - }))))); + title: "Click to view ".concat(row.stateCode, " on the map") + }, usstate.name)); + } + }, + coordinates: { + field: 'latitude', + title: 'Coordinates', + sorting: false, + filtering: false, + searchable: false, + render: function render(row) { + return /*#__PURE__*/_react.default.createElement(_react.default.Fragment, null, renderCaptionString(row.latitude.toFixed(5), 'Latitude'), /*#__PURE__*/_react.default.createElement("br", null), renderCaptionString(row.longitude.toFixed(5), 'Longitude')); } }, latitude: { @@ -645,12 +778,13 @@ var SiteMapTable = function SiteMapTable() { var columns = []; if (focus === _SiteMapUtils.FEATURE_TYPES.SITES.KEY) { - columns = [commonColumns.site, commonColumns.latitude, commonColumns.longitude, { + columns = [commonColumns.site, { // Site Type field: 'type', title: 'Type', sorting: true, defaultSort: 'desc', + searchable: true, lookup: Object.fromEntries(Array.from(new Set(rows.map(function (row) { return row.type; }))).sort().map(function (k) { @@ -673,7 +807,12 @@ var SiteMapTable = function SiteMapTable() { render: function render(row) { return ucWord(row.terrain); } - }, commonColumns.domain, commonColumns.state]; + }, commonColumns.domain, commonColumns.state, commonColumns.coordinates + /* + commonColumns.latitude, + commonColumns.longitude, + */ + ]; } if (focus === _SiteMapUtils.FEATURE_TYPES.LOCATIONS.KEY) { @@ -683,13 +822,15 @@ var SiteMapTable = function SiteMapTable() { title: 'Name', sorting: true, defaultSort: 'desc', + searchable: true, render: function render(row) { return /*#__PURE__*/_react.default.createElement(_Link.default, { component: "button", className: classes.linkButton, onClick: function onClick() { return jumpTo(row.name); - } + }, + title: "View ".concat(row.name, " on map") }, row.name); } }, { @@ -698,6 +839,7 @@ var SiteMapTable = function SiteMapTable() { title: 'Type', sorting: true, defaultSort: 'desc', + searchable: true, lookup: Object.fromEntries(Array.from(new Set(rows.map(function (row) { return row.featureKey; }))).sort(function (a, b) { @@ -722,7 +864,7 @@ var SiteMapTable = function SiteMapTable() { className: classes.startFlex }, renderFeatureIcon(featureKey), /*#__PURE__*/_react.default.createElement("span", null, featureName)); } - }, commonColumns.latitude, commonColumns.longitude, { + }, { // Elevation field: 'elevation', title: 'Elevation', @@ -738,6 +880,7 @@ var SiteMapTable = function SiteMapTable() { title: 'NLCD Class', sorting: true, deafultSort: 'asc', + searchable: true, lookup: Object.fromEntries(Object.keys(_SiteMapUtils.NLCD_CLASSES).filter(function (classKey) { return rows.some(function (row) { return row.nlcdClass === classKey; @@ -804,6 +947,7 @@ var SiteMapTable = function SiteMapTable() { filtering: false, sorting: true, deafultSort: 'asc', + searchable: true, customSort: function customSort(rowA, rowB) { var a = Array.isArray(rowA.samplingModules) ? rowA.samplingModules.length : null; var b = Array.isArray(rowB.samplingModules) ? rowB.samplingModules.length : null; @@ -845,16 +989,26 @@ var SiteMapTable = function SiteMapTable() { fontSize: "inherit" })))) : renderCaptionString(); } - }, commonColumns.site, commonColumns.domain, commonColumns.state]; + }, commonColumns.site, commonColumns.domain, commonColumns.state, commonColumns.coordinates + /* + commonColumns.latitude, + commonColumns.longitude, + */ + ]; } + var toolbarClassName = view === _SiteMapUtils.VIEWS.SPLIT ? classes.toolbarContainer : "".concat(classes.toolbarContainer, " ").concat(classes.toolbarContainerNoSplit); var components = { Container: _Box.default, Toolbar: function Toolbar(toolbarProps) { return /*#__PURE__*/_react.default.createElement("div", { - className: classes.toolbarContainer, + className: toolbarClassName, "data-selenium": "sitemap-table-toolbar" - }, /*#__PURE__*/_react.default.createElement(_materialTable.MTableToolbar, toolbarProps)); + }, /*#__PURE__*/_react.default.createElement(_materialTable.MTableToolbar, _extends({}, toolbarProps, { + classes: { + title: classes.tableTitle + } + }))); }, FilterRow: function FilterRow(filterRowProps) { return /*#__PURE__*/_react.default.createElement(_materialTable.MTableFilterRow, _extends({}, filterRowProps, { @@ -886,7 +1040,8 @@ var SiteMapTable = function SiteMapTable() { top: 0, backgroundColor: _Theme.default.palette.grey[50] }, - pageSizeOptions: [5, 10, 20, 50, 100], + pageSize: 100, + pageSizeOptions: [100, 200, 500], rowStyle: function rowStyle(row) { if (selectionActive) { if (!rowIsSelectable(row)) { @@ -915,26 +1070,35 @@ var SiteMapTable = function SiteMapTable() { } }; - if (fullHeight) { - tableOptions.pageSize = 50; - } else { + if (!fullHeight && view !== _SiteMapUtils.VIEWS.SPLIT) { tableOptions.maxBodyHeight = "".concat(maxBodyHeight || _SiteMapUtils.MIN_TABLE_MAX_BODY_HEIGHT, "px"); } + var containerClassName = "".concat(classes.tableContainer, " ").concat(classes.tableContainerIntegrated); + var containerStyle = {}; + + if (view === _SiteMapUtils.VIEWS.TABLE && fullHeight) { + containerStyle = { + position: 'relative' + }; + } + + if (view === _SiteMapUtils.VIEWS.SPLIT) { + containerClassName = "".concat(classes.tableContainer, " ").concat(classes.tableContainerStandalone); + } + return /*#__PURE__*/_react.default.createElement("div", { ref: tableRef, - className: classes.tableContainer, - style: fullHeight ? { - position: 'relative' - } : {}, + className: containerClassName, + style: containerStyle, "data-selenium": "sitemap-content-table" }, /*#__PURE__*/_react.default.createElement(_materialTable.default, { + title: "".concat(ucWord(focus), " in view"), icons: _MaterialTableIcons.default, components: components, columns: columns, data: rows, localization: localization, - title: null, options: tableOptions, onSelectionChange: !selectionActive ? null : function (newRows) { var action = { @@ -953,5 +1117,18 @@ var SiteMapTable = function SiteMapTable() { })); }; -var _default = SiteMapTable; -exports.default = _default; \ No newline at end of file +var _default = SiteMapTable; // Additional items exported for unit testing + +exports.default = _default; + +var getTestableItems = function getTestableItems() { + return process.env.NODE_ENV !== 'test' ? {} : { + ucWord: ucWord, + parseSearchTerms: parseSearchTerms, + searchOnAttribs: searchOnAttribs, + calculateMaxBodyHeight: calculateMaxBodyHeight, + getFeatureName: getFeatureName + }; +}; + +exports.getTestableItems = getTestableItems; \ No newline at end of file diff --git a/lib/components/SiteMap/SiteMapUtils.d.ts b/lib/components/SiteMap/SiteMapUtils.d.ts index e1667f8a..4bd23fd4 100644 --- a/lib/components/SiteMap/SiteMapUtils.d.ts +++ b/lib/components/SiteMap/SiteMapUtils.d.ts @@ -12,6 +12,9 @@ export namespace SITE_TERRAINS { export const AQUATIC: string; export const TERRESTRIAL: string; } +export namespace MANUAL_LOCATION_TYPES { + export const PROTOTYPE_SITE: string; +} export namespace FEATURE_TYPES { export namespace SITES { export const unit: string; @@ -72,6 +75,7 @@ export namespace FEATURE_DATA_SOURCES { export const GRAPHQL_LOCATIONS_API: string; export const ARCGIS_ASSETS_API: string; export const NEON_CONTEXT: string; + export const MANUAL_LOCATIONS: string; } export namespace SELECTION_PORTIONS { export const PARTIAL: string; @@ -90,6 +94,7 @@ export namespace HIGHLIGHT_STATUS { export namespace VIEWS { export const MAP: string; export const TABLE: string; + export const SPLIT: string; } export namespace MAP_MOUSE_MODES { export const PAN: string; @@ -1031,6 +1036,7 @@ export namespace FEATURES { iconSvg: any; iconSelectedSvg: any; iconShape: string; + maxZoom: number; }; export const TERRESTRIAL_RELOCATABLE_SITES: { name: string; @@ -1049,6 +1055,7 @@ export namespace FEATURES { iconSvg: any; iconSelectedSvg: any; iconShape: string; + maxZoom: number; }; export const AQUATIC_CORE_SITES: { name: string; @@ -1067,6 +1074,7 @@ export namespace FEATURES { iconSvg: any; iconSelectedSvg: any; iconShape: string; + maxZoom: number; }; export const AQUATIC_RELOCATABLE_SITES: { name: string; @@ -1085,6 +1093,26 @@ export namespace FEATURES { iconSvg: any; iconSelectedSvg: any; iconShape: string; + maxZoom: number; + }; + export const DECOMMISSIONED_SITES: { + name: string; + nameSingular: string; + type: any; + description: string; + parent: string; + attributes: { + type: string; + terrain: string; + }; + dataSource: string; + primaryIdOnly: boolean; + featureShape: string; + iconScale: number; + iconSvg: any; + iconSelectedSvg: any; + iconShape: string; + maxZoom: number; }; } export namespace GRAPHQL_LOCATIONS_API_CONSTANTS { @@ -1253,7 +1281,6 @@ export namespace DEFAULT_STATE { } } } -export function hydrateNeonContextData(state: any, neonContextData: any): any; export namespace SITE_MAP_PROP_TYPES { export const view: PropTypes.Requireable; export const aspectRatio: PropTypes.Requireable; @@ -1271,6 +1298,9 @@ export namespace SITE_MAP_PROP_TYPES { export const onSelectionChange: PropTypes.Requireable<(...args: any[]) => any>; export const search: PropTypes.Requireable; export const features: PropTypes.Requireable[]>; + export const manualLocationData: PropTypes.Requireable<(PropTypes.InferProps<{ + manualLocationType: PropTypes.Validator; + }> | null | undefined)[]>; } export namespace SITE_MAP_DEFAULT_PROPS { const view_1: string; @@ -1303,10 +1333,18 @@ export namespace SITE_MAP_DEFAULT_PROPS { export { search_1 as search }; const features_1: null; export { features_1 as features }; + const manualLocationData_1: null; + export { manualLocationData_1 as manualLocationData }; } +export function getZoomedIcon(featureKey?: any, zoom?: number, highlight?: string, selection?: string): any; export function getZoomedIcons(zoom: any): {}; +export function getPhantomLeafletMap(state: any): any; export function mapIsAtFocusLocation(state?: {}): boolean; export function getMapStateForFocusLocation(state?: {}): any; +export function findCentroid(coords?: any[]): any[] | null; +export function getMapStateForManualLocationData(state: any): any; +export function parseManualLocationFeatureData(state: any): any; +export function hydrateNeonContextData(state: any, neonContextData: any): any; export function getDynamicAspectRatio(unusableVerticalSpace?: number): number; export function boundsAreValid(bounds: any): boolean; export function calculateLocationsInBounds(locations: any, bounds?: any, extendMap?: boolean, extendPoints?: number): string[]; diff --git a/lib/components/SiteMap/SiteMapUtils.js b/lib/components/SiteMap/SiteMapUtils.js index 3f2c6719..b4201845 100644 --- a/lib/components/SiteMap/SiteMapUtils.js +++ b/lib/components/SiteMap/SiteMapUtils.js @@ -3,7 +3,7 @@ Object.defineProperty(exports, "__esModule", { value: true }); -exports.deriveFullObservatoryZoomLevel = exports.calculateLocationsInBounds = exports.boundsAreValid = exports.getDynamicAspectRatio = exports.getMapStateForFocusLocation = exports.mapIsAtFocusLocation = exports.getZoomedIcons = exports.SITE_MAP_DEFAULT_PROPS = exports.SITE_MAP_PROP_TYPES = exports.hydrateNeonContextData = exports.DEFAULT_STATE = exports.OVERLAYS = exports.OVERLAY_GROUPS_BY_TITLE = exports.OVERLAY_GROUPS = exports.BASE_LAYERS_BY_TITLE = exports.BASE_LAYERS = exports.getHref = exports.calculateFeatureAvailability = exports.BOUNDARY_COLORS = exports.GRAPHQL_LOCATIONS_API_CONSTANTS = exports.FEATURES = exports.LOCATION_ICON_SVG_SHAPES = exports.NLCD_CLASSES = exports.PLOT_SAMPLING_MODULES = exports.FETCH_STATUS = exports.MAP_MOUSE_MODES = exports.VIEWS = exports.HIGHLIGHT_STATUS = exports.UNSELECTABLE_MARKER_FILTER = exports.SELECTION_STATUS = exports.SELECTION_PORTIONS = exports.FEATURE_DATA_SOURCES = exports.FEATURE_TYPES = exports.SITE_TERRAINS = exports.SORT_DIRECTIONS = exports.SITE_LOCATION_HIERARCHIES_MIN_ZOOM = exports.KM2_TO_ACRES = exports.MIN_TABLE_MAX_BODY_HEIGHT = exports.MIN_CONTAINER_HEIGHT = exports.OBSERVATORY_CENTER = exports.MAP_ZOOM_RANGE = void 0; +exports.deriveFullObservatoryZoomLevel = exports.calculateLocationsInBounds = exports.boundsAreValid = exports.getDynamicAspectRatio = exports.hydrateNeonContextData = exports.parseManualLocationFeatureData = exports.getMapStateForManualLocationData = exports.findCentroid = exports.getMapStateForFocusLocation = exports.mapIsAtFocusLocation = exports.getPhantomLeafletMap = exports.getZoomedIcons = exports.getZoomedIcon = exports.SITE_MAP_DEFAULT_PROPS = exports.SITE_MAP_PROP_TYPES = exports.DEFAULT_STATE = exports.OVERLAYS = exports.OVERLAY_GROUPS_BY_TITLE = exports.OVERLAY_GROUPS = exports.BASE_LAYERS_BY_TITLE = exports.BASE_LAYERS = exports.getHref = exports.calculateFeatureAvailability = exports.BOUNDARY_COLORS = exports.GRAPHQL_LOCATIONS_API_CONSTANTS = exports.FEATURES = exports.LOCATION_ICON_SVG_SHAPES = exports.NLCD_CLASSES = exports.PLOT_SAMPLING_MODULES = exports.FETCH_STATUS = exports.MAP_MOUSE_MODES = exports.VIEWS = exports.HIGHLIGHT_STATUS = exports.UNSELECTABLE_MARKER_FILTER = exports.SELECTION_STATUS = exports.SELECTION_PORTIONS = exports.FEATURE_DATA_SOURCES = exports.FEATURE_TYPES = exports.MANUAL_LOCATION_TYPES = exports.SITE_TERRAINS = exports.SORT_DIRECTIONS = exports.SITE_LOCATION_HIERARCHIES_MIN_ZOOM = exports.KM2_TO_ACRES = exports.MIN_TABLE_MAX_BODY_HEIGHT = exports.MIN_CONTAINER_HEIGHT = exports.OBSERVATORY_CENTER = exports.MAP_ZOOM_RANGE = void 0; var _propTypes = _interopRequireDefault(require("prop-types")); @@ -55,6 +55,8 @@ var _iconSiteRelocatableAquatic = _interopRequireDefault(require("./svg/icon-sit var _iconSiteRelocatableAquaticSelected = _interopRequireDefault(require("./svg/icon-site-relocatable-aquatic-selected.svg")); +var _iconSiteDecommissioned = _interopRequireDefault(require("./svg/icon-site-decommissioned.svg")); + var _iconBenchmark = _interopRequireDefault(require("./svg/icon-benchmark.svg")); var _iconBuoy = _interopRequireDefault(require("./svg/icon-buoy.svg")); @@ -107,10 +109,12 @@ var _statesShapes = _interopRequireDefault(require("../../staticJSON/statesShape var _domainsShapes = _interopRequireDefault(require("../../staticJSON/domainsShapes.json")); -var _shadow, _shadow2, _shadow3, _shadow4, _availableFeatureType, _derived; +var _shadow, _shadow2, _shadow3, _shadow4, _initialized, _availableFeatureType, _derived; function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } +function _typeof(obj) { "@babel/helpers - typeof"; if (typeof Symbol === "function" && typeof Symbol.iterator === "symbol") { _typeof = function _typeof(obj) { return typeof obj; }; } else { _typeof = function _typeof(obj) { return obj && typeof Symbol === "function" && obj.constructor === Symbol && obj !== Symbol.prototype ? "symbol" : typeof obj; }; } return _typeof(obj); } + function _toConsumableArray(arr) { return _arrayWithoutHoles(arr) || _iterableToArray(arr) || _unsupportedIterableToArray(arr) || _nonIterableSpread(); } function _nonIterableSpread() { throw new TypeError("Invalid attempt to spread non-iterable instance.\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method."); } @@ -123,12 +127,20 @@ function _arrayWithoutHoles(arr) { if (Array.isArray(arr)) return _arrayLikeToAr function _arrayLikeToArray(arr, len) { if (len == null || len > arr.length) len = arr.length; for (var i = 0, arr2 = new Array(len); i < len; i++) { arr2[i] = arr[i]; } return arr2; } -function _typeof(obj) { "@babel/helpers - typeof"; if (typeof Symbol === "function" && typeof Symbol.iterator === "symbol") { _typeof = function _typeof(obj) { return typeof obj; }; } else { _typeof = function _typeof(obj) { return obj && typeof Symbol === "function" && obj.constructor === Symbol && obj !== Symbol.prototype ? "symbol" : typeof obj; }; } return _typeof(obj); } - function _extends() { _extends = Object.assign || function (target) { for (var i = 1; i < arguments.length; i++) { var source = arguments[i]; for (var key in source) { if (Object.prototype.hasOwnProperty.call(source, key)) { target[key] = source[key]; } } } return target; }; return _extends.apply(this, arguments); } function _defineProperty(obj, key, value) { if (key in obj) { Object.defineProperty(obj, key, { value: value, enumerable: true, configurable: true, writable: true }); } else { obj[key] = value; } return obj; } +var isCoord = function isCoord(c) { + return Array.isArray(c) && c.length === 2 && c.every(function (x) { + return Number.isFinite(x); + }); +}; + +var round = function round(x) { + return Number.parseFloat(x.toFixed(4), 10); +}; + var MAP_ZOOM_RANGE = [1, 19]; exports.MAP_ZOOM_RANGE = MAP_ZOOM_RANGE; var OBSERVATORY_CENTER = [52.68, -110.75]; @@ -161,13 +173,18 @@ exports.SORT_DIRECTIONS = SORT_DIRECTIONS; var SITE_TERRAINS = { AQUATIC: 'AQUATIC', TERRESTRIAL: 'TERRESTRIAL' +}; // For consistency in expressing the types of manual location data fed in through props + +exports.SITE_TERRAINS = SITE_TERRAINS; +var MANUAL_LOCATION_TYPES = { + PROTOTYPE_SITE: 'PROTOTYPE_SITE' }; // For consistency in differentiating discrete sets of data that can be tabulated together. // e.g. all LOCATIONS type feature data can coexist in a single table view with a // single column definition. But LOCATIONS and SITES shouldn't, as each set has // different common attributes that should map to table columns (yes, sites are locations too, // but we represent and interact with them differently) -exports.SITE_TERRAINS = SITE_TERRAINS; +exports.MANUAL_LOCATION_TYPES = MANUAL_LOCATION_TYPES; var FEATURE_TYPES = { SITES: { unit: 'site', @@ -220,7 +237,9 @@ var FEATURE_DATA_SOURCES = { REST_LOCATIONS_API: 'REST_LOCATIONS_API', GRAPHQL_LOCATIONS_API: 'GRAPHQL_LOCATIONS_API', ARCGIS_ASSETS_API: 'ARCGIS_ASSETS_API', - NEON_CONTEXT: 'NEON_CONTEXT' + NEON_CONTEXT: 'NEON_CONTEXT', + MANUAL_LOCATIONS: 'MANUAL_LOCATIONS' // data injected by manualLocationData prop + }; exports.FEATURE_DATA_SOURCES = FEATURE_DATA_SOURCES; var SELECTED_ICON_OFFSET = 30; // Number of pixels bigger in one dimension for selected icons @@ -250,7 +269,8 @@ var HIGHLIGHT_STATUS = { exports.HIGHLIGHT_STATUS = HIGHLIGHT_STATUS; var VIEWS = { MAP: 'MAP', - TABLE: 'TABLE' + TABLE: 'TABLE', + SPLIT: 'SPLIT' }; // For consistency in denoting exclusive available mouse behaviors on the map exports.VIEWS = VIEWS; @@ -1138,7 +1158,8 @@ var FEATURES = { iconScale: 1, iconSvg: _iconSiteCoreTerrestrial.default, iconSelectedSvg: _iconSiteCoreTerrestrialSelected.default, - iconShape: LOCATION_ICON_SVG_SHAPES.SQUARE.KEY + iconShape: LOCATION_ICON_SVG_SHAPES.SQUARE.KEY, + maxZoom: 9 }, TERRESTRIAL_RELOCATABLE_SITES: { name: 'Terrestrial Relocatable Sites', @@ -1156,7 +1177,8 @@ var FEATURES = { iconScale: 1, iconSvg: _iconSiteRelocatableTerrestrial.default, iconSelectedSvg: _iconSiteRelocatableTerrestrialSelected.default, - iconShape: LOCATION_ICON_SVG_SHAPES.CIRCLE.KEY + iconShape: LOCATION_ICON_SVG_SHAPES.CIRCLE.KEY, + maxZoom: 9 }, AQUATIC_CORE_SITES: { name: 'Aquatic Core Sites', @@ -1174,7 +1196,8 @@ var FEATURES = { iconScale: 1, iconSvg: _iconSiteCoreAquatic.default, iconSelectedSvg: _iconSiteCoreAquaticSelected.default, - iconShape: LOCATION_ICON_SVG_SHAPES.SQUARE.KEY + iconShape: LOCATION_ICON_SVG_SHAPES.SQUARE.KEY, + maxZoom: 9 }, AQUATIC_RELOCATABLE_SITES: { name: 'Aquatic Relocatable Sites', @@ -1192,7 +1215,27 @@ var FEATURES = { iconScale: 1, iconSvg: _iconSiteRelocatableAquatic.default, iconSelectedSvg: _iconSiteRelocatableAquaticSelected.default, - iconShape: LOCATION_ICON_SVG_SHAPES.CIRCLE.KEY + iconShape: LOCATION_ICON_SVG_SHAPES.CIRCLE.KEY, + maxZoom: 9 + }, + DECOMMISSIONED_SITES: { + name: 'Decommissioned Sites', + nameSingular: 'Decommissioned Site', + type: FEATURE_TYPES.SITES.KEY, + description: 'No longer active in observatory', + parent: 'SITE_MARKERS', + attributes: { + type: 'DECOMMISSIONED', + terrain: 'DECOMMISSIONED' + }, + dataSource: FEATURE_DATA_SOURCES.MANUAL_LOCATIONS, + primaryIdOnly: true, + featureShape: 'Marker', + iconScale: 1, + iconSvg: _iconSiteDecommissioned.default, + iconSelectedSvg: _iconSiteDecommissioned.default, + iconShape: LOCATION_ICON_SVG_SHAPES.CIRCLE.KEY, + maxZoom: 19 } }; // Replicate keys as attributes to completely eliminate the need to write a feature key string @@ -1243,7 +1286,16 @@ exports.BOUNDARY_COLORS = BOUNDARY_COLORS; var calculateFeatureAvailability = function calculateFeatureAvailability(state) { var featureIsAvailable = function featureIsAvailable(feature) { - // Parent must be available (if the feature has a parent) + // Special case: show SITES group at all zoom levels if DECOMMISSIONED_SITES are also shown + var hasDecomissionedSites = !!(state.manualLocationData || []).filter(function (manualLocation) { + return manualLocation.manualLocationType === MANUAL_LOCATION_TYPES.PROTOTYPE_SITE && !state.sites[manualLocation.siteCode]; + }).length; + + if (feature.KEY === 'SITE_MARKERS' && hasDecomissionedSites) { + return true; + } // Parent must be available (if the feature has a parent) + + if (typeof feature.parent === 'string' && !featureIsAvailable(FEATURES[feature.parent])) { return false; } @@ -1538,9 +1590,7 @@ var featureIsHiddenByDefault = function featureIsHiddenByDefault(key) { var DEFAULT_STATE = { view: { current: null, - initialized: Object.fromEntries(Object.keys(VIEWS).map(function (view) { - return [view, false]; - })) + initialized: (_initialized = {}, _defineProperty(_initialized, VIEWS.MAP, false), _defineProperty(_initialized, VIEWS.TABLE, false), _initialized) }, neonContextHydrated: false, // Whether NeonContext data has been one-time hydrated into state @@ -1618,7 +1668,9 @@ var DEFAULT_STATE = { featureDataFetchesHasAwaiting: false, // Boolean: track whether any data fetches are awaiting call featureDataFetches: Object.fromEntries(Object.keys(FEATURE_DATA_SOURCES).filter(function (dataSource) { - return dataSource !== FEATURE_DATA_SOURCES.NEON_CONTEXT; + return (// eslint-disable-next-line max-len + ![FEATURE_DATA_SOURCES.MANUAL_LOCATIONS, FEATURE_DATA_SOURCES.NEON_CONTEXT].includes(dataSource) + ); }).map(function (dataSource) { return [dataSource, {}]; })), @@ -1647,7 +1699,8 @@ var DEFAULT_STATE = { } }, - fullscreen: false + fullscreen: false, + manualLocationData: null }; // Initialize featureData and featureDataFetches objects for all features that have a dataSource exports.DEFAULT_STATE = DEFAULT_STATE; @@ -1718,47 +1771,11 @@ if (_domainsShapes.default) { }; }); } - -var hydrateNeonContextData = function hydrateNeonContextData(state, neonContextData) { - var newState = _extends({}, state, { - neonContextHydrated: true - }); // Sites - - - Object.keys(neonContextData.sites).forEach(function (siteCode) { - newState.sites[siteCode] = _extends({}, neonContextData.sites[siteCode]); - var featureKey = Object.keys(FEATURES).filter(function (key) { - return FEATURES[key].type === FEATURE_TYPES.SITES.KEY; - }).find(function (key) { - return FEATURES[key].attributes.type === neonContextData.sites[siteCode].type && FEATURES[key].attributes.terrain === neonContextData.sites[siteCode].terrain; - }) || null; - - if (featureKey !== null) { - // eslint-disable-next-line max-len - newState.featureData[FEATURE_TYPES.SITES.KEY][featureKey][siteCode] = newState.sites[siteCode]; - } - }); // States - - Object.keys(neonContextData.states).forEach(function (stateCode) { - newState.featureData[FEATURE_TYPES.STATES.KEY][FEATURES.STATES.KEY][stateCode] = _extends({}, newState.featureData[FEATURE_TYPES.STATES.KEY][FEATURES.STATES.KEY][stateCode] || {}, neonContextData.states[stateCode], { - sites: neonContextData.stateSites[stateCode] - }); - }); // Domains - - Object.keys(neonContextData.domains).forEach(function (domainCode) { - newState.featureData[FEATURE_TYPES.DOMAINS.KEY][FEATURES.DOMAINS.KEY][domainCode] = _extends({}, newState.featureData[FEATURE_TYPES.DOMAINS.KEY][FEATURES.DOMAINS.KEY][domainCode] || {}, neonContextData.domains[domainCode], { - sites: neonContextData.domainSites[domainCode] - }); - }); - return newState; -}; /** PropTypes and defaultProps */ -exports.hydrateNeonContextData = hydrateNeonContextData; - var SelectionLimitPropType = function SelectionLimitPropType(props, propName) { var prop = props[propName]; @@ -1815,7 +1832,11 @@ var SITE_MAP_PROP_TYPES = { onSelectionChange: _propTypes.default.func, // Filter props search: _propTypes.default.string, - features: _propTypes.default.arrayOf(_propTypes.default.oneOf(Object.keys(FEATURES))) + features: _propTypes.default.arrayOf(_propTypes.default.oneOf(Object.keys(FEATURES))), + // Manual Location Data + manualLocationData: _propTypes.default.arrayOf(_propTypes.default.shape({ + manualLocationType: _propTypes.default.oneOf(Object.keys(MANUAL_LOCATION_TYPES)).isRequired + })) }; exports.SITE_MAP_PROP_TYPES = SITE_MAP_PROP_TYPES; var SITE_MAP_DEFAULT_PROPS = { @@ -1840,7 +1861,9 @@ var SITE_MAP_DEFAULT_PROPS = { onSelectionChange: function onSelectionChange() {}, // Filter props search: null, - features: null + features: null, + // Manual Location Data + manualLocationData: null }; /** Icon Utility Functions @@ -1904,28 +1927,28 @@ var getZoomedIcon = function getZoomedIcon() { var minScale = 0.2; var maxScale = Math.max((maxZoom - minZoom) / (MAP_ZOOM_RANGE[1] - MAP_ZOOM_RANGE[0]), 0.5); var baseScale = ((zoom || minZoom) - minZoom) / (maxZoom - minZoom); - var scale = (minScale + baseScale * (maxScale - minScale)) * iconScale; + var scale = Math.max((minScale + baseScale * (maxScale - minScale)) * iconScale, minScale); var iconProps = { iconUrl: iconUrl, iconRetinaUrl: iconUrl, iconSize: iconSize.map(function (x) { - return x * scale; + return round(x * scale); }), iconAnchor: iconAnchor.map(function (x) { - return x * scale; + return round(x * scale); }), popupAnchor: popupAnchor.map(function (x) { - return x * scale; + return round(x * scale); }) }; if (shadowUrl && shadowSize && shadowAnchor) { iconProps.shadowUrl = shadowUrl; iconProps.shadowSize = shadowSize.map(function (x) { - return x * scale; + return round(x * scale); }); iconProps.shadowAnchor = shadowAnchor.map(function (x) { - return x * scale; + return round(x * scale); }); } @@ -1935,6 +1958,8 @@ var getZoomedIcon = function getZoomedIcon() { // a new icon instance for every discrete location in view when several share the same icon. +exports.getZoomedIcon = getZoomedIcon; + var getZoomedIcons = function getZoomedIcons(zoom) { var featureTypes = [FEATURE_TYPES.LOCATIONS.KEY, FEATURE_TYPES.SITES.KEY, FEATURE_TYPES.OTHER.KEY]; var icons = {}; @@ -1963,22 +1988,30 @@ exports.getZoomedIcons = getZoomedIcons; var getPhantomLeafletMap = function getPhantomLeafletMap(state) { var _state$aspectRatio = state.aspectRatio, aspectRatio = _state$aspectRatio.currentValue, - widthReference = _state$aspectRatio.widthReference; - - _leaflet.default.Map.include({ - getSize: function getSize() { - return new _leaflet.default.Point(widthReference, widthReference * aspectRatio); + widthReference = _state$aspectRatio.widthReference, + _state$map = state.map, + center = _state$map.center, + zoom = _state$map.zoom; + var x = widthReference || 400; + var y = widthReference * aspectRatio || 300; + + var PhantomMapClass = _leaflet.default.Map.extend({ + includes: { + getSize: function getSize() { + return new _leaflet.default.Point(x, y); + } } }); var element = document.createElement('div'); - var map = new _leaflet.default.Map(element, { - center: state.map.center, - zoom: state.map.zoom + return new PhantomMapClass(element, { + center: center, + zoom: zoom }); - return map; }; +exports.getPhantomLeafletMap = getPhantomLeafletMap; + var mapIsAtFocusLocation = function mapIsAtFocusLocation() { var state = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {}; return state.map && state.map.zoom && Array.isArray(state.map.center) && state.map.center.length === 2 && state.focusLocation && state.focusLocation.current && state.focusLocation.map.zoom && Array.isArray(state.focusLocation.map.center) && state.focusLocation.map.center.length === 2 && state.map.zoom === state.focusLocation.map.zoom && state.map.center[0] === state.focusLocation.map.center[0] && state.map.center[1] === state.focusLocation.map.center[1] || false; @@ -1991,7 +2024,7 @@ var getMapStateForFocusLocation = function getMapStateForFocusLocation() { var focusLocation = state.focusLocation; if (!focusLocation || !focusLocation.current) { - return state; + return state.map; } var current = focusLocation.current; @@ -2000,18 +2033,17 @@ var getMapStateForFocusLocation = function getMapStateForFocusLocation() { _ref3$type = _ref3.type, type = _ref3$type === void 0 ? '' : _ref3$type, latitude = _ref3.latitude, - longitude = _ref3.longitude; + longitude = _ref3.longitude; // No latitude/longitude: previous map state - var newState = _extends({}, state); - - newState.map.bounds = null; - newState.map.zoom = null; // No latitude/longitude: return all defaults if (!Number.isFinite(latitude) || !Number.isFinite(longitude)) { - newState.map.center = SITE_MAP_DEFAULT_PROPS.mapCenter; - return newState; - } // Everything else (valid location with a center) + return state.map; + } + var newState = _extends({}, state); + + newState.map.bounds = null; + newState.map.zoom = null; // Everything else (valid location with a center) newState.map.center = [latitude, longitude]; newState.map.bounds = null; @@ -2048,22 +2080,199 @@ var getMapStateForFocusLocation = function getMapStateForFocusLocation() { var newBounds = phantomMap.getBounds() || null; newState.map.bounds = !newBounds ? null : { /* eslint-disable no-underscore-dangle */ - lat: [newBounds._southWest.lat, newBounds._northEast.lat], - lng: [newBounds._southWest.lng, newBounds._northEast.lng] + lat: [round(newBounds._southWest.lat), round(newBounds._northEast.lat)], + lng: [round(newBounds._southWest.lng), round(newBounds._northEast.lng)] + /* eslint-enable no-underscore-dangle */ + + }; + phantomMap.remove(); + } // Done + + + return newState.map; +}; + +exports.getMapStateForFocusLocation = getMapStateForFocusLocation; + +var findCentroid = function findCentroid() { + var coords = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : []; + + if (!Array.isArray(coords) || !coords.length || !coords.every(isCoord)) { + return null; + } + + if (coords.length === 1) { + return _toConsumableArray(coords[0]); + } + + var c = { + x: 0, + y: 0, + z: 0 + }; + coords.forEach(function (coord) { + var rLat = coord[0] * (Math.PI / 180); + var rLng = coord[1] * (Math.PI / 180); + c.x += Math.cos(rLat) * Math.cos(rLng); + c.y += Math.cos(rLat) * Math.sin(rLng); + c.z += Math.sin(rLat); + }); + c.x /= coords.length; + c.y /= coords.length; + c.z /= coords.length; + var cLng = Math.atan2(c.y, c.x); + var cHyp = Math.sqrt(Math.pow(c.x, 2) + Math.pow(c.y, 2)); + var cLat = Math.atan2(c.z, cHyp); + return [round(cLat * (180 / Math.PI)), round(cLng * (180 / Math.PI))]; +}; + +exports.findCentroid = findCentroid; + +var getMapStateForManualLocationData = function getMapStateForManualLocationData(state) { + var previousMapState = state.map, + manualLocationData = state.manualLocationData; + + if (!Array.isArray(manualLocationData) || !manualLocationData.length) { + return previousMapState; + } + + var newState = _extends({}, state); + + newState.map.zoom = null; + newState.map.bounds = null; + newState.map.center = SITE_MAP_DEFAULT_PROPS.mapCenter; + var bounds = [[null, null], [null, null]]; + var locationCenters = manualLocationData.reduce(function (acc, cur) { + if (Number.isFinite(cur.latitude) && Number.isFinite(cur.longitude)) { + acc.push([cur.latitude, cur.longitude]); + + if (bounds[0][0] === null || cur.latitude < bounds[0][0]) { + bounds[0][0] = cur.latitude; + } + + if (bounds[0][1] === null || cur.longitude < bounds[0][1]) { + bounds[0][1] = cur.longitude; + } + + if (bounds[1][0] === null || cur.latitude > bounds[1][0]) { + bounds[1][0] = cur.latitude; + } + + if (bounds[1][1] === null || cur.longitude > bounds[1][1]) { + bounds[1][1] = cur.longitude; + } + } + + return acc; + }, []); + + if (!locationCenters.length) { + return previousMapState; + } + + if (locationCenters.length === 1) { + newState.map.center = _toConsumableArray(locationCenters[0]); + newState.map.zoom = 6; + } else { + newState.map.center = findCentroid(locationCenters); + var phantomZoomMap = getPhantomLeafletMap(newState); + phantomZoomMap.fitBounds(bounds, { + animate: false, + padding: [50, 50] + }); + newState.map.zoom = phantomZoomMap.getZoom(); + phantomZoomMap.remove(); + } // Regenerate icons and bounds if we have a valid zoom + + + if (newState.map.zoom !== null) { + newState.map.zoomedIcons = getZoomedIcons(newState.map.zoom); + var phantomMap = getPhantomLeafletMap(newState); + var newBounds = phantomMap.getBounds() || null; + newState.map.bounds = !newBounds ? null : { + /* eslint-disable no-underscore-dangle */ + lat: [round(newBounds._southWest.lat), round(newBounds._northEast.lat)], + lng: [round(newBounds._southWest.lng), round(newBounds._northEast.lng)] /* eslint-enable no-underscore-dangle */ }; + phantomMap.remove(); } // Done return newState.map; +}; // Parse any manualLocationData into the mainfeatureData object so that its entries can be +// rendered consistently with other features + + +exports.getMapStateForManualLocationData = getMapStateForManualLocationData; + +var parseManualLocationFeatureData = function parseManualLocationFeatureData(state) { + if (!state.neonContextHydrated || !Array.isArray(state.manualLocationData) || !state.manualLocationData.length) { + return state; + } + + var newState = _extends({}, state); + + state.manualLocationData.forEach(function (manualLocation) { + var siteCode = manualLocation.siteCode; + + if (manualLocation.manualLocationType === MANUAL_LOCATION_TYPES.PROTOTYPE_SITE && siteCode && !newState.sites[siteCode]) { + var featureType = FEATURE_TYPES.SITES.KEY; + var featureKey = FEATURES.DECOMMISSIONED_SITES.KEY; + newState.featureData[featureType][featureKey][siteCode] = manualLocation; // Harmonize some values + + newState.featureData[featureType][featureKey][siteCode].type = 'Decommissioned'; + newState.featureData[featureType][featureKey][siteCode].stateCode = manualLocation.state; + newState.featureData[featureType][featureKey][siteCode].domainCode = manualLocation.domain; + newState.featureData[featureType][featureKey][siteCode].description = manualLocation.siteName; + } + }); + newState.map = getMapStateForManualLocationData(newState); + return newState; +}; + +exports.parseManualLocationFeatureData = parseManualLocationFeatureData; + +var hydrateNeonContextData = function hydrateNeonContextData(state, neonContextData) { + var newState = _extends({}, state, { + neonContextHydrated: true + }); // Sites + + + Object.keys(neonContextData.sites).forEach(function (siteCode) { + newState.sites[siteCode] = _extends({}, neonContextData.sites[siteCode]); + var featureKey = Object.keys(FEATURES).filter(function (key) { + return FEATURES[key].type === FEATURE_TYPES.SITES.KEY; + }).find(function (key) { + return FEATURES[key].attributes.type === neonContextData.sites[siteCode].type && FEATURES[key].attributes.terrain === neonContextData.sites[siteCode].terrain; + }) || null; + + if (featureKey !== null) { + // eslint-disable-next-line max-len + newState.featureData[FEATURE_TYPES.SITES.KEY][featureKey][siteCode] = newState.sites[siteCode]; + } + }); // States + + Object.keys(neonContextData.states).forEach(function (stateCode) { + newState.featureData[FEATURE_TYPES.STATES.KEY][FEATURES.STATES.KEY][stateCode] = _extends({}, newState.featureData[FEATURE_TYPES.STATES.KEY][FEATURES.STATES.KEY][stateCode] || {}, neonContextData.states[stateCode], { + sites: neonContextData.stateSites[stateCode] + }); + }); // Domains + + Object.keys(neonContextData.domains).forEach(function (domainCode) { + newState.featureData[FEATURE_TYPES.DOMAINS.KEY][FEATURES.DOMAINS.KEY][domainCode] = _extends({}, newState.featureData[FEATURE_TYPES.DOMAINS.KEY][FEATURES.DOMAINS.KEY][domainCode] || {}, neonContextData.domains[domainCode], { + sites: neonContextData.domainSites[domainCode] + }); + }); + return parseManualLocationFeatureData(newState); }; /** Aspect Ratio */ -exports.getMapStateForFocusLocation = getMapStateForFocusLocation; +exports.hydrateNeonContextData = hydrateNeonContextData; var dynamicAspectRatios = ['1:2', '9:16', '2:3', '5:7', '4:5', '1:1', '5:4', '7:5', '3:2', '16:9', '2:1', '2.5:1', '3:1'].map(function (ratio) { var parts = /^([\d.]+):([\d.]+)$/.exec(ratio) || ['', '1', '1']; return parseFloat(parts[2]) / parseFloat(parts[1]); @@ -2118,12 +2327,6 @@ var calculateLocationsInBounds = function calculateLocationsInBounds(locations) // is in the map. NOTE: extendPoints does not work with boundaries, only solitary points. var flatten = function flatten(items) { - var isCoord = function isCoord(c) { - return Array.isArray(c) && c.length === 2 && c.every(function (x) { - return Number.isFinite(x); - }); - }; - var flat = []; items.forEach(function (item) { if (Array.isArray(item) && !isCoord(item)) { diff --git a/lib/components/SiteMap/svg/icon-site-decommissioned.svg b/lib/components/SiteMap/svg/icon-site-decommissioned.svg new file mode 100644 index 00000000..3abecdf2 --- /dev/null +++ b/lib/components/SiteMap/svg/icon-site-decommissioned.svg @@ -0,0 +1,94 @@ + + + + + + + + image/svg+xml + + + + + + + + + + + + + diff --git a/lib/components/Theme/Theme.js b/lib/components/Theme/Theme.js index 0df8995a..af2396ee 100644 --- a/lib/components/Theme/Theme.js +++ b/lib/components/Theme/Theme.js @@ -310,7 +310,7 @@ var baseTheme = (0, _styles2.createMuiTheme)({ }, MuiBackdrop: { root: { - zIndex: 3 + zIndex: 50 } }, MuiBreadcrumbs: { @@ -462,7 +462,7 @@ var baseTheme = (0, _styles2.createMuiTheme)({ zIndex: 105 }, paper: { - zIndex: 3 + zIndex: 10 } }, MuiFormControlLabel: { diff --git a/lib/components/TimeSeriesViewer/TimeSeriesViewerAxes.js b/lib/components/TimeSeriesViewer/TimeSeriesViewerAxes.js index 2ea54376..de4a11b1 100644 --- a/lib/components/TimeSeriesViewer/TimeSeriesViewerAxes.js +++ b/lib/components/TimeSeriesViewer/TimeSeriesViewerAxes.js @@ -70,8 +70,8 @@ var useStyles = (0, _styles.makeStyles)(function (theme) { flexWrap: 'wrap' }, smallButton: { - fontSize: '0.8rem', - padding: theme.spacing(0.125, 0.75), + fontSize: '0.55rem', + padding: theme.spacing(0.25, 0.75), whiteSpace: 'nowrap' }, smallButtonIcon: { diff --git a/lib/components/TimeSeriesViewer/TimeSeriesViewerContainer.js b/lib/components/TimeSeriesViewer/TimeSeriesViewerContainer.js index 68925960..068a869c 100644 --- a/lib/components/TimeSeriesViewer/TimeSeriesViewerContainer.js +++ b/lib/components/TimeSeriesViewer/TimeSeriesViewerContainer.js @@ -134,7 +134,11 @@ var useStyles = (0, _styles.makeStyles)(function (theme) { zIndex: 10, margin: theme.spacing(-0.5, -0.5, 0, -0.5), padding: theme.spacing(20, 4, 4, 4), - backgroundColor: 'rgba(255, 255, 255, 0.75)' + backgroundColor: 'rgba(255, 255, 255, 0.75)', + '& .MuiTypography-root': { + boxShadow: '0px 0px 20px 25px #fff', + backgroundColor: '#fff' + } }, titleContainer: { marginBottom: theme.spacing(2) @@ -158,6 +162,12 @@ var useStyles = (0, _styles.makeStyles)(function (theme) { fontWeight: 700, fontSize: '85%', marginRight: _Theme.default.spacing(1) + }, + errorIcon: { + color: _Theme.default.colors.RED[400] + }, + warningIcon: { + color: _Theme.default.colors.GOLD[500] } }; }); @@ -560,9 +570,10 @@ function TimeSeriesViewerContainer() { var renderGraphOverlay = function renderGraphOverlay() { var isError = state.status === _TimeSeriesViewerContext.TIME_SERIES_VIEWER_STATUS.ERROR; + var isWarning = state.status === _TimeSeriesViewerContext.TIME_SERIES_VIEWER_STATUS.WARNING; var isLoading = !isError && state.status !== _TimeSeriesViewerContext.TIME_SERIES_VIEWER_STATUS.READY; - if (isError) { + if (isError || isWarning) { return /*#__PURE__*/_react.default.createElement("div", { className: classes.graphOverlay }, /*#__PURE__*/_react.default.createElement(_Typography.default, { @@ -572,7 +583,7 @@ function TimeSeriesViewerContainer() { } }, state.displayError || 'An unknown error occurred; unable to visualize data product'), /*#__PURE__*/_react.default.createElement(_Error.default, { fontSize: "large", - color: "error" + className: classes[isError ? 'errorIcon' : 'warningIcon'] })); } diff --git a/lib/components/TimeSeriesViewer/TimeSeriesViewerContext.d.ts b/lib/components/TimeSeriesViewer/TimeSeriesViewerContext.d.ts index 71e054de..a7455adf 100644 --- a/lib/components/TimeSeriesViewer/TimeSeriesViewerContext.d.ts +++ b/lib/components/TimeSeriesViewer/TimeSeriesViewerContext.d.ts @@ -4,6 +4,7 @@ export namespace TIME_SERIES_VIEWER_STATUS { export const READY_FOR_DATA: string; export const LOADING_DATA: string; export const ERROR: string; + export const WARNING: string; export const READY_FOR_SERIES: string; export const READY: string; } @@ -163,6 +164,8 @@ export function getTestableItems(): { y1: any; y2: any; }; + isDefault: boolean; + invalidDefaultVariables: Set; }; availableQualityFlags: Set; availableTimeSteps: Set; diff --git a/lib/components/TimeSeriesViewer/TimeSeriesViewerContext.js b/lib/components/TimeSeriesViewer/TimeSeriesViewerContext.js index 5b13c0d6..1e2952f1 100644 --- a/lib/components/TimeSeriesViewer/TimeSeriesViewerContext.js +++ b/lib/components/TimeSeriesViewer/TimeSeriesViewerContext.js @@ -86,7 +86,9 @@ var TIME_SERIES_VIEWER_STATUS = { LOADING_DATA: 'LOADING_DATA', // Actively loading plottable series data ERROR: 'ERROR', - // Stop everything because problem + // Stop everything because problem, do not trigger new fetches no matter what + WARNING: 'WARNING', + // Current selection/data makes a graph not possible; show warning READY_FOR_SERIES: 'READY_FOR_SERIES', // Ready to re-calculate series data for the graph READY: 'READY' // Ready for user input @@ -143,8 +145,9 @@ var generateYAxisRange = function generateYAxisRange() { return axisRange; } - var low = (dataRange[0] || 0) - standardDeviation; - var high = (dataRange[1] || 0) + standardDeviation; + var margin = standardDeviation !== 0 ? standardDeviation : dataRange[0] / 2; + var low = (dataRange[0] || 0) - margin; + var high = (dataRange[1] || 0) + margin; low = parseFloat(low.toFixed(precision), 10); high = parseFloat(high.toFixed(precision), 10); @@ -226,7 +229,10 @@ var DEFAULT_STATE = { yAxes: { y1: (0, _cloneDeep.default)(DEFAULT_AXIS_STATE), y2: (0, _cloneDeep.default)(DEFAULT_AXIS_STATE) - } + }, + isDefault: true, + invalidDefaultVariables: new Set() // canBeDefault vars found to have no series data after load + }, availableQualityFlags: new Set(), availableTimeSteps: new Set(['auto']) @@ -734,7 +740,8 @@ var parseSitePositions = function parseSitePositions(site, csv) { var applyDefaultsToSelection = function applyDefaultsToSelection(state) { - var product = state.product, + var status = state.status, + product = state.product, variables = state.variables, selection = state.selection; @@ -780,7 +787,7 @@ var applyDefaultsToSelection = function applyDefaultsToSelection(state) { // Ensure the selection has at least one variable if (!selection.variables.length) { var defaultVar = Object.keys(variables).find(function (v) { - return variables[v].canBeDefault; + return variables[v].canBeDefault && !selection.invalidDefaultVariables.has(v); }); if (defaultVar) { @@ -879,7 +886,26 @@ var applyDefaultsToSelection = function applyDefaultsToSelection(state) { selection.yAxes[yAxis].standardDeviation = fixedStandardDeviation; selection.yAxes[yAxis].axisRange = generateYAxisRange(selection.yAxes[yAxis]); } - }); // Generate a new digest for effect comparison + }); // Edge case: if the default site/month/position/variable produces a series with no data then + // it wasn't a good default, but we had no way of knowing until the data series was fully parsed. + // Here we check for this scenario and if we're in it then add the variable to the + // invalidDefaultVariables set, remove the variable from the selection, and run again. + // We'll recurse through the variables available for the site/month/position until we find one + // that works or show a meaningful error instructing the user to select a different site, + // month, or position. Note that as soon as the user makes an active selection of any kind + // isDefault will be false for the lifetime of the time series viewer instance, so this automated + // removal of a selected variable can only happen before any user selection happens. + + if (status === TIME_SERIES_VIEWER_STATUS.READY_FOR_SERIES && selection.isDefault && selection.variables.length && selection.yAxes.y1.dataRange.every(function (x) { + return x === null; + })) { + selection.invalidDefaultVariables.add(selection.variables[0]); + selection.variables = []; + return applyDefaultsToSelection(_extends({}, state, { + selection: selection + })); + } // Generate a new digest for effect comparison + selection.digest = JSON.stringify({ sites: selection.sites, @@ -955,6 +981,12 @@ var reducer = function reducer(state, action) { } }; + var softFail = function softFail(error) { + newState.status = TIME_SERIES_VIEWER_STATUS.WARNING; + newState.displayError = error; + return newState; + }; + var fail = function fail(error) { newState.status = TIME_SERIES_VIEWER_STATUS.ERROR; newState.displayError = error; @@ -1100,6 +1132,14 @@ var reducer = function reducer(state, action) { // Regenerate Graph Data Actions case 'regenerateGraphData': + if (!action.graphData.series.length || Object.keys(state.selection.yAxes).every(function (y) { + return state.selection.yAxes[y].dataRange.every(function (x) { + return x === null; + }); + })) { + return softFail('Current selection of dates/sites/positions/variables does not have any valid numeric data.'); + } + newState.graphData = action.graphData; newState.status = TIME_SERIES_VIEWER_STATUS.READY; return newState; @@ -1158,6 +1198,11 @@ var reducer = function reducer(state, action) { delete newState.dataFetches[action.token]; newState.status = TIME_SERIES_VIEWER_STATUS.READY_FOR_SERIES; calcSelection(); + + if (!newState.selection.variables.length) { + return softFail('None of the variables for this product\'s default site/month/position have data. Please select a different site, month, or position.'); + } + return newState; case 'noDataFilesFetchNecessary': @@ -1177,6 +1222,7 @@ var reducer = function reducer(state, action) { // Core Selection Actions case 'selectDateRange': + newState.selection.isDefault = false; newState.selection.dateRange = action.dateRange; newState.selection.activelySelectingDateRange = action.dateRange; calcSelection(); @@ -1184,6 +1230,7 @@ var reducer = function reducer(state, action) { return newState; case 'selectVariables': + newState.selection.isDefault = false; parsedContent = limitVariablesToTwoUnits(state, action.variables); newState.selection.variables = parsedContent.variables; /* eslint-disable prefer-destructuring */ @@ -1220,6 +1267,7 @@ var reducer = function reducer(state, action) { return state; } + newState.selection.isDefault = false; newState.selection.yAxes[action.axis].rangeMode = action.mode; if (action.mode !== Y_AXIS_RANGE_MODES.CUSTOM) { @@ -1239,6 +1287,7 @@ var reducer = function reducer(state, action) { return state; } + newState.selection.isDefault = false; newState.selection.yAxes[action.axis].axisRange = action.range; return newState; @@ -1253,10 +1302,13 @@ var reducer = function reducer(state, action) { // Option Selection Actions case 'selectLogScale': + newState.selection.isDefault = false; newState.selection.logscale = !!action.logscale; return newState; case 'selectSwapYAxes': + newState.selection.isDefault = false; + if (state.selection.yAxes.y2.units === null) { return state; } @@ -1269,19 +1321,24 @@ var reducer = function reducer(state, action) { return newState; case 'setRollPeriod': + newState.selection.isDefault = false; newState.selection.rollPeriod = action.rollPeriod; return newState; case 'selectAllQualityFlags': + newState.selection.isDefault = false; newState.selection.qualityFlags = Array.from(state.availableQualityFlags); calcStatus(); return newState; case 'selectNoneQualityFlags': + newState.selection.isDefault = false; newState.selection.qualityFlags = []; return newState; case 'selectToggleQualityFlag': + newState.selection.isDefault = false; + if (action.selected && !state.selection.qualityFlags.includes(action.qualityFlag)) { newState.selection.qualityFlags.push(action.qualityFlag); } else if (!action.selected) { @@ -1294,6 +1351,8 @@ var reducer = function reducer(state, action) { return newState; case 'selectTimeStep': + newState.selection.isDefault = false; + if (!state.availableTimeSteps.has(action.timeStep)) { return state; } @@ -1303,6 +1362,8 @@ var reducer = function reducer(state, action) { return newState; case 'selectAddSite': + newState.selection.isDefault = false; + if (!state.product.sites[action.siteCode]) { return state; } @@ -1324,8 +1385,9 @@ var reducer = function reducer(state, action) { case 'updateSelectedSites': if (!action.siteCodes || !action.siteCodes.constructor || action.siteCodes.constructor.name !== 'Set' || !action.siteCodes.size) { return state; - } // Remove any sites that are no longer in the selected set + } + newState.selection.isDefault = false; // Remove any sites that are no longer in the selected set newState.selection.sites = newState.selection.sites.filter(function (site) { return action.siteCodes.has(site.siteCode); @@ -1362,6 +1424,7 @@ var reducer = function reducer(state, action) { return state; } + newState.selection.isDefault = false; newState.selection.sites = newState.selection.sites.filter(function (site) { return site.siteCode !== action.siteCode; }); @@ -1388,6 +1451,7 @@ var reducer = function reducer(state, action) { return state; } + newState.selection.isDefault = false; newState.selection.sites[selectedSiteIdx].positions = _toConsumableArray(action.positions); calcSelection(); calcStatus(); diff --git a/lib/remoteAssets/drupal-header.html.d.ts b/lib/remoteAssets/drupal-header.html.d.ts index b6347bb6..57cbc631 100644 --- a/lib/remoteAssets/drupal-header.html.d.ts +++ b/lib/remoteAssets/drupal-header.html.d.ts @@ -1,2 +1,2 @@ -declare var _default: "\n
\n
\n \n \n \n
\n
\n
\n \n \n \n\n\n \n\n
\n
\n \n
\n
\n

Search

\n \n\n
\n \n
\n \n\n \n
\n
\n\n\n
\n\n
\n\n\n\n\n
\n \n\n
\n
\n
\n
\n
\n\n\n"; +declare var _default: "\n
\n
\n \n \n \n
\n
\n
\n \n \n \n\n\n \n\n
\n
\n \n
\n
\n

Search

\n \n\n
\n \n
\n \n\n \n
\n
\n\n\n
\n\n
\n\n\n\n\n
\n \n\n
\n
\n
\n
\n
\n\n\n"; export default _default; diff --git a/lib/remoteAssets/drupal-header.html.js b/lib/remoteAssets/drupal-header.html.js index 1208dc2d..739cf631 100644 --- a/lib/remoteAssets/drupal-header.html.js +++ b/lib/remoteAssets/drupal-header.html.js @@ -6,6 +6,6 @@ Object.defineProperty(exports, "__esModule", { exports.default = void 0; var html; -var _default = html = "\n
\n
\n \n \n \n
\n
\n
\n \n \n \n\n\n \n\n
\n
\n \n
\n
\n

Search

\n \n\n
\n \n
\n \n\n \n
\n
\n\n\n
\n\n
\n\n\n\n\n
\n \n\n
\n
\n
\n
\n
\n\n\n"; +var _default = html = "\n
\n
\n \n \n \n
\n
\n
\n \n \n \n\n\n \n\n
\n
\n \n
\n
\n

Search

\n \n\n
\n \n
\n \n\n \n
\n
\n\n\n
\n\n
\n\n\n\n\n
\n \n\n
\n
\n
\n
\n
\n\n\n"; exports.default = _default; \ No newline at end of file diff --git a/lib/remoteAssets/drupal-theme.css b/lib/remoteAssets/drupal-theme.css index 464b3351..f72916ba 100644 --- a/lib/remoteAssets/drupal-theme.css +++ b/lib/remoteAssets/drupal-theme.css @@ -427,6 +427,8 @@ text-decoration: none; } #header .jumpmenu__heading a:visited.is-active, #footer .jumpmenu__heading a:visited.is-active { color: #565A5C; } + #header .ui-autocomplete, #footer .ui-autocomplete { + z-index: 9999; } #header .layout, #footer .layout { padding: 2rem 7.40741%; } @media (min-width: 75em) { @@ -1112,6 +1114,8 @@ @media (min-width: 37.5em) { #header .webform-element-description, #footer .webform-element-description { font-size: 0.75rem; } } + #header #linkit-editor-dialog-form, #footer #linkit-editor-dialog-form { + min-height: 400px; } #header .table-wrapper, #footer .table-wrapper { display: -ms-grid; display: grid; diff --git a/lib/staticJSON/sites.json b/lib/staticJSON/sites.json index 95f2071b..92522e65 100644 --- a/lib/staticJSON/sites.json +++ b/lib/staticJSON/sites.json @@ -20,7 +20,7 @@ "zoom": 15 }, "BARC": { - "description": "Barco Lake", + "description": "Ordway-Swisher Biological Station - Barco Lake", "type": "CORE", "stateCode": "FL", "domainCode": "D03", @@ -30,7 +30,7 @@ "zoom": 16 }, "BARR": { - "description": "Utqiaġvik Environmental Observatory", + "description": "Utqiaġvik", "type": "RELOCATABLE", "stateCode": "AK", "domainCode": "D18", @@ -90,7 +90,7 @@ "zoom": 15 }, "BLWA": { - "description": "Black Warrior River", + "description": "Black Warrior River near Dead Lake", "type": "RELOCATABLE", "stateCode": "AL", "domainCode": "D08", @@ -110,7 +110,7 @@ "zoom": 15 }, "CARI": { - "description": "Caribou Creek at Caribou-Poker Creeks Research Watershed", + "description": "Caribou Creek, Caribou-Poker Creeks Research Watershed", "type": "CORE", "stateCode": "AK", "domainCode": "D19", @@ -220,7 +220,7 @@ "zoom": 15 }, "GRSM": { - "description": "Great Smoky Mountains National Park", + "description": "Great Smoky Mountains National Park, Twin Creeks", "type": "RELOCATABLE", "stateCode": "TN", "domainCode": "D07", @@ -270,7 +270,7 @@ "zoom": 12 }, "HOPB": { - "description": "Hop Brook", + "description": "Lower Hop Brook", "type": "CORE", "stateCode": "MA", "domainCode": "D01", @@ -310,7 +310,7 @@ "zoom": 15 }, "KONA": { - "description": "Konza Prairie Biological Station", + "description": "Konza Prairie Biological Station - Relocatable", "type": "RELOCATABLE", "stateCode": "KS", "domainCode": "D06", @@ -550,7 +550,7 @@ "zoom": 15 }, "PUUM": { - "description": "Pu-u Maka-ala Natural Area Reserve", + "description": "Pu'u Maka'ala Natural Area Reserve", "type": "CORE", "stateCode": "HI", "domainCode": "D20", @@ -570,7 +570,7 @@ "zoom": 15 }, "RMNP": { - "description": "Rocky Mountain National Park", + "description": "Rocky Mountain National Park, CASTNET", "type": "RELOCATABLE", "stateCode": "CO", "domainCode": "D10", @@ -600,7 +600,7 @@ "zoom": 12 }, "SJER": { - "description": "San Joaquin", + "description": "San Joaquin Experimental Range", "type": "CORE", "stateCode": "CA", "domainCode": "D17", @@ -640,7 +640,7 @@ "zoom": 12 }, "STER": { - "description": "Sterling", + "description": "North Sterling, CO", "type": "RELOCATABLE", "stateCode": "CO", "domainCode": "D10", @@ -650,7 +650,7 @@ "zoom": 12 }, "SUGG": { - "description": "Suggs Lake", + "description": "Ordway-Swisher Biological Station - Suggs Lake", "type": "CORE", "stateCode": "FL", "domainCode": "D03", @@ -700,7 +700,7 @@ "zoom": 16 }, "TOMB": { - "description": "Tombigbee River", + "description": "Lower Tombigbee River at Choctaw Refuge", "type": "RELOCATABLE", "stateCode": "AL", "domainCode": "D08", @@ -800,7 +800,7 @@ "zoom": 12 }, "YELL": { - "description": "Yellowstone National Park", + "description": "Yellowstone Nothern Range (Frog Rock)", "type": "CORE", "stateCode": "WY", "domainCode": "D12", diff --git a/lib/workers/parseTimeSeriesData.js b/lib/workers/parseTimeSeriesData.js index 17931291..61b4e085 100644 --- a/lib/workers/parseTimeSeriesData.js +++ b/lib/workers/parseTimeSeriesData.js @@ -145,6 +145,11 @@ function parseTimeSeriesData() { return; } + if (Number.isFinite(series[fieldName].range[0]) && series[fieldName].range[0] === series[fieldName].range[1]) { + series[fieldName].variance = 0; + return; + } + var mean = series[fieldName].sum / series[fieldName].count; var sumOfSquares = 0; series[fieldName].data.forEach(function (value) { diff --git a/package-lock.json b/package-lock.json index d63ad860..178617c6 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,13 +1,13 @@ { "name": "portal-core-components", - "version": "1.6.1", + "version": "1.6.3", "lockfileVersion": 1, "requires": true, "dependencies": { "@babel/cli": { - "version": "7.12.10", - "resolved": "https://registry.npmjs.org/@babel/cli/-/cli-7.12.10.tgz", - "integrity": "sha512-+y4ZnePpvWs1fc/LhZRTHkTesbXkyBYuOB+5CyodZqrEuETXi3zOVfpAQIdgC3lXbHLTDG9dQosxR9BhvLKDLQ==", + "version": "7.13.0", + "resolved": "https://registry.npmjs.org/@babel/cli/-/cli-7.13.0.tgz", + "integrity": "sha512-y5AohgeVhU+wO5kU1WGMLdocFj83xCxVjsVFa2ilII8NEwmBZvx7Ambq621FbFIK68loYJ9p43nfoi6es+rzSA==", "dev": true, "requires": { "@nicolo-ribaudo/chokidar-2": "2.1.8-no-fsevents", @@ -23,39 +23,40 @@ } }, "@babel/code-frame": { - "version": "7.12.11", - "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.12.11.tgz", - "integrity": "sha512-Zt1yodBx1UcyiePMSkWnU4hPqhwq7hGi2nFL1LeA3EUl+q2LQx16MISgJ0+z7dnmgvP9QtIleuETGOiOH1RcIw==", + "version": "7.12.13", + "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.12.13.tgz", + "integrity": "sha512-HV1Cm0Q3ZrpCR93tkWOYiuYIgLxZXZFVG2VgK+MBWjUqZTundupbfx2aXarXuw5Ko5aMcjtJgbSs4vUGBS5v6g==", "requires": { - "@babel/highlight": "^7.10.4" + "@babel/highlight": "^7.12.13" } }, "@babel/compat-data": { - "version": "7.12.7", - "resolved": "https://registry.npmjs.org/@babel/compat-data/-/compat-data-7.12.7.tgz", - "integrity": "sha512-YaxPMGs/XIWtYqrdEOZOCPsVWfEoriXopnsz3/i7apYPXQ3698UFhS6dVT1KN5qOsWmVgw/FOrmQgpRaZayGsw==", + "version": "7.13.8", + "resolved": "https://registry.npmjs.org/@babel/compat-data/-/compat-data-7.13.8.tgz", + "integrity": "sha512-EaI33z19T4qN3xLXsGf48M2cDqa6ei9tPZlfLdb2HC+e/cFtREiRd8hdSqDbwdLB0/+gLwqJmCYASH0z2bUdog==", "dev": true }, "@babel/core": { - "version": "7.12.10", - "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.12.10.tgz", - "integrity": "sha512-eTAlQKq65zHfkHZV0sIVODCPGVgoo1HdBlbSLi9CqOzuZanMv2ihzY+4paiKr1mH+XmYESMAmJ/dpZ68eN6d8w==", - "dev": true, - "requires": { - "@babel/code-frame": "^7.10.4", - "@babel/generator": "^7.12.10", - "@babel/helper-module-transforms": "^7.12.1", - "@babel/helpers": "^7.12.5", - "@babel/parser": "^7.12.10", - "@babel/template": "^7.12.7", - "@babel/traverse": "^7.12.10", - "@babel/types": "^7.12.10", + "version": "7.13.8", + "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.13.8.tgz", + "integrity": "sha512-oYapIySGw1zGhEFRd6lzWNLWFX2s5dA/jm+Pw/+59ZdXtjyIuwlXbrId22Md0rgZVop+aVoqow2riXhBLNyuQg==", + "dev": true, + "requires": { + "@babel/code-frame": "^7.12.13", + "@babel/generator": "^7.13.0", + "@babel/helper-compilation-targets": "^7.13.8", + "@babel/helper-module-transforms": "^7.13.0", + "@babel/helpers": "^7.13.0", + "@babel/parser": "^7.13.4", + "@babel/template": "^7.12.13", + "@babel/traverse": "^7.13.0", + "@babel/types": "^7.13.0", "convert-source-map": "^1.7.0", "debug": "^4.1.0", - "gensync": "^1.0.0-beta.1", + "gensync": "^1.0.0-beta.2", "json5": "^2.1.2", "lodash": "^4.17.19", - "semver": "^5.4.1", + "semver": "^6.3.0", "source-map": "^0.5.0" }, "dependencies": { @@ -73,202 +74,245 @@ "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", "dev": true + }, + "semver": { + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", + "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==", + "dev": true } } }, "@babel/generator": { - "version": "7.12.11", - "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.12.11.tgz", - "integrity": "sha512-Ggg6WPOJtSi8yYQvLVjG8F/TlpWDlKx0OpS4Kt+xMQPs5OaGYWy+v1A+1TvxI6sAMGZpKWWoAQ1DaeQbImlItA==", + "version": "7.13.9", + "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.13.9.tgz", + "integrity": "sha512-mHOOmY0Axl/JCTkxTU6Lf5sWOg/v8nUa+Xkt4zMTftX0wqmb6Sh7J8gvcehBw7q0AhrhAR+FDacKjCZ2X8K+Sw==", "dev": true, "requires": { - "@babel/types": "^7.12.11", + "@babel/types": "^7.13.0", "jsesc": "^2.5.1", "source-map": "^0.5.0" } }, "@babel/helper-annotate-as-pure": { - "version": "7.12.10", - "resolved": "https://registry.npmjs.org/@babel/helper-annotate-as-pure/-/helper-annotate-as-pure-7.12.10.tgz", - "integrity": "sha512-XplmVbC1n+KY6jL8/fgLVXXUauDIB+lD5+GsQEh6F6GBF1dq1qy4DP4yXWzDKcoqXB3X58t61e85Fitoww4JVQ==", + "version": "7.12.13", + "resolved": "https://registry.npmjs.org/@babel/helper-annotate-as-pure/-/helper-annotate-as-pure-7.12.13.tgz", + "integrity": "sha512-7YXfX5wQ5aYM/BOlbSccHDbuXXFPxeoUmfWtz8le2yTkTZc+BxsiEnENFoi2SlmA8ewDkG2LgIMIVzzn2h8kfw==", "dev": true, "requires": { - "@babel/types": "^7.12.10" + "@babel/types": "^7.12.13" } }, "@babel/helper-builder-binary-assignment-operator-visitor": { - "version": "7.10.4", - "resolved": "https://registry.npmjs.org/@babel/helper-builder-binary-assignment-operator-visitor/-/helper-builder-binary-assignment-operator-visitor-7.10.4.tgz", - "integrity": "sha512-L0zGlFrGWZK4PbT8AszSfLTM5sDU1+Az/En9VrdT8/LmEiJt4zXt+Jve9DCAnQcbqDhCI+29y/L93mrDzddCcg==", + "version": "7.12.13", + "resolved": "https://registry.npmjs.org/@babel/helper-builder-binary-assignment-operator-visitor/-/helper-builder-binary-assignment-operator-visitor-7.12.13.tgz", + "integrity": "sha512-CZOv9tGphhDRlVjVkAgm8Nhklm9RzSmWpX2my+t7Ua/KT616pEzXsQCjinzvkRvHWJ9itO4f296efroX23XCMA==", "dev": true, "requires": { - "@babel/helper-explode-assignable-expression": "^7.10.4", - "@babel/types": "^7.10.4" + "@babel/helper-explode-assignable-expression": "^7.12.13", + "@babel/types": "^7.12.13" } }, "@babel/helper-compilation-targets": { - "version": "7.12.5", - "resolved": "https://registry.npmjs.org/@babel/helper-compilation-targets/-/helper-compilation-targets-7.12.5.tgz", - "integrity": "sha512-+qH6NrscMolUlzOYngSBMIOQpKUGPPsc61Bu5W10mg84LxZ7cmvnBHzARKbDoFxVvqqAbj6Tg6N7bSrWSPXMyw==", + "version": "7.13.8", + "resolved": "https://registry.npmjs.org/@babel/helper-compilation-targets/-/helper-compilation-targets-7.13.8.tgz", + "integrity": "sha512-pBljUGC1y3xKLn1nrx2eAhurLMA8OqBtBP/JwG4U8skN7kf8/aqwwxpV1N6T0e7r6+7uNitIa/fUxPFagSXp3A==", "dev": true, "requires": { - "@babel/compat-data": "^7.12.5", - "@babel/helper-validator-option": "^7.12.1", + "@babel/compat-data": "^7.13.8", + "@babel/helper-validator-option": "^7.12.17", "browserslist": "^4.14.5", - "semver": "^5.5.0" + "semver": "^6.3.0" + }, + "dependencies": { + "semver": { + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", + "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==", + "dev": true + } } }, "@babel/helper-create-class-features-plugin": { - "version": "7.12.1", - "resolved": "https://registry.npmjs.org/@babel/helper-create-class-features-plugin/-/helper-create-class-features-plugin-7.12.1.tgz", - "integrity": "sha512-hkL++rWeta/OVOBTRJc9a5Azh5mt5WgZUGAKMD8JM141YsE08K//bp1unBBieO6rUKkIPyUE0USQ30jAy3Sk1w==", + "version": "7.13.8", + "resolved": "https://registry.npmjs.org/@babel/helper-create-class-features-plugin/-/helper-create-class-features-plugin-7.13.8.tgz", + "integrity": "sha512-qioaRrKHQbn4hkRKDHbnuQ6kAxmmOF+kzKGnIfxPK4j2rckSJCpKzr/SSTlohSCiE3uAQpNDJ9FIh4baeE8W+w==", "dev": true, "requires": { - "@babel/helper-function-name": "^7.10.4", - "@babel/helper-member-expression-to-functions": "^7.12.1", - "@babel/helper-optimise-call-expression": "^7.10.4", - "@babel/helper-replace-supers": "^7.12.1", - "@babel/helper-split-export-declaration": "^7.10.4" + "@babel/helper-function-name": "^7.12.13", + "@babel/helper-member-expression-to-functions": "^7.13.0", + "@babel/helper-optimise-call-expression": "^7.12.13", + "@babel/helper-replace-supers": "^7.13.0", + "@babel/helper-split-export-declaration": "^7.12.13" } }, "@babel/helper-create-regexp-features-plugin": { - "version": "7.12.7", - "resolved": "https://registry.npmjs.org/@babel/helper-create-regexp-features-plugin/-/helper-create-regexp-features-plugin-7.12.7.tgz", - "integrity": "sha512-idnutvQPdpbduutvi3JVfEgcVIHooQnhvhx0Nk9isOINOIGYkZea1Pk2JlJRiUnMefrlvr0vkByATBY/mB4vjQ==", + "version": "7.12.17", + "resolved": "https://registry.npmjs.org/@babel/helper-create-regexp-features-plugin/-/helper-create-regexp-features-plugin-7.12.17.tgz", + "integrity": "sha512-p2VGmBu9oefLZ2nQpgnEnG0ZlRPvL8gAGvPUMQwUdaE8k49rOMuZpOwdQoy5qJf6K8jL3bcAMhVUlHAjIgJHUg==", "dev": true, "requires": { - "@babel/helper-annotate-as-pure": "^7.10.4", + "@babel/helper-annotate-as-pure": "^7.12.13", "regexpu-core": "^4.7.1" } }, - "@babel/helper-define-map": { - "version": "7.10.5", - "resolved": "https://registry.npmjs.org/@babel/helper-define-map/-/helper-define-map-7.10.5.tgz", - "integrity": "sha512-fMw4kgFB720aQFXSVaXr79pjjcW5puTCM16+rECJ/plGS+zByelE8l9nCpV1GibxTnFVmUuYG9U8wYfQHdzOEQ==", + "@babel/helper-define-polyfill-provider": { + "version": "0.1.5", + "resolved": "https://registry.npmjs.org/@babel/helper-define-polyfill-provider/-/helper-define-polyfill-provider-0.1.5.tgz", + "integrity": "sha512-nXuzCSwlJ/WKr8qxzW816gwyT6VZgiJG17zR40fou70yfAcqjoNyTLl/DQ+FExw5Hx5KNqshmN8Ldl/r2N7cTg==", "dev": true, "requires": { - "@babel/helper-function-name": "^7.10.4", - "@babel/types": "^7.10.5", - "lodash": "^4.17.19" + "@babel/helper-compilation-targets": "^7.13.0", + "@babel/helper-module-imports": "^7.12.13", + "@babel/helper-plugin-utils": "^7.13.0", + "@babel/traverse": "^7.13.0", + "debug": "^4.1.1", + "lodash.debounce": "^4.0.8", + "resolve": "^1.14.2", + "semver": "^6.1.2" + }, + "dependencies": { + "debug": { + "version": "4.3.1", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.1.tgz", + "integrity": "sha512-doEwdvm4PCeK4K3RQN2ZC2BYUBaxwLARCqZmMjtF8a51J2Rb0xpVloFRnCODwqjpwnAoao4pelN8l3RJdv3gRQ==", + "dev": true, + "requires": { + "ms": "2.1.2" + } + }, + "ms": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", + "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", + "dev": true + }, + "semver": { + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", + "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==", + "dev": true + } } }, "@babel/helper-explode-assignable-expression": { - "version": "7.12.1", - "resolved": "https://registry.npmjs.org/@babel/helper-explode-assignable-expression/-/helper-explode-assignable-expression-7.12.1.tgz", - "integrity": "sha512-dmUwH8XmlrUpVqgtZ737tK88v07l840z9j3OEhCLwKTkjlvKpfqXVIZ0wpK3aeOxspwGrf/5AP5qLx4rO3w5rA==", + "version": "7.13.0", + "resolved": "https://registry.npmjs.org/@babel/helper-explode-assignable-expression/-/helper-explode-assignable-expression-7.13.0.tgz", + "integrity": "sha512-qS0peLTDP8kOisG1blKbaoBg/o9OSa1qoumMjTK5pM+KDTtpxpsiubnCGP34vK8BXGcb2M9eigwgvoJryrzwWA==", "dev": true, "requires": { - "@babel/types": "^7.12.1" + "@babel/types": "^7.13.0" } }, "@babel/helper-function-name": { - "version": "7.12.11", - "resolved": "https://registry.npmjs.org/@babel/helper-function-name/-/helper-function-name-7.12.11.tgz", - "integrity": "sha512-AtQKjtYNolKNi6nNNVLQ27CP6D9oFR6bq/HPYSizlzbp7uC1M59XJe8L+0uXjbIaZaUJF99ruHqVGiKXU/7ybA==", + "version": "7.12.13", + "resolved": "https://registry.npmjs.org/@babel/helper-function-name/-/helper-function-name-7.12.13.tgz", + "integrity": "sha512-TZvmPn0UOqmvi5G4vvw0qZTpVptGkB1GL61R6lKvrSdIxGm5Pky7Q3fpKiIkQCAtRCBUwB0PaThlx9vebCDSwA==", "dev": true, "requires": { - "@babel/helper-get-function-arity": "^7.12.10", - "@babel/template": "^7.12.7", - "@babel/types": "^7.12.11" + "@babel/helper-get-function-arity": "^7.12.13", + "@babel/template": "^7.12.13", + "@babel/types": "^7.12.13" } }, "@babel/helper-get-function-arity": { - "version": "7.12.10", - "resolved": "https://registry.npmjs.org/@babel/helper-get-function-arity/-/helper-get-function-arity-7.12.10.tgz", - "integrity": "sha512-mm0n5BPjR06wh9mPQaDdXWDoll/j5UpCAPl1x8fS71GHm7HA6Ua2V4ylG1Ju8lvcTOietbPNNPaSilKj+pj+Ag==", + "version": "7.12.13", + "resolved": "https://registry.npmjs.org/@babel/helper-get-function-arity/-/helper-get-function-arity-7.12.13.tgz", + "integrity": "sha512-DjEVzQNz5LICkzN0REdpD5prGoidvbdYk1BVgRUOINaWJP2t6avB27X1guXK1kXNrX0WMfsrm1A/ZBthYuIMQg==", "dev": true, "requires": { - "@babel/types": "^7.12.10" + "@babel/types": "^7.12.13" } }, "@babel/helper-hoist-variables": { - "version": "7.10.4", - "resolved": "https://registry.npmjs.org/@babel/helper-hoist-variables/-/helper-hoist-variables-7.10.4.tgz", - "integrity": "sha512-wljroF5PgCk2juF69kanHVs6vrLwIPNp6DLD+Lrl3hoQ3PpPPikaDRNFA+0t81NOoMt2DL6WW/mdU8k4k6ZzuA==", + "version": "7.13.0", + "resolved": "https://registry.npmjs.org/@babel/helper-hoist-variables/-/helper-hoist-variables-7.13.0.tgz", + "integrity": "sha512-0kBzvXiIKfsCA0y6cFEIJf4OdzfpRuNk4+YTeHZpGGc666SATFKTz6sRncwFnQk7/ugJ4dSrCj6iJuvW4Qwr2g==", "dev": true, "requires": { - "@babel/types": "^7.10.4" + "@babel/traverse": "^7.13.0", + "@babel/types": "^7.13.0" } }, "@babel/helper-member-expression-to-functions": { - "version": "7.12.7", - "resolved": "https://registry.npmjs.org/@babel/helper-member-expression-to-functions/-/helper-member-expression-to-functions-7.12.7.tgz", - "integrity": "sha512-DCsuPyeWxeHgh1Dus7APn7iza42i/qXqiFPWyBDdOFtvS581JQePsc1F/nD+fHrcswhLlRc2UpYS1NwERxZhHw==", + "version": "7.13.0", + "resolved": "https://registry.npmjs.org/@babel/helper-member-expression-to-functions/-/helper-member-expression-to-functions-7.13.0.tgz", + "integrity": "sha512-yvRf8Ivk62JwisqV1rFRMxiSMDGnN6KH1/mDMmIrij4jztpQNRoHqqMG3U6apYbGRPJpgPalhva9Yd06HlUxJQ==", "dev": true, "requires": { - "@babel/types": "^7.12.7" + "@babel/types": "^7.13.0" } }, "@babel/helper-module-imports": { - "version": "7.12.5", - "resolved": "https://registry.npmjs.org/@babel/helper-module-imports/-/helper-module-imports-7.12.5.tgz", - "integrity": "sha512-SR713Ogqg6++uexFRORf/+nPXMmWIn80TALu0uaFb+iQIUoR7bOC7zBWyzBs5b3tBBJXuyD0cRu1F15GyzjOWA==", + "version": "7.12.13", + "resolved": "https://registry.npmjs.org/@babel/helper-module-imports/-/helper-module-imports-7.12.13.tgz", + "integrity": "sha512-NGmfvRp9Rqxy0uHSSVP+SRIW1q31a7Ji10cLBcqSDUngGentY4FRiHOFZFE1CLU5eiL0oE8reH7Tg1y99TDM/g==", "requires": { - "@babel/types": "^7.12.5" + "@babel/types": "^7.12.13" } }, "@babel/helper-module-transforms": { - "version": "7.12.1", - "resolved": "https://registry.npmjs.org/@babel/helper-module-transforms/-/helper-module-transforms-7.12.1.tgz", - "integrity": "sha512-QQzehgFAZ2bbISiCpmVGfiGux8YVFXQ0abBic2Envhej22DVXV9nCFaS5hIQbkyo1AdGb+gNME2TSh3hYJVV/w==", - "dev": true, - "requires": { - "@babel/helper-module-imports": "^7.12.1", - "@babel/helper-replace-supers": "^7.12.1", - "@babel/helper-simple-access": "^7.12.1", - "@babel/helper-split-export-declaration": "^7.11.0", - "@babel/helper-validator-identifier": "^7.10.4", - "@babel/template": "^7.10.4", - "@babel/traverse": "^7.12.1", - "@babel/types": "^7.12.1", + "version": "7.13.0", + "resolved": "https://registry.npmjs.org/@babel/helper-module-transforms/-/helper-module-transforms-7.13.0.tgz", + "integrity": "sha512-Ls8/VBwH577+pw7Ku1QkUWIyRRNHpYlts7+qSqBBFCW3I8QteB9DxfcZ5YJpOwH6Ihe/wn8ch7fMGOP1OhEIvw==", + "dev": true, + "requires": { + "@babel/helper-module-imports": "^7.12.13", + "@babel/helper-replace-supers": "^7.13.0", + "@babel/helper-simple-access": "^7.12.13", + "@babel/helper-split-export-declaration": "^7.12.13", + "@babel/helper-validator-identifier": "^7.12.11", + "@babel/template": "^7.12.13", + "@babel/traverse": "^7.13.0", + "@babel/types": "^7.13.0", "lodash": "^4.17.19" } }, "@babel/helper-optimise-call-expression": { - "version": "7.12.10", - "resolved": "https://registry.npmjs.org/@babel/helper-optimise-call-expression/-/helper-optimise-call-expression-7.12.10.tgz", - "integrity": "sha512-4tpbU0SrSTjjt65UMWSrUOPZTsgvPgGG4S8QSTNHacKzpS51IVWGDj0yCwyeZND/i+LSN2g/O63jEXEWm49sYQ==", + "version": "7.12.13", + "resolved": "https://registry.npmjs.org/@babel/helper-optimise-call-expression/-/helper-optimise-call-expression-7.12.13.tgz", + "integrity": "sha512-BdWQhoVJkp6nVjB7nkFWcn43dkprYauqtk++Py2eaf/GRDFm5BxRqEIZCiHlZUGAVmtwKcsVL1dC68WmzeFmiA==", "dev": true, "requires": { - "@babel/types": "^7.12.10" + "@babel/types": "^7.12.13" } }, "@babel/helper-plugin-utils": { - "version": "7.10.4", - "resolved": "https://registry.npmjs.org/@babel/helper-plugin-utils/-/helper-plugin-utils-7.10.4.tgz", - "integrity": "sha512-O4KCvQA6lLiMU9l2eawBPMf1xPP8xPfB3iEQw150hOVTqj/rfXz0ThTb4HEzqQfs2Bmo5Ay8BzxfzVtBrr9dVg==", + "version": "7.13.0", + "resolved": "https://registry.npmjs.org/@babel/helper-plugin-utils/-/helper-plugin-utils-7.13.0.tgz", + "integrity": "sha512-ZPafIPSwzUlAoWT8DKs1W2VyF2gOWthGd5NGFMsBcMMol+ZhK+EQY/e6V96poa6PA/Bh+C9plWN0hXO1uB8AfQ==", "dev": true }, "@babel/helper-remap-async-to-generator": { - "version": "7.12.1", - "resolved": "https://registry.npmjs.org/@babel/helper-remap-async-to-generator/-/helper-remap-async-to-generator-7.12.1.tgz", - "integrity": "sha512-9d0KQCRM8clMPcDwo8SevNs+/9a8yWVVmaE80FGJcEP8N1qToREmWEGnBn8BUlJhYRFz6fqxeRL1sl5Ogsed7A==", + "version": "7.13.0", + "resolved": "https://registry.npmjs.org/@babel/helper-remap-async-to-generator/-/helper-remap-async-to-generator-7.13.0.tgz", + "integrity": "sha512-pUQpFBE9JvC9lrQbpX0TmeNIy5s7GnZjna2lhhcHC7DzgBs6fWn722Y5cfwgrtrqc7NAJwMvOa0mKhq6XaE4jg==", "dev": true, "requires": { - "@babel/helper-annotate-as-pure": "^7.10.4", - "@babel/helper-wrap-function": "^7.10.4", - "@babel/types": "^7.12.1" + "@babel/helper-annotate-as-pure": "^7.12.13", + "@babel/helper-wrap-function": "^7.13.0", + "@babel/types": "^7.13.0" } }, "@babel/helper-replace-supers": { - "version": "7.12.11", - "resolved": "https://registry.npmjs.org/@babel/helper-replace-supers/-/helper-replace-supers-7.12.11.tgz", - "integrity": "sha512-q+w1cqmhL7R0FNzth/PLLp2N+scXEK/L2AHbXUyydxp828F4FEa5WcVoqui9vFRiHDQErj9Zof8azP32uGVTRA==", + "version": "7.13.0", + "resolved": "https://registry.npmjs.org/@babel/helper-replace-supers/-/helper-replace-supers-7.13.0.tgz", + "integrity": "sha512-Segd5me1+Pz+rmN/NFBOplMbZG3SqRJOBlY+mA0SxAv6rjj7zJqr1AVr3SfzUVTLCv7ZLU5FycOM/SBGuLPbZw==", "dev": true, "requires": { - "@babel/helper-member-expression-to-functions": "^7.12.7", - "@babel/helper-optimise-call-expression": "^7.12.10", - "@babel/traverse": "^7.12.10", - "@babel/types": "^7.12.11" + "@babel/helper-member-expression-to-functions": "^7.13.0", + "@babel/helper-optimise-call-expression": "^7.12.13", + "@babel/traverse": "^7.13.0", + "@babel/types": "^7.13.0" } }, "@babel/helper-simple-access": { - "version": "7.12.1", - "resolved": "https://registry.npmjs.org/@babel/helper-simple-access/-/helper-simple-access-7.12.1.tgz", - "integrity": "sha512-OxBp7pMrjVewSSC8fXDFrHrBcJATOOFssZwv16F3/6Xtc138GHybBfPbm9kfiqQHKhYQrlamWILwlDCeyMFEaA==", + "version": "7.12.13", + "resolved": "https://registry.npmjs.org/@babel/helper-simple-access/-/helper-simple-access-7.12.13.tgz", + "integrity": "sha512-0ski5dyYIHEfwpWGx5GPWhH35j342JaflmCeQmsPWcrOQDtCN6C1zKAVRFVbK53lPW2c9TsuLLSUDf0tIGJ5hA==", "dev": true, "requires": { - "@babel/types": "^7.12.1" + "@babel/types": "^7.12.13" } }, "@babel/helper-skip-transparent-expression-wrappers": { @@ -281,12 +325,12 @@ } }, "@babel/helper-split-export-declaration": { - "version": "7.12.11", - "resolved": "https://registry.npmjs.org/@babel/helper-split-export-declaration/-/helper-split-export-declaration-7.12.11.tgz", - "integrity": "sha512-LsIVN8j48gHgwzfocYUSkO/hjYAOJqlpJEc7tGXcIm4cubjVUf8LGW6eWRyxEu7gA25q02p0rQUWoCI33HNS5g==", + "version": "7.12.13", + "resolved": "https://registry.npmjs.org/@babel/helper-split-export-declaration/-/helper-split-export-declaration-7.12.13.tgz", + "integrity": "sha512-tCJDltF83htUtXx5NLcaDqRmknv652ZWCHyoTETf1CXYJdPC7nohZohjUgieXhv0hTJdRf2FjDueFehdNucpzg==", "dev": true, "requires": { - "@babel/types": "^7.12.11" + "@babel/types": "^7.12.13" } }, "@babel/helper-validator-identifier": { @@ -295,69 +339,69 @@ "integrity": "sha512-np/lG3uARFybkoHokJUmf1QfEvRVCPbmQeUQpKow5cQ3xWrV9i3rUHodKDJPQfTVX61qKi+UdYk8kik84n7XOw==" }, "@babel/helper-validator-option": { - "version": "7.12.11", - "resolved": "https://registry.npmjs.org/@babel/helper-validator-option/-/helper-validator-option-7.12.11.tgz", - "integrity": "sha512-TBFCyj939mFSdeX7U7DDj32WtzYY7fDcalgq8v3fBZMNOJQNn7nOYzMaUCiPxPYfCup69mtIpqlKgMZLvQ8Xhw==", + "version": "7.12.17", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-option/-/helper-validator-option-7.12.17.tgz", + "integrity": "sha512-TopkMDmLzq8ngChwRlyjR6raKD6gMSae4JdYDB8bByKreQgG0RBTuKe9LRxW3wFtUnjxOPRKBDwEH6Mg5KeDfw==", "dev": true }, "@babel/helper-wrap-function": { - "version": "7.12.3", - "resolved": "https://registry.npmjs.org/@babel/helper-wrap-function/-/helper-wrap-function-7.12.3.tgz", - "integrity": "sha512-Cvb8IuJDln3rs6tzjW3Y8UeelAOdnpB8xtQ4sme2MSZ9wOxrbThporC0y/EtE16VAtoyEfLM404Xr1e0OOp+ow==", + "version": "7.13.0", + "resolved": "https://registry.npmjs.org/@babel/helper-wrap-function/-/helper-wrap-function-7.13.0.tgz", + "integrity": "sha512-1UX9F7K3BS42fI6qd2A4BjKzgGjToscyZTdp1DjknHLCIvpgne6918io+aL5LXFcER/8QWiwpoY902pVEqgTXA==", "dev": true, "requires": { - "@babel/helper-function-name": "^7.10.4", - "@babel/template": "^7.10.4", - "@babel/traverse": "^7.10.4", - "@babel/types": "^7.10.4" + "@babel/helper-function-name": "^7.12.13", + "@babel/template": "^7.12.13", + "@babel/traverse": "^7.13.0", + "@babel/types": "^7.13.0" } }, "@babel/helpers": { - "version": "7.12.5", - "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.12.5.tgz", - "integrity": "sha512-lgKGMQlKqA8meJqKsW6rUnc4MdUk35Ln0ATDqdM1a/UpARODdI4j5Y5lVfUScnSNkJcdCRAaWkspykNoFg9sJA==", + "version": "7.13.0", + "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.13.0.tgz", + "integrity": "sha512-aan1MeFPxFacZeSz6Ld7YZo5aPuqnKlD7+HZY75xQsueczFccP9A7V05+oe0XpLwHK3oLorPe9eaAUljL7WEaQ==", "dev": true, "requires": { - "@babel/template": "^7.10.4", - "@babel/traverse": "^7.12.5", - "@babel/types": "^7.12.5" + "@babel/template": "^7.12.13", + "@babel/traverse": "^7.13.0", + "@babel/types": "^7.13.0" } }, "@babel/highlight": { - "version": "7.10.4", - "resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.10.4.tgz", - "integrity": "sha512-i6rgnR/YgPEQzZZnbTHHuZdlE8qyoBNalD6F+q4vAFlcMEcqmkoG+mPqJYJCo63qPf74+Y1UZsl3l6f7/RIkmA==", + "version": "7.13.8", + "resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.13.8.tgz", + "integrity": "sha512-4vrIhfJyfNf+lCtXC2ck1rKSzDwciqF7IWFhXXrSOUC2O5DrVp+w4c6ed4AllTxhTkUP5x2tYj41VaxdVMMRDw==", "requires": { - "@babel/helper-validator-identifier": "^7.10.4", + "@babel/helper-validator-identifier": "^7.12.11", "chalk": "^2.0.0", "js-tokens": "^4.0.0" } }, "@babel/parser": { - "version": "7.12.11", - "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.12.11.tgz", - "integrity": "sha512-N3UxG+uuF4CMYoNj8AhnbAcJF0PiuJ9KHuy1lQmkYsxTer/MAH9UBNHsBoAX/4s6NvlDD047No8mYVGGzLL4hg==", + "version": "7.13.9", + "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.13.9.tgz", + "integrity": "sha512-nEUfRiARCcaVo3ny3ZQjURjHQZUo/JkEw7rLlSZy/psWGnvwXFtPcr6jb7Yb41DVW5LTe6KRq9LGleRNsg1Frw==", "dev": true }, "@babel/plugin-proposal-async-generator-functions": { - "version": "7.12.12", - "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-async-generator-functions/-/plugin-proposal-async-generator-functions-7.12.12.tgz", - "integrity": "sha512-nrz9y0a4xmUrRq51bYkWJIO5SBZyG2ys2qinHsN0zHDHVsUaModrkpyWWWXfGqYQmOL3x9sQIcTNN/pBGpo09A==", + "version": "7.13.8", + "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-async-generator-functions/-/plugin-proposal-async-generator-functions-7.13.8.tgz", + "integrity": "sha512-rPBnhj+WgoSmgq+4gQUtXx/vOcU+UYtjy1AA/aeD61Hwj410fwYyqfUcRP3lR8ucgliVJL/G7sXcNUecC75IXA==", "dev": true, "requires": { - "@babel/helper-plugin-utils": "^7.10.4", - "@babel/helper-remap-async-to-generator": "^7.12.1", - "@babel/plugin-syntax-async-generators": "^7.8.0" + "@babel/helper-plugin-utils": "^7.13.0", + "@babel/helper-remap-async-to-generator": "^7.13.0", + "@babel/plugin-syntax-async-generators": "^7.8.4" } }, "@babel/plugin-proposal-class-properties": { - "version": "7.12.1", - "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-class-properties/-/plugin-proposal-class-properties-7.12.1.tgz", - "integrity": "sha512-cKp3dlQsFsEs5CWKnN7BnSHOd0EOW8EKpEjkoz1pO2E5KzIDNV9Ros1b0CnmbVgAGXJubOYVBOGCT1OmJwOI7w==", + "version": "7.13.0", + "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-class-properties/-/plugin-proposal-class-properties-7.13.0.tgz", + "integrity": "sha512-KnTDjFNC1g+45ka0myZNvSBFLhNCLN+GeGYLDEA8Oq7MZ6yMgfLoIRh86GRT0FjtJhZw8JyUskP9uvj5pHM9Zg==", "dev": true, "requires": { - "@babel/helper-create-class-features-plugin": "^7.12.1", - "@babel/helper-plugin-utils": "^7.10.4" + "@babel/helper-create-class-features-plugin": "^7.13.0", + "@babel/helper-plugin-utils": "^7.13.0" } }, "@babel/plugin-proposal-decorators": { @@ -372,115 +416,117 @@ } }, "@babel/plugin-proposal-dynamic-import": { - "version": "7.12.1", - "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-dynamic-import/-/plugin-proposal-dynamic-import-7.12.1.tgz", - "integrity": "sha512-a4rhUSZFuq5W8/OO8H7BL5zspjnc1FLd9hlOxIK/f7qG4a0qsqk8uvF/ywgBA8/OmjsapjpvaEOYItfGG1qIvQ==", + "version": "7.13.8", + "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-dynamic-import/-/plugin-proposal-dynamic-import-7.13.8.tgz", + "integrity": "sha512-ONWKj0H6+wIRCkZi9zSbZtE/r73uOhMVHh256ys0UzfM7I3d4n+spZNWjOnJv2gzopumP2Wxi186vI8N0Y2JyQ==", "dev": true, "requires": { - "@babel/helper-plugin-utils": "^7.10.4", - "@babel/plugin-syntax-dynamic-import": "^7.8.0" + "@babel/helper-plugin-utils": "^7.13.0", + "@babel/plugin-syntax-dynamic-import": "^7.8.3" } }, "@babel/plugin-proposal-export-namespace-from": { - "version": "7.12.1", - "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-export-namespace-from/-/plugin-proposal-export-namespace-from-7.12.1.tgz", - "integrity": "sha512-6CThGf0irEkzujYS5LQcjBx8j/4aQGiVv7J9+2f7pGfxqyKh3WnmVJYW3hdrQjyksErMGBPQrCnHfOtna+WLbw==", + "version": "7.12.13", + "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-export-namespace-from/-/plugin-proposal-export-namespace-from-7.12.13.tgz", + "integrity": "sha512-INAgtFo4OnLN3Y/j0VwAgw3HDXcDtX+C/erMvWzuV9v71r7urb6iyMXu7eM9IgLr1ElLlOkaHjJ0SbCmdOQ3Iw==", "dev": true, "requires": { - "@babel/helper-plugin-utils": "^7.10.4", + "@babel/helper-plugin-utils": "^7.12.13", "@babel/plugin-syntax-export-namespace-from": "^7.8.3" } }, "@babel/plugin-proposal-json-strings": { - "version": "7.12.1", - "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-json-strings/-/plugin-proposal-json-strings-7.12.1.tgz", - "integrity": "sha512-GoLDUi6U9ZLzlSda2Df++VSqDJg3CG+dR0+iWsv6XRw1rEq+zwt4DirM9yrxW6XWaTpmai1cWJLMfM8qQJf+yw==", + "version": "7.13.8", + "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-json-strings/-/plugin-proposal-json-strings-7.13.8.tgz", + "integrity": "sha512-w4zOPKUFPX1mgvTmL/fcEqy34hrQ1CRcGxdphBc6snDnnqJ47EZDIyop6IwXzAC8G916hsIuXB2ZMBCExC5k7Q==", "dev": true, "requires": { - "@babel/helper-plugin-utils": "^7.10.4", - "@babel/plugin-syntax-json-strings": "^7.8.0" + "@babel/helper-plugin-utils": "^7.13.0", + "@babel/plugin-syntax-json-strings": "^7.8.3" } }, "@babel/plugin-proposal-logical-assignment-operators": { - "version": "7.12.1", - "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-logical-assignment-operators/-/plugin-proposal-logical-assignment-operators-7.12.1.tgz", - "integrity": "sha512-k8ZmVv0JU+4gcUGeCDZOGd0lCIamU/sMtIiX3UWnUc5yzgq6YUGyEolNYD+MLYKfSzgECPcqetVcJP9Afe/aCA==", + "version": "7.13.8", + "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-logical-assignment-operators/-/plugin-proposal-logical-assignment-operators-7.13.8.tgz", + "integrity": "sha512-aul6znYB4N4HGweImqKn59Su9RS8lbUIqxtXTOcAGtNIDczoEFv+l1EhmX8rUBp3G1jMjKJm8m0jXVp63ZpS4A==", "dev": true, "requires": { - "@babel/helper-plugin-utils": "^7.10.4", + "@babel/helper-plugin-utils": "^7.13.0", "@babel/plugin-syntax-logical-assignment-operators": "^7.10.4" } }, "@babel/plugin-proposal-nullish-coalescing-operator": { - "version": "7.12.1", - "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-nullish-coalescing-operator/-/plugin-proposal-nullish-coalescing-operator-7.12.1.tgz", - "integrity": "sha512-nZY0ESiaQDI1y96+jk6VxMOaL4LPo/QDHBqL+SF3/vl6dHkTwHlOI8L4ZwuRBHgakRBw5zsVylel7QPbbGuYgg==", + "version": "7.13.8", + "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-nullish-coalescing-operator/-/plugin-proposal-nullish-coalescing-operator-7.13.8.tgz", + "integrity": "sha512-iePlDPBn//UhxExyS9KyeYU7RM9WScAG+D3Hhno0PLJebAEpDZMocbDe64eqynhNAnwz/vZoL/q/QB2T1OH39A==", "dev": true, "requires": { - "@babel/helper-plugin-utils": "^7.10.4", - "@babel/plugin-syntax-nullish-coalescing-operator": "^7.8.0" + "@babel/helper-plugin-utils": "^7.13.0", + "@babel/plugin-syntax-nullish-coalescing-operator": "^7.8.3" } }, "@babel/plugin-proposal-numeric-separator": { - "version": "7.12.7", - "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-numeric-separator/-/plugin-proposal-numeric-separator-7.12.7.tgz", - "integrity": "sha512-8c+uy0qmnRTeukiGsjLGy6uVs/TFjJchGXUeBqlG4VWYOdJWkhhVPdQ3uHwbmalfJwv2JsV0qffXP4asRfL2SQ==", + "version": "7.12.13", + "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-numeric-separator/-/plugin-proposal-numeric-separator-7.12.13.tgz", + "integrity": "sha512-O1jFia9R8BUCl3ZGB7eitaAPu62TXJRHn7rh+ojNERCFyqRwJMTmhz+tJ+k0CwI6CLjX/ee4qW74FSqlq9I35w==", "dev": true, "requires": { - "@babel/helper-plugin-utils": "^7.10.4", + "@babel/helper-plugin-utils": "^7.12.13", "@babel/plugin-syntax-numeric-separator": "^7.10.4" } }, "@babel/plugin-proposal-object-rest-spread": { - "version": "7.12.1", - "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-object-rest-spread/-/plugin-proposal-object-rest-spread-7.12.1.tgz", - "integrity": "sha512-s6SowJIjzlhx8o7lsFx5zmY4At6CTtDvgNQDdPzkBQucle58A6b/TTeEBYtyDgmcXjUTM+vE8YOGHZzzbc/ioA==", + "version": "7.13.8", + "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-object-rest-spread/-/plugin-proposal-object-rest-spread-7.13.8.tgz", + "integrity": "sha512-DhB2EuB1Ih7S3/IRX5AFVgZ16k3EzfRbq97CxAVI1KSYcW+lexV8VZb7G7L8zuPVSdQMRn0kiBpf/Yzu9ZKH0g==", "dev": true, "requires": { - "@babel/helper-plugin-utils": "^7.10.4", - "@babel/plugin-syntax-object-rest-spread": "^7.8.0", - "@babel/plugin-transform-parameters": "^7.12.1" + "@babel/compat-data": "^7.13.8", + "@babel/helper-compilation-targets": "^7.13.8", + "@babel/helper-plugin-utils": "^7.13.0", + "@babel/plugin-syntax-object-rest-spread": "^7.8.3", + "@babel/plugin-transform-parameters": "^7.13.0" } }, "@babel/plugin-proposal-optional-catch-binding": { - "version": "7.12.1", - "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-optional-catch-binding/-/plugin-proposal-optional-catch-binding-7.12.1.tgz", - "integrity": "sha512-hFvIjgprh9mMw5v42sJWLI1lzU5L2sznP805zeT6rySVRA0Y18StRhDqhSxlap0oVgItRsB6WSROp4YnJTJz0g==", + "version": "7.13.8", + "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-optional-catch-binding/-/plugin-proposal-optional-catch-binding-7.13.8.tgz", + "integrity": "sha512-0wS/4DUF1CuTmGo+NiaHfHcVSeSLj5S3e6RivPTg/2k3wOv3jO35tZ6/ZWsQhQMvdgI7CwphjQa/ccarLymHVA==", "dev": true, "requires": { - "@babel/helper-plugin-utils": "^7.10.4", - "@babel/plugin-syntax-optional-catch-binding": "^7.8.0" + "@babel/helper-plugin-utils": "^7.13.0", + "@babel/plugin-syntax-optional-catch-binding": "^7.8.3" } }, "@babel/plugin-proposal-optional-chaining": { - "version": "7.12.7", - "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-optional-chaining/-/plugin-proposal-optional-chaining-7.12.7.tgz", - "integrity": "sha512-4ovylXZ0PWmwoOvhU2vhnzVNnm88/Sm9nx7V8BPgMvAzn5zDou3/Awy0EjglyubVHasJj+XCEkr/r1X3P5elCA==", + "version": "7.13.8", + "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-optional-chaining/-/plugin-proposal-optional-chaining-7.13.8.tgz", + "integrity": "sha512-hpbBwbTgd7Cz1QryvwJZRo1U0k1q8uyBmeXOSQUjdg/A2TASkhR/rz7AyqZ/kS8kbpsNA80rOYbxySBJAqmhhQ==", "dev": true, "requires": { - "@babel/helper-plugin-utils": "^7.10.4", + "@babel/helper-plugin-utils": "^7.13.0", "@babel/helper-skip-transparent-expression-wrappers": "^7.12.1", - "@babel/plugin-syntax-optional-chaining": "^7.8.0" + "@babel/plugin-syntax-optional-chaining": "^7.8.3" } }, "@babel/plugin-proposal-private-methods": { - "version": "7.12.1", - "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-private-methods/-/plugin-proposal-private-methods-7.12.1.tgz", - "integrity": "sha512-mwZ1phvH7/NHK6Kf8LP7MYDogGV+DKB1mryFOEwx5EBNQrosvIczzZFTUmWaeujd5xT6G1ELYWUz3CutMhjE1w==", + "version": "7.13.0", + "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-private-methods/-/plugin-proposal-private-methods-7.13.0.tgz", + "integrity": "sha512-MXyyKQd9inhx1kDYPkFRVOBXQ20ES8Pto3T7UZ92xj2mY0EVD8oAVzeyYuVfy/mxAdTSIayOvg+aVzcHV2bn6Q==", "dev": true, "requires": { - "@babel/helper-create-class-features-plugin": "^7.12.1", - "@babel/helper-plugin-utils": "^7.10.4" + "@babel/helper-create-class-features-plugin": "^7.13.0", + "@babel/helper-plugin-utils": "^7.13.0" } }, "@babel/plugin-proposal-unicode-property-regex": { - "version": "7.12.1", - "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-unicode-property-regex/-/plugin-proposal-unicode-property-regex-7.12.1.tgz", - "integrity": "sha512-MYq+l+PvHuw/rKUz1at/vb6nCnQ2gmJBNaM62z0OgH7B2W1D9pvkpYtlti9bGtizNIU1K3zm4bZF9F91efVY0w==", + "version": "7.12.13", + "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-unicode-property-regex/-/plugin-proposal-unicode-property-regex-7.12.13.tgz", + "integrity": "sha512-XyJmZidNfofEkqFV5VC/bLabGmO5QzenPO/YOfGuEbgU+2sSwMmio3YLb4WtBgcmmdwZHyVyv8on77IUjQ5Gvg==", "dev": true, "requires": { - "@babel/helper-create-regexp-features-plugin": "^7.12.1", - "@babel/helper-plugin-utils": "^7.10.4" + "@babel/helper-create-regexp-features-plugin": "^7.12.13", + "@babel/helper-plugin-utils": "^7.12.13" } }, "@babel/plugin-syntax-async-generators": { @@ -493,21 +539,21 @@ } }, "@babel/plugin-syntax-class-properties": { - "version": "7.12.1", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-class-properties/-/plugin-syntax-class-properties-7.12.1.tgz", - "integrity": "sha512-U40A76x5gTwmESz+qiqssqmeEsKvcSyvtgktrm0uzcARAmM9I1jR221f6Oq+GmHrcD+LvZDag1UTOTe2fL3TeA==", + "version": "7.12.13", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-class-properties/-/plugin-syntax-class-properties-7.12.13.tgz", + "integrity": "sha512-fm4idjKla0YahUNgFNLCB0qySdsoPiZP3iQE3rky0mBUtMZ23yDJ9SJdg6dXTSDnulOVqiF3Hgr9nbXvXTQZYA==", "dev": true, "requires": { - "@babel/helper-plugin-utils": "^7.10.4" + "@babel/helper-plugin-utils": "^7.12.13" } }, "@babel/plugin-syntax-decorators": { - "version": "7.12.1", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-decorators/-/plugin-syntax-decorators-7.12.1.tgz", - "integrity": "sha512-ir9YW5daRrTYiy9UJ2TzdNIJEZu8KclVzDcfSt4iEmOtwQ4llPtWInNKJyKnVXp1vE4bbVd5S31M/im3mYMO1w==", + "version": "7.12.13", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-decorators/-/plugin-syntax-decorators-7.12.13.tgz", + "integrity": "sha512-Rw6aIXGuqDLr6/LoBBYE57nKOzQpz/aDkKlMqEwH+Vp0MXbG6H/TfRjaY343LKxzAKAMXIHsQ8JzaZKuDZ9MwA==", "dev": true, "requires": { - "@babel/helper-plugin-utils": "^7.10.4" + "@babel/helper-plugin-utils": "^7.12.13" } }, "@babel/plugin-syntax-dynamic-import": { @@ -529,12 +575,12 @@ } }, "@babel/plugin-syntax-flow": { - "version": "7.12.1", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-flow/-/plugin-syntax-flow-7.12.1.tgz", - "integrity": "sha512-1lBLLmtxrwpm4VKmtVFselI/P3pX+G63fAtUUt6b2Nzgao77KNDwyuRt90Mj2/9pKobtt68FdvjfqohZjg/FCA==", + "version": "7.12.13", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-flow/-/plugin-syntax-flow-7.12.13.tgz", + "integrity": "sha512-J/RYxnlSLXZLVR7wTRsozxKT8qbsx1mNKJzXEEjQ0Kjx1ZACcyHgbanNWNCFtc36IzuWhYWPpvJFFoexoOWFmA==", "dev": true, "requires": { - "@babel/helper-plugin-utils": "^7.10.4" + "@babel/helper-plugin-utils": "^7.12.13" } }, "@babel/plugin-syntax-json-strings": { @@ -547,12 +593,12 @@ } }, "@babel/plugin-syntax-jsx": { - "version": "7.12.1", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-jsx/-/plugin-syntax-jsx-7.12.1.tgz", - "integrity": "sha512-1yRi7yAtB0ETgxdY9ti/p2TivUxJkTdhu/ZbF9MshVGqOx1TdB3b7xCXs49Fupgg50N45KcAsRP/ZqWjs9SRjg==", + "version": "7.12.13", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-jsx/-/plugin-syntax-jsx-7.12.13.tgz", + "integrity": "sha512-d4HM23Q1K7oq/SLNmG6mRt85l2csmQ0cHRaxRXjKW0YFdEXqlZ5kzFQKH5Uc3rDJECgu+yCRgPkG04Mm98R/1g==", "dev": true, "requires": { - "@babel/helper-plugin-utils": "^7.10.4" + "@babel/helper-plugin-utils": "^7.12.13" } }, "@babel/plugin-syntax-logical-assignment-operators": { @@ -610,122 +656,121 @@ } }, "@babel/plugin-syntax-top-level-await": { - "version": "7.12.1", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-top-level-await/-/plugin-syntax-top-level-await-7.12.1.tgz", - "integrity": "sha512-i7ooMZFS+a/Om0crxZodrTzNEPJHZrlMVGMTEpFAj6rYY/bKCddB0Dk/YxfPuYXOopuhKk/e1jV6h+WUU9XN3A==", + "version": "7.12.13", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-top-level-await/-/plugin-syntax-top-level-await-7.12.13.tgz", + "integrity": "sha512-A81F9pDwyS7yM//KwbCSDqy3Uj4NMIurtplxphWxoYtNPov7cJsDkAFNNyVlIZ3jwGycVsurZ+LtOA8gZ376iQ==", "dev": true, "requires": { - "@babel/helper-plugin-utils": "^7.10.4" + "@babel/helper-plugin-utils": "^7.12.13" } }, "@babel/plugin-syntax-typescript": { - "version": "7.12.1", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-typescript/-/plugin-syntax-typescript-7.12.1.tgz", - "integrity": "sha512-UZNEcCY+4Dp9yYRCAHrHDU+9ZXLYaY9MgBXSRLkB9WjYFRR6quJBumfVrEkUxrePPBwFcpWfNKXqVRQQtm7mMA==", + "version": "7.12.13", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-typescript/-/plugin-syntax-typescript-7.12.13.tgz", + "integrity": "sha512-cHP3u1JiUiG2LFDKbXnwVad81GvfyIOmCD6HIEId6ojrY0Drfy2q1jw7BwN7dE84+kTnBjLkXoL3IEy/3JPu2w==", "dev": true, "requires": { - "@babel/helper-plugin-utils": "^7.10.4" + "@babel/helper-plugin-utils": "^7.12.13" } }, "@babel/plugin-transform-arrow-functions": { - "version": "7.12.1", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-arrow-functions/-/plugin-transform-arrow-functions-7.12.1.tgz", - "integrity": "sha512-5QB50qyN44fzzz4/qxDPQMBCTHgxg3n0xRBLJUmBlLoU/sFvxVWGZF/ZUfMVDQuJUKXaBhbupxIzIfZ6Fwk/0A==", + "version": "7.13.0", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-arrow-functions/-/plugin-transform-arrow-functions-7.13.0.tgz", + "integrity": "sha512-96lgJagobeVmazXFaDrbmCLQxBysKu7U6Do3mLsx27gf5Dk85ezysrs2BZUpXD703U/Su1xTBDxxar2oa4jAGg==", "dev": true, "requires": { - "@babel/helper-plugin-utils": "^7.10.4" + "@babel/helper-plugin-utils": "^7.13.0" } }, "@babel/plugin-transform-async-to-generator": { - "version": "7.12.1", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-async-to-generator/-/plugin-transform-async-to-generator-7.12.1.tgz", - "integrity": "sha512-SDtqoEcarK1DFlRJ1hHRY5HvJUj5kX4qmtpMAm2QnhOlyuMC4TMdCRgW6WXpv93rZeYNeLP22y8Aq2dbcDRM1A==", + "version": "7.13.0", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-async-to-generator/-/plugin-transform-async-to-generator-7.13.0.tgz", + "integrity": "sha512-3j6E004Dx0K3eGmhxVJxwwI89CTJrce7lg3UrtFuDAVQ/2+SJ/h/aSFOeE6/n0WB1GsOffsJp6MnPQNQ8nmwhg==", "dev": true, "requires": { - "@babel/helper-module-imports": "^7.12.1", - "@babel/helper-plugin-utils": "^7.10.4", - "@babel/helper-remap-async-to-generator": "^7.12.1" + "@babel/helper-module-imports": "^7.12.13", + "@babel/helper-plugin-utils": "^7.13.0", + "@babel/helper-remap-async-to-generator": "^7.13.0" } }, "@babel/plugin-transform-block-scoped-functions": { - "version": "7.12.1", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-block-scoped-functions/-/plugin-transform-block-scoped-functions-7.12.1.tgz", - "integrity": "sha512-5OpxfuYnSgPalRpo8EWGPzIYf0lHBWORCkj5M0oLBwHdlux9Ri36QqGW3/LR13RSVOAoUUMzoPI/jpE4ABcHoA==", + "version": "7.12.13", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-block-scoped-functions/-/plugin-transform-block-scoped-functions-7.12.13.tgz", + "integrity": "sha512-zNyFqbc3kI/fVpqwfqkg6RvBgFpC4J18aKKMmv7KdQ/1GgREapSJAykLMVNwfRGO3BtHj3YQZl8kxCXPcVMVeg==", "dev": true, "requires": { - "@babel/helper-plugin-utils": "^7.10.4" + "@babel/helper-plugin-utils": "^7.12.13" } }, "@babel/plugin-transform-block-scoping": { - "version": "7.12.12", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-block-scoping/-/plugin-transform-block-scoping-7.12.12.tgz", - "integrity": "sha512-VOEPQ/ExOVqbukuP7BYJtI5ZxxsmegTwzZ04j1aF0dkSypGo9XpDHuOrABsJu+ie+penpSJheDJ11x1BEZNiyQ==", + "version": "7.12.13", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-block-scoping/-/plugin-transform-block-scoping-7.12.13.tgz", + "integrity": "sha512-Pxwe0iqWJX4fOOM2kEZeUuAxHMWb9nK+9oh5d11bsLoB0xMg+mkDpt0eYuDZB7ETrY9bbcVlKUGTOGWy7BHsMQ==", "dev": true, "requires": { - "@babel/helper-plugin-utils": "^7.10.4" + "@babel/helper-plugin-utils": "^7.12.13" } }, "@babel/plugin-transform-classes": { - "version": "7.12.1", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-classes/-/plugin-transform-classes-7.12.1.tgz", - "integrity": "sha512-/74xkA7bVdzQTBeSUhLLJgYIcxw/dpEpCdRDiHgPJ3Mv6uC11UhjpOhl72CgqbBCmt1qtssCyB2xnJm1+PFjog==", + "version": "7.13.0", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-classes/-/plugin-transform-classes-7.13.0.tgz", + "integrity": "sha512-9BtHCPUARyVH1oXGcSJD3YpsqRLROJx5ZNP6tN5vnk17N0SVf9WCtf8Nuh1CFmgByKKAIMstitKduoCmsaDK5g==", "dev": true, "requires": { - "@babel/helper-annotate-as-pure": "^7.10.4", - "@babel/helper-define-map": "^7.10.4", - "@babel/helper-function-name": "^7.10.4", - "@babel/helper-optimise-call-expression": "^7.10.4", - "@babel/helper-plugin-utils": "^7.10.4", - "@babel/helper-replace-supers": "^7.12.1", - "@babel/helper-split-export-declaration": "^7.10.4", + "@babel/helper-annotate-as-pure": "^7.12.13", + "@babel/helper-function-name": "^7.12.13", + "@babel/helper-optimise-call-expression": "^7.12.13", + "@babel/helper-plugin-utils": "^7.13.0", + "@babel/helper-replace-supers": "^7.13.0", + "@babel/helper-split-export-declaration": "^7.12.13", "globals": "^11.1.0" } }, "@babel/plugin-transform-computed-properties": { - "version": "7.12.1", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-computed-properties/-/plugin-transform-computed-properties-7.12.1.tgz", - "integrity": "sha512-vVUOYpPWB7BkgUWPo4C44mUQHpTZXakEqFjbv8rQMg7TC6S6ZhGZ3otQcRH6u7+adSlE5i0sp63eMC/XGffrzg==", + "version": "7.13.0", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-computed-properties/-/plugin-transform-computed-properties-7.13.0.tgz", + "integrity": "sha512-RRqTYTeZkZAz8WbieLTvKUEUxZlUTdmL5KGMyZj7FnMfLNKV4+r5549aORG/mgojRmFlQMJDUupwAMiF2Q7OUg==", "dev": true, "requires": { - "@babel/helper-plugin-utils": "^7.10.4" + "@babel/helper-plugin-utils": "^7.13.0" } }, "@babel/plugin-transform-destructuring": { - "version": "7.12.1", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-destructuring/-/plugin-transform-destructuring-7.12.1.tgz", - "integrity": "sha512-fRMYFKuzi/rSiYb2uRLiUENJOKq4Gnl+6qOv5f8z0TZXg3llUwUhsNNwrwaT/6dUhJTzNpBr+CUvEWBtfNY1cw==", + "version": "7.13.0", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-destructuring/-/plugin-transform-destructuring-7.13.0.tgz", + "integrity": "sha512-zym5em7tePoNT9s964c0/KU3JPPnuq7VhIxPRefJ4/s82cD+q1mgKfuGRDMCPL0HTyKz4dISuQlCusfgCJ86HA==", "dev": true, "requires": { - "@babel/helper-plugin-utils": "^7.10.4" + "@babel/helper-plugin-utils": "^7.13.0" } }, "@babel/plugin-transform-dotall-regex": { - "version": "7.12.1", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-dotall-regex/-/plugin-transform-dotall-regex-7.12.1.tgz", - "integrity": "sha512-B2pXeRKoLszfEW7J4Hg9LoFaWEbr/kzo3teWHmtFCszjRNa/b40f9mfeqZsIDLLt/FjwQ6pz/Gdlwy85xNckBA==", + "version": "7.12.13", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-dotall-regex/-/plugin-transform-dotall-regex-7.12.13.tgz", + "integrity": "sha512-foDrozE65ZFdUC2OfgeOCrEPTxdB3yjqxpXh8CH+ipd9CHd4s/iq81kcUpyH8ACGNEPdFqbtzfgzbT/ZGlbDeQ==", "dev": true, "requires": { - "@babel/helper-create-regexp-features-plugin": "^7.12.1", - "@babel/helper-plugin-utils": "^7.10.4" + "@babel/helper-create-regexp-features-plugin": "^7.12.13", + "@babel/helper-plugin-utils": "^7.12.13" } }, "@babel/plugin-transform-duplicate-keys": { - "version": "7.12.1", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-duplicate-keys/-/plugin-transform-duplicate-keys-7.12.1.tgz", - "integrity": "sha512-iRght0T0HztAb/CazveUpUQrZY+aGKKaWXMJ4uf9YJtqxSUe09j3wteztCUDRHs+SRAL7yMuFqUsLoAKKzgXjw==", + "version": "7.12.13", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-duplicate-keys/-/plugin-transform-duplicate-keys-7.12.13.tgz", + "integrity": "sha512-NfADJiiHdhLBW3pulJlJI2NB0t4cci4WTZ8FtdIuNc2+8pslXdPtRRAEWqUY+m9kNOk2eRYbTAOipAxlrOcwwQ==", "dev": true, "requires": { - "@babel/helper-plugin-utils": "^7.10.4" + "@babel/helper-plugin-utils": "^7.12.13" } }, "@babel/plugin-transform-exponentiation-operator": { - "version": "7.12.1", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-exponentiation-operator/-/plugin-transform-exponentiation-operator-7.12.1.tgz", - "integrity": "sha512-7tqwy2bv48q+c1EHbXK0Zx3KXd2RVQp6OC7PbwFNt/dPTAV3Lu5sWtWuAj8owr5wqtWnqHfl2/mJlUmqkChKug==", + "version": "7.12.13", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-exponentiation-operator/-/plugin-transform-exponentiation-operator-7.12.13.tgz", + "integrity": "sha512-fbUelkM1apvqez/yYx1/oICVnGo2KM5s63mhGylrmXUxK/IAXSIf87QIxVfZldWf4QsOafY6vV3bX8aMHSvNrA==", "dev": true, "requires": { - "@babel/helper-builder-binary-assignment-operator-visitor": "^7.10.4", - "@babel/helper-plugin-utils": "^7.10.4" + "@babel/helper-builder-binary-assignment-operator-visitor": "^7.12.13", + "@babel/helper-plugin-utils": "^7.12.13" } }, "@babel/plugin-transform-flow-strip-types": { @@ -739,199 +784,199 @@ } }, "@babel/plugin-transform-for-of": { - "version": "7.12.1", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-for-of/-/plugin-transform-for-of-7.12.1.tgz", - "integrity": "sha512-Zaeq10naAsuHo7heQvyV0ptj4dlZJwZgNAtBYBnu5nNKJoW62m0zKcIEyVECrUKErkUkg6ajMy4ZfnVZciSBhg==", + "version": "7.13.0", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-for-of/-/plugin-transform-for-of-7.13.0.tgz", + "integrity": "sha512-IHKT00mwUVYE0zzbkDgNRP6SRzvfGCYsOxIRz8KsiaaHCcT9BWIkO+H9QRJseHBLOGBZkHUdHiqj6r0POsdytg==", "dev": true, "requires": { - "@babel/helper-plugin-utils": "^7.10.4" + "@babel/helper-plugin-utils": "^7.13.0" } }, "@babel/plugin-transform-function-name": { - "version": "7.12.1", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-function-name/-/plugin-transform-function-name-7.12.1.tgz", - "integrity": "sha512-JF3UgJUILoFrFMEnOJLJkRHSk6LUSXLmEFsA23aR2O5CSLUxbeUX1IZ1YQ7Sn0aXb601Ncwjx73a+FVqgcljVw==", + "version": "7.12.13", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-function-name/-/plugin-transform-function-name-7.12.13.tgz", + "integrity": "sha512-6K7gZycG0cmIwwF7uMK/ZqeCikCGVBdyP2J5SKNCXO5EOHcqi+z7Jwf8AmyDNcBgxET8DrEtCt/mPKPyAzXyqQ==", "dev": true, "requires": { - "@babel/helper-function-name": "^7.10.4", - "@babel/helper-plugin-utils": "^7.10.4" + "@babel/helper-function-name": "^7.12.13", + "@babel/helper-plugin-utils": "^7.12.13" } }, "@babel/plugin-transform-literals": { - "version": "7.12.1", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-literals/-/plugin-transform-literals-7.12.1.tgz", - "integrity": "sha512-+PxVGA+2Ag6uGgL0A5f+9rklOnnMccwEBzwYFL3EUaKuiyVnUipyXncFcfjSkbimLrODoqki1U9XxZzTvfN7IQ==", + "version": "7.12.13", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-literals/-/plugin-transform-literals-7.12.13.tgz", + "integrity": "sha512-FW+WPjSR7hiUxMcKqyNjP05tQ2kmBCdpEpZHY1ARm96tGQCCBvXKnpjILtDplUnJ/eHZ0lALLM+d2lMFSpYJrQ==", "dev": true, "requires": { - "@babel/helper-plugin-utils": "^7.10.4" + "@babel/helper-plugin-utils": "^7.12.13" } }, "@babel/plugin-transform-member-expression-literals": { - "version": "7.12.1", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-member-expression-literals/-/plugin-transform-member-expression-literals-7.12.1.tgz", - "integrity": "sha512-1sxePl6z9ad0gFMB9KqmYofk34flq62aqMt9NqliS/7hPEpURUCMbyHXrMPlo282iY7nAvUB1aQd5mg79UD9Jg==", + "version": "7.12.13", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-member-expression-literals/-/plugin-transform-member-expression-literals-7.12.13.tgz", + "integrity": "sha512-kxLkOsg8yir4YeEPHLuO2tXP9R/gTjpuTOjshqSpELUN3ZAg2jfDnKUvzzJxObun38sw3wm4Uu69sX/zA7iRvg==", "dev": true, "requires": { - "@babel/helper-plugin-utils": "^7.10.4" + "@babel/helper-plugin-utils": "^7.12.13" } }, "@babel/plugin-transform-modules-amd": { - "version": "7.12.1", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-amd/-/plugin-transform-modules-amd-7.12.1.tgz", - "integrity": "sha512-tDW8hMkzad5oDtzsB70HIQQRBiTKrhfgwC/KkJeGsaNFTdWhKNt/BiE8c5yj19XiGyrxpbkOfH87qkNg1YGlOQ==", + "version": "7.13.0", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-amd/-/plugin-transform-modules-amd-7.13.0.tgz", + "integrity": "sha512-EKy/E2NHhY/6Vw5d1k3rgoobftcNUmp9fGjb9XZwQLtTctsRBOTRO7RHHxfIky1ogMN5BxN7p9uMA3SzPfotMQ==", "dev": true, "requires": { - "@babel/helper-module-transforms": "^7.12.1", - "@babel/helper-plugin-utils": "^7.10.4", + "@babel/helper-module-transforms": "^7.13.0", + "@babel/helper-plugin-utils": "^7.13.0", "babel-plugin-dynamic-import-node": "^2.3.3" } }, "@babel/plugin-transform-modules-commonjs": { - "version": "7.12.1", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-commonjs/-/plugin-transform-modules-commonjs-7.12.1.tgz", - "integrity": "sha512-dY789wq6l0uLY8py9c1B48V8mVL5gZh/+PQ5ZPrylPYsnAvnEMjqsUXkuoDVPeVK+0VyGar+D08107LzDQ6pag==", + "version": "7.13.8", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-commonjs/-/plugin-transform-modules-commonjs-7.13.8.tgz", + "integrity": "sha512-9QiOx4MEGglfYZ4XOnU79OHr6vIWUakIj9b4mioN8eQIoEh+pf5p/zEB36JpDFWA12nNMiRf7bfoRvl9Rn79Bw==", "dev": true, "requires": { - "@babel/helper-module-transforms": "^7.12.1", - "@babel/helper-plugin-utils": "^7.10.4", - "@babel/helper-simple-access": "^7.12.1", + "@babel/helper-module-transforms": "^7.13.0", + "@babel/helper-plugin-utils": "^7.13.0", + "@babel/helper-simple-access": "^7.12.13", "babel-plugin-dynamic-import-node": "^2.3.3" } }, "@babel/plugin-transform-modules-systemjs": { - "version": "7.12.1", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-systemjs/-/plugin-transform-modules-systemjs-7.12.1.tgz", - "integrity": "sha512-Hn7cVvOavVh8yvW6fLwveFqSnd7rbQN3zJvoPNyNaQSvgfKmDBO9U1YL9+PCXGRlZD9tNdWTy5ACKqMuzyn32Q==", + "version": "7.13.8", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-systemjs/-/plugin-transform-modules-systemjs-7.13.8.tgz", + "integrity": "sha512-hwqctPYjhM6cWvVIlOIe27jCIBgHCsdH2xCJVAYQm7V5yTMoilbVMi9f6wKg0rpQAOn6ZG4AOyvCqFF/hUh6+A==", "dev": true, "requires": { - "@babel/helper-hoist-variables": "^7.10.4", - "@babel/helper-module-transforms": "^7.12.1", - "@babel/helper-plugin-utils": "^7.10.4", - "@babel/helper-validator-identifier": "^7.10.4", + "@babel/helper-hoist-variables": "^7.13.0", + "@babel/helper-module-transforms": "^7.13.0", + "@babel/helper-plugin-utils": "^7.13.0", + "@babel/helper-validator-identifier": "^7.12.11", "babel-plugin-dynamic-import-node": "^2.3.3" } }, "@babel/plugin-transform-modules-umd": { - "version": "7.12.1", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-umd/-/plugin-transform-modules-umd-7.12.1.tgz", - "integrity": "sha512-aEIubCS0KHKM0zUos5fIoQm+AZUMt1ZvMpqz0/H5qAQ7vWylr9+PLYurT+Ic7ID/bKLd4q8hDovaG3Zch2uz5Q==", + "version": "7.13.0", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-umd/-/plugin-transform-modules-umd-7.13.0.tgz", + "integrity": "sha512-D/ILzAh6uyvkWjKKyFE/W0FzWwasv6vPTSqPcjxFqn6QpX3u8DjRVliq4F2BamO2Wee/om06Vyy+vPkNrd4wxw==", "dev": true, "requires": { - "@babel/helper-module-transforms": "^7.12.1", - "@babel/helper-plugin-utils": "^7.10.4" + "@babel/helper-module-transforms": "^7.13.0", + "@babel/helper-plugin-utils": "^7.13.0" } }, "@babel/plugin-transform-named-capturing-groups-regex": { - "version": "7.12.1", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-named-capturing-groups-regex/-/plugin-transform-named-capturing-groups-regex-7.12.1.tgz", - "integrity": "sha512-tB43uQ62RHcoDp9v2Nsf+dSM8sbNodbEicbQNA53zHz8pWUhsgHSJCGpt7daXxRydjb0KnfmB+ChXOv3oADp1Q==", + "version": "7.12.13", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-named-capturing-groups-regex/-/plugin-transform-named-capturing-groups-regex-7.12.13.tgz", + "integrity": "sha512-Xsm8P2hr5hAxyYblrfACXpQKdQbx4m2df9/ZZSQ8MAhsadw06+jW7s9zsSw6he+mJZXRlVMyEnVktJo4zjk1WA==", "dev": true, "requires": { - "@babel/helper-create-regexp-features-plugin": "^7.12.1" + "@babel/helper-create-regexp-features-plugin": "^7.12.13" } }, "@babel/plugin-transform-new-target": { - "version": "7.12.1", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-new-target/-/plugin-transform-new-target-7.12.1.tgz", - "integrity": "sha512-+eW/VLcUL5L9IvJH7rT1sT0CzkdUTvPrXC2PXTn/7z7tXLBuKvezYbGdxD5WMRoyvyaujOq2fWoKl869heKjhw==", + "version": "7.12.13", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-new-target/-/plugin-transform-new-target-7.12.13.tgz", + "integrity": "sha512-/KY2hbLxrG5GTQ9zzZSc3xWiOy379pIETEhbtzwZcw9rvuaVV4Fqy7BYGYOWZnaoXIQYbbJ0ziXLa/sKcGCYEQ==", "dev": true, "requires": { - "@babel/helper-plugin-utils": "^7.10.4" + "@babel/helper-plugin-utils": "^7.12.13" } }, "@babel/plugin-transform-object-assign": { - "version": "7.12.1", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-object-assign/-/plugin-transform-object-assign-7.12.1.tgz", - "integrity": "sha512-geUHn4XwHznRAFiuROTy0Hr7bKbpijJCmr1Svt/VNGhpxmp0OrdxURNpWbOAf94nUbL+xj6gbxRVPHWIbRpRoA==", + "version": "7.12.13", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-object-assign/-/plugin-transform-object-assign-7.12.13.tgz", + "integrity": "sha512-4QxDMc0lAOkIBSfCrnSGbAJ+4epDBF2XXwcLXuBcG1xl9u7LrktNVD4+LwhL47XuKVPQ7R25e/WdcV+h97HyZA==", "dev": true, "requires": { - "@babel/helper-plugin-utils": "^7.10.4" + "@babel/helper-plugin-utils": "^7.12.13" } }, "@babel/plugin-transform-object-super": { - "version": "7.12.1", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-object-super/-/plugin-transform-object-super-7.12.1.tgz", - "integrity": "sha512-AvypiGJH9hsquNUn+RXVcBdeE3KHPZexWRdimhuV59cSoOt5kFBmqlByorAeUlGG2CJWd0U+4ZtNKga/TB0cAw==", + "version": "7.12.13", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-object-super/-/plugin-transform-object-super-7.12.13.tgz", + "integrity": "sha512-JzYIcj3XtYspZDV8j9ulnoMPZZnF/Cj0LUxPOjR89BdBVx+zYJI9MdMIlUZjbXDX+6YVeS6I3e8op+qQ3BYBoQ==", "dev": true, "requires": { - "@babel/helper-plugin-utils": "^7.10.4", - "@babel/helper-replace-supers": "^7.12.1" + "@babel/helper-plugin-utils": "^7.12.13", + "@babel/helper-replace-supers": "^7.12.13" } }, "@babel/plugin-transform-parameters": { - "version": "7.12.1", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-parameters/-/plugin-transform-parameters-7.12.1.tgz", - "integrity": "sha512-xq9C5EQhdPK23ZeCdMxl8bbRnAgHFrw5EOC3KJUsSylZqdkCaFEXxGSBuTSObOpiiHHNyb82es8M1QYgfQGfNg==", + "version": "7.13.0", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-parameters/-/plugin-transform-parameters-7.13.0.tgz", + "integrity": "sha512-Jt8k/h/mIwE2JFEOb3lURoY5C85ETcYPnbuAJ96zRBzh1XHtQZfs62ChZ6EP22QlC8c7Xqr9q+e1SU5qttwwjw==", "dev": true, "requires": { - "@babel/helper-plugin-utils": "^7.10.4" + "@babel/helper-plugin-utils": "^7.13.0" } }, "@babel/plugin-transform-property-literals": { - "version": "7.12.1", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-property-literals/-/plugin-transform-property-literals-7.12.1.tgz", - "integrity": "sha512-6MTCR/mZ1MQS+AwZLplX4cEySjCpnIF26ToWo942nqn8hXSm7McaHQNeGx/pt7suI1TWOWMfa/NgBhiqSnX0cQ==", + "version": "7.12.13", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-property-literals/-/plugin-transform-property-literals-7.12.13.tgz", + "integrity": "sha512-nqVigwVan+lR+g8Fj8Exl0UQX2kymtjcWfMOYM1vTYEKujeyv2SkMgazf2qNcK7l4SDiKyTA/nHCPqL4e2zo1A==", "dev": true, "requires": { - "@babel/helper-plugin-utils": "^7.10.4" + "@babel/helper-plugin-utils": "^7.12.13" } }, "@babel/plugin-transform-react-constant-elements": { - "version": "7.12.1", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-react-constant-elements/-/plugin-transform-react-constant-elements-7.12.1.tgz", - "integrity": "sha512-KOHd0tIRLoER+J+8f9DblZDa1fLGPwaaN1DI1TVHuQFOpjHV22C3CUB3obeC4fexHY9nx+fH0hQNvLFFfA1mxA==", + "version": "7.12.13", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-react-constant-elements/-/plugin-transform-react-constant-elements-7.12.13.tgz", + "integrity": "sha512-qmzKVTn46Upvtxv8LQoQ8mTCdUC83AOVQIQm57e9oekLT5cmK9GOMOfcWhe8jMNx4UJXn/UDhVZ/7lGofVNeDQ==", "dev": true, "requires": { - "@babel/helper-plugin-utils": "^7.10.4" + "@babel/helper-plugin-utils": "^7.12.13" } }, "@babel/plugin-transform-react-display-name": { - "version": "7.12.1", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-react-display-name/-/plugin-transform-react-display-name-7.12.1.tgz", - "integrity": "sha512-cAzB+UzBIrekfYxyLlFqf/OagTvHLcVBb5vpouzkYkBclRPraiygVnafvAoipErZLI8ANv8Ecn6E/m5qPXD26w==", + "version": "7.12.13", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-react-display-name/-/plugin-transform-react-display-name-7.12.13.tgz", + "integrity": "sha512-MprESJzI9O5VnJZrL7gg1MpdqmiFcUv41Jc7SahxYsNP2kDkFqClxxTZq+1Qv4AFCamm+GXMRDQINNn+qrxmiA==", "dev": true, "requires": { - "@babel/helper-plugin-utils": "^7.10.4" + "@babel/helper-plugin-utils": "^7.12.13" } }, "@babel/plugin-transform-react-jsx": { - "version": "7.12.12", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-react-jsx/-/plugin-transform-react-jsx-7.12.12.tgz", - "integrity": "sha512-JDWGuzGNWscYcq8oJVCtSE61a5+XAOos+V0HrxnDieUus4UMnBEosDnY1VJqU5iZ4pA04QY7l0+JvHL1hZEfsw==", + "version": "7.12.17", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-react-jsx/-/plugin-transform-react-jsx-7.12.17.tgz", + "integrity": "sha512-mwaVNcXV+l6qJOuRhpdTEj8sT/Z0owAVWf9QujTZ0d2ye9X/K+MTOTSizcgKOj18PGnTc/7g1I4+cIUjsKhBcw==", "dev": true, "requires": { - "@babel/helper-annotate-as-pure": "^7.12.10", - "@babel/helper-module-imports": "^7.12.5", - "@babel/helper-plugin-utils": "^7.10.4", - "@babel/plugin-syntax-jsx": "^7.12.1", - "@babel/types": "^7.12.12" + "@babel/helper-annotate-as-pure": "^7.12.13", + "@babel/helper-module-imports": "^7.12.13", + "@babel/helper-plugin-utils": "^7.12.13", + "@babel/plugin-syntax-jsx": "^7.12.13", + "@babel/types": "^7.12.17" } }, "@babel/plugin-transform-react-jsx-development": { - "version": "7.12.12", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-react-jsx-development/-/plugin-transform-react-jsx-development-7.12.12.tgz", - "integrity": "sha512-i1AxnKxHeMxUaWVXQOSIco4tvVvvCxMSfeBMnMM06mpaJt3g+MpxYQQrDfojUQldP1xxraPSJYSMEljoWM/dCg==", + "version": "7.12.17", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-react-jsx-development/-/plugin-transform-react-jsx-development-7.12.17.tgz", + "integrity": "sha512-BPjYV86SVuOaudFhsJR1zjgxxOhJDt6JHNoD48DxWEIxUCAMjV1ys6DYw4SDYZh0b1QsS2vfIA9t/ZsQGsDOUQ==", "dev": true, "requires": { - "@babel/plugin-transform-react-jsx": "^7.12.12" + "@babel/plugin-transform-react-jsx": "^7.12.17" } }, "@babel/plugin-transform-react-jsx-self": { - "version": "7.12.1", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-react-jsx-self/-/plugin-transform-react-jsx-self-7.12.1.tgz", - "integrity": "sha512-FbpL0ieNWiiBB5tCldX17EtXgmzeEZjFrix72rQYeq9X6nUK38HCaxexzVQrZWXanxKJPKVVIU37gFjEQYkPkA==", + "version": "7.12.13", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-react-jsx-self/-/plugin-transform-react-jsx-self-7.12.13.tgz", + "integrity": "sha512-FXYw98TTJ125GVCCkFLZXlZ1qGcsYqNQhVBQcZjyrwf8FEUtVfKIoidnO8S0q+KBQpDYNTmiGo1gn67Vti04lQ==", "dev": true, "requires": { - "@babel/helper-plugin-utils": "^7.10.4" + "@babel/helper-plugin-utils": "^7.12.13" } }, "@babel/plugin-transform-react-jsx-source": { - "version": "7.12.1", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-react-jsx-source/-/plugin-transform-react-jsx-source-7.12.1.tgz", - "integrity": "sha512-keQ5kBfjJNRc6zZN1/nVHCd6LLIHq4aUKcVnvE/2l+ZZROSbqoiGFRtT5t3Is89XJxBQaP7NLZX2jgGHdZvvFQ==", + "version": "7.12.13", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-react-jsx-source/-/plugin-transform-react-jsx-source-7.12.13.tgz", + "integrity": "sha512-O5JJi6fyfih0WfDgIJXksSPhGP/G0fQpfxYy87sDc+1sFmsCS6wr3aAn+whbzkhbjtq4VMqLRaSzR6IsshIC0Q==", "dev": true, "requires": { - "@babel/helper-plugin-utils": "^7.10.4" + "@babel/helper-plugin-utils": "^7.12.13" } }, "@babel/plugin-transform-react-pure-annotations": { @@ -945,21 +990,21 @@ } }, "@babel/plugin-transform-regenerator": { - "version": "7.12.1", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-regenerator/-/plugin-transform-regenerator-7.12.1.tgz", - "integrity": "sha512-gYrHqs5itw6i4PflFX3OdBPMQdPbF4bj2REIUxlMRUFk0/ZOAIpDFuViuxPjUL7YC8UPnf+XG7/utJvqXdPKng==", + "version": "7.12.13", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-regenerator/-/plugin-transform-regenerator-7.12.13.tgz", + "integrity": "sha512-lxb2ZAvSLyJ2PEe47hoGWPmW22v7CtSl9jW8mingV4H2sEX/JOcrAj2nPuGWi56ERUm2bUpjKzONAuT6HCn2EA==", "dev": true, "requires": { "regenerator-transform": "^0.14.2" } }, "@babel/plugin-transform-reserved-words": { - "version": "7.12.1", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-reserved-words/-/plugin-transform-reserved-words-7.12.1.tgz", - "integrity": "sha512-pOnUfhyPKvZpVyBHhSBoX8vfA09b7r00Pmm1sH+29ae2hMTKVmSp4Ztsr8KBKjLjx17H0eJqaRC3bR2iThM54A==", + "version": "7.12.13", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-reserved-words/-/plugin-transform-reserved-words-7.12.13.tgz", + "integrity": "sha512-xhUPzDXxZN1QfiOy/I5tyye+TRz6lA7z6xaT4CLOjPRMVg1ldRf0LHw0TDBpYL4vG78556WuHdyO9oi5UmzZBg==", "dev": true, "requires": { - "@babel/helper-plugin-utils": "^7.10.4" + "@babel/helper-plugin-utils": "^7.12.13" } }, "@babel/plugin-transform-runtime": { @@ -975,153 +1020,163 @@ } }, "@babel/plugin-transform-shorthand-properties": { - "version": "7.12.1", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-shorthand-properties/-/plugin-transform-shorthand-properties-7.12.1.tgz", - "integrity": "sha512-GFZS3c/MhX1OusqB1MZ1ct2xRzX5ppQh2JU1h2Pnfk88HtFTM+TWQqJNfwkmxtPQtb/s1tk87oENfXJlx7rSDw==", + "version": "7.12.13", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-shorthand-properties/-/plugin-transform-shorthand-properties-7.12.13.tgz", + "integrity": "sha512-xpL49pqPnLtf0tVluuqvzWIgLEhuPpZzvs2yabUHSKRNlN7ScYU7aMlmavOeyXJZKgZKQRBlh8rHbKiJDraTSw==", "dev": true, "requires": { - "@babel/helper-plugin-utils": "^7.10.4" + "@babel/helper-plugin-utils": "^7.12.13" } }, "@babel/plugin-transform-spread": { - "version": "7.12.1", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-spread/-/plugin-transform-spread-7.12.1.tgz", - "integrity": "sha512-vuLp8CP0BE18zVYjsEBZ5xoCecMK6LBMMxYzJnh01rxQRvhNhH1csMMmBfNo5tGpGO+NhdSNW2mzIvBu3K1fng==", + "version": "7.13.0", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-spread/-/plugin-transform-spread-7.13.0.tgz", + "integrity": "sha512-V6vkiXijjzYeFmQTr3dBxPtZYLPcUfY34DebOU27jIl2M/Y8Egm52Hw82CSjjPqd54GTlJs5x+CR7HeNr24ckg==", "dev": true, "requires": { - "@babel/helper-plugin-utils": "^7.10.4", + "@babel/helper-plugin-utils": "^7.13.0", "@babel/helper-skip-transparent-expression-wrappers": "^7.12.1" } }, "@babel/plugin-transform-sticky-regex": { - "version": "7.12.7", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-sticky-regex/-/plugin-transform-sticky-regex-7.12.7.tgz", - "integrity": "sha512-VEiqZL5N/QvDbdjfYQBhruN0HYjSPjC4XkeqW4ny/jNtH9gcbgaqBIXYEZCNnESMAGs0/K/R7oFGMhOyu/eIxg==", + "version": "7.12.13", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-sticky-regex/-/plugin-transform-sticky-regex-7.12.13.tgz", + "integrity": "sha512-Jc3JSaaWT8+fr7GRvQP02fKDsYk4K/lYwWq38r/UGfaxo89ajud321NH28KRQ7xy1Ybc0VUE5Pz8psjNNDUglg==", "dev": true, "requires": { - "@babel/helper-plugin-utils": "^7.10.4" + "@babel/helper-plugin-utils": "^7.12.13" } }, "@babel/plugin-transform-template-literals": { - "version": "7.12.1", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-template-literals/-/plugin-transform-template-literals-7.12.1.tgz", - "integrity": "sha512-b4Zx3KHi+taXB1dVRBhVJtEPi9h1THCeKmae2qP0YdUHIFhVjtpqqNfxeVAa1xeHVhAy4SbHxEwx5cltAu5apw==", + "version": "7.13.0", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-template-literals/-/plugin-transform-template-literals-7.13.0.tgz", + "integrity": "sha512-d67umW6nlfmr1iehCcBv69eSUSySk1EsIS8aTDX4Xo9qajAh6mYtcl4kJrBkGXuxZPEgVr7RVfAvNW6YQkd4Mw==", "dev": true, "requires": { - "@babel/helper-plugin-utils": "^7.10.4" + "@babel/helper-plugin-utils": "^7.13.0" } }, "@babel/plugin-transform-typeof-symbol": { - "version": "7.12.10", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-typeof-symbol/-/plugin-transform-typeof-symbol-7.12.10.tgz", - "integrity": "sha512-JQ6H8Rnsogh//ijxspCjc21YPd3VLVoYtAwv3zQmqAt8YGYUtdo5usNhdl4b9/Vir2kPFZl6n1h0PfUz4hJhaA==", + "version": "7.12.13", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-typeof-symbol/-/plugin-transform-typeof-symbol-7.12.13.tgz", + "integrity": "sha512-eKv/LmUJpMnu4npgfvs3LiHhJua5fo/CysENxa45YCQXZwKnGCQKAg87bvoqSW1fFT+HA32l03Qxsm8ouTY3ZQ==", "dev": true, "requires": { - "@babel/helper-plugin-utils": "^7.10.4" + "@babel/helper-plugin-utils": "^7.12.13" } }, "@babel/plugin-transform-typescript": { - "version": "7.12.1", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-typescript/-/plugin-transform-typescript-7.12.1.tgz", - "integrity": "sha512-VrsBByqAIntM+EYMqSm59SiMEf7qkmI9dqMt6RbD/wlwueWmYcI0FFK5Fj47pP6DRZm+3teXjosKlwcZJ5lIMw==", + "version": "7.13.0", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-typescript/-/plugin-transform-typescript-7.13.0.tgz", + "integrity": "sha512-elQEwluzaU8R8dbVuW2Q2Y8Nznf7hnjM7+DSCd14Lo5fF63C9qNLbwZYbmZrtV9/ySpSUpkRpQXvJb6xyu4hCQ==", "dev": true, "requires": { - "@babel/helper-create-class-features-plugin": "^7.12.1", - "@babel/helper-plugin-utils": "^7.10.4", - "@babel/plugin-syntax-typescript": "^7.12.1" + "@babel/helper-create-class-features-plugin": "^7.13.0", + "@babel/helper-plugin-utils": "^7.13.0", + "@babel/plugin-syntax-typescript": "^7.12.13" } }, "@babel/plugin-transform-unicode-escapes": { - "version": "7.12.1", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-unicode-escapes/-/plugin-transform-unicode-escapes-7.12.1.tgz", - "integrity": "sha512-I8gNHJLIc7GdApm7wkVnStWssPNbSRMPtgHdmH3sRM1zopz09UWPS4x5V4n1yz/MIWTVnJ9sp6IkuXdWM4w+2Q==", + "version": "7.12.13", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-unicode-escapes/-/plugin-transform-unicode-escapes-7.12.13.tgz", + "integrity": "sha512-0bHEkdwJ/sN/ikBHfSmOXPypN/beiGqjo+o4/5K+vxEFNPRPdImhviPakMKG4x96l85emoa0Z6cDflsdBusZbw==", "dev": true, "requires": { - "@babel/helper-plugin-utils": "^7.10.4" + "@babel/helper-plugin-utils": "^7.12.13" } }, "@babel/plugin-transform-unicode-regex": { - "version": "7.12.1", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-unicode-regex/-/plugin-transform-unicode-regex-7.12.1.tgz", - "integrity": "sha512-SqH4ClNngh/zGwHZOOQMTD+e8FGWexILV+ePMyiDJttAWRh5dhDL8rcl5lSgU3Huiq6Zn6pWTMvdPAb21Dwdyg==", + "version": "7.12.13", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-unicode-regex/-/plugin-transform-unicode-regex-7.12.13.tgz", + "integrity": "sha512-mDRzSNY7/zopwisPZ5kM9XKCfhchqIYwAKRERtEnhYscZB79VRekuRSoYbN0+KVe3y8+q1h6A4svXtP7N+UoCA==", "dev": true, "requires": { - "@babel/helper-create-regexp-features-plugin": "^7.12.1", - "@babel/helper-plugin-utils": "^7.10.4" + "@babel/helper-create-regexp-features-plugin": "^7.12.13", + "@babel/helper-plugin-utils": "^7.12.13" } }, "@babel/preset-env": { - "version": "7.12.11", - "resolved": "https://registry.npmjs.org/@babel/preset-env/-/preset-env-7.12.11.tgz", - "integrity": "sha512-j8Tb+KKIXKYlDBQyIOy4BLxzv1NUOwlHfZ74rvW+Z0Gp4/cI2IMDPBWAgWceGcE7aep9oL/0K9mlzlMGxA8yNw==", - "dev": true, - "requires": { - "@babel/compat-data": "^7.12.7", - "@babel/helper-compilation-targets": "^7.12.5", - "@babel/helper-module-imports": "^7.12.5", - "@babel/helper-plugin-utils": "^7.10.4", - "@babel/helper-validator-option": "^7.12.11", - "@babel/plugin-proposal-async-generator-functions": "^7.12.1", - "@babel/plugin-proposal-class-properties": "^7.12.1", - "@babel/plugin-proposal-dynamic-import": "^7.12.1", - "@babel/plugin-proposal-export-namespace-from": "^7.12.1", - "@babel/plugin-proposal-json-strings": "^7.12.1", - "@babel/plugin-proposal-logical-assignment-operators": "^7.12.1", - "@babel/plugin-proposal-nullish-coalescing-operator": "^7.12.1", - "@babel/plugin-proposal-numeric-separator": "^7.12.7", - "@babel/plugin-proposal-object-rest-spread": "^7.12.1", - "@babel/plugin-proposal-optional-catch-binding": "^7.12.1", - "@babel/plugin-proposal-optional-chaining": "^7.12.7", - "@babel/plugin-proposal-private-methods": "^7.12.1", - "@babel/plugin-proposal-unicode-property-regex": "^7.12.1", - "@babel/plugin-syntax-async-generators": "^7.8.0", - "@babel/plugin-syntax-class-properties": "^7.12.1", - "@babel/plugin-syntax-dynamic-import": "^7.8.0", + "version": "7.13.9", + "resolved": "https://registry.npmjs.org/@babel/preset-env/-/preset-env-7.13.9.tgz", + "integrity": "sha512-mcsHUlh2rIhViqMG823JpscLMesRt3QbMsv1+jhopXEb3W2wXvQ9QoiOlZI9ZbR3XqPtaFpZwEZKYqGJnGMZTQ==", + "dev": true, + "requires": { + "@babel/compat-data": "^7.13.8", + "@babel/helper-compilation-targets": "^7.13.8", + "@babel/helper-plugin-utils": "^7.13.0", + "@babel/helper-validator-option": "^7.12.17", + "@babel/plugin-proposal-async-generator-functions": "^7.13.8", + "@babel/plugin-proposal-class-properties": "^7.13.0", + "@babel/plugin-proposal-dynamic-import": "^7.13.8", + "@babel/plugin-proposal-export-namespace-from": "^7.12.13", + "@babel/plugin-proposal-json-strings": "^7.13.8", + "@babel/plugin-proposal-logical-assignment-operators": "^7.13.8", + "@babel/plugin-proposal-nullish-coalescing-operator": "^7.13.8", + "@babel/plugin-proposal-numeric-separator": "^7.12.13", + "@babel/plugin-proposal-object-rest-spread": "^7.13.8", + "@babel/plugin-proposal-optional-catch-binding": "^7.13.8", + "@babel/plugin-proposal-optional-chaining": "^7.13.8", + "@babel/plugin-proposal-private-methods": "^7.13.0", + "@babel/plugin-proposal-unicode-property-regex": "^7.12.13", + "@babel/plugin-syntax-async-generators": "^7.8.4", + "@babel/plugin-syntax-class-properties": "^7.12.13", + "@babel/plugin-syntax-dynamic-import": "^7.8.3", "@babel/plugin-syntax-export-namespace-from": "^7.8.3", - "@babel/plugin-syntax-json-strings": "^7.8.0", + "@babel/plugin-syntax-json-strings": "^7.8.3", "@babel/plugin-syntax-logical-assignment-operators": "^7.10.4", - "@babel/plugin-syntax-nullish-coalescing-operator": "^7.8.0", + "@babel/plugin-syntax-nullish-coalescing-operator": "^7.8.3", "@babel/plugin-syntax-numeric-separator": "^7.10.4", - "@babel/plugin-syntax-object-rest-spread": "^7.8.0", - "@babel/plugin-syntax-optional-catch-binding": "^7.8.0", - "@babel/plugin-syntax-optional-chaining": "^7.8.0", - "@babel/plugin-syntax-top-level-await": "^7.12.1", - "@babel/plugin-transform-arrow-functions": "^7.12.1", - "@babel/plugin-transform-async-to-generator": "^7.12.1", - "@babel/plugin-transform-block-scoped-functions": "^7.12.1", - "@babel/plugin-transform-block-scoping": "^7.12.11", - "@babel/plugin-transform-classes": "^7.12.1", - "@babel/plugin-transform-computed-properties": "^7.12.1", - "@babel/plugin-transform-destructuring": "^7.12.1", - "@babel/plugin-transform-dotall-regex": "^7.12.1", - "@babel/plugin-transform-duplicate-keys": "^7.12.1", - "@babel/plugin-transform-exponentiation-operator": "^7.12.1", - "@babel/plugin-transform-for-of": "^7.12.1", - "@babel/plugin-transform-function-name": "^7.12.1", - "@babel/plugin-transform-literals": "^7.12.1", - "@babel/plugin-transform-member-expression-literals": "^7.12.1", - "@babel/plugin-transform-modules-amd": "^7.12.1", - "@babel/plugin-transform-modules-commonjs": "^7.12.1", - "@babel/plugin-transform-modules-systemjs": "^7.12.1", - "@babel/plugin-transform-modules-umd": "^7.12.1", - "@babel/plugin-transform-named-capturing-groups-regex": "^7.12.1", - "@babel/plugin-transform-new-target": "^7.12.1", - "@babel/plugin-transform-object-super": "^7.12.1", - "@babel/plugin-transform-parameters": "^7.12.1", - "@babel/plugin-transform-property-literals": "^7.12.1", - "@babel/plugin-transform-regenerator": "^7.12.1", - "@babel/plugin-transform-reserved-words": "^7.12.1", - "@babel/plugin-transform-shorthand-properties": "^7.12.1", - "@babel/plugin-transform-spread": "^7.12.1", - "@babel/plugin-transform-sticky-regex": "^7.12.7", - "@babel/plugin-transform-template-literals": "^7.12.1", - "@babel/plugin-transform-typeof-symbol": "^7.12.10", - "@babel/plugin-transform-unicode-escapes": "^7.12.1", - "@babel/plugin-transform-unicode-regex": "^7.12.1", - "@babel/preset-modules": "^0.1.3", - "@babel/types": "^7.12.11", - "core-js-compat": "^3.8.0", - "semver": "^5.5.0" + "@babel/plugin-syntax-object-rest-spread": "^7.8.3", + "@babel/plugin-syntax-optional-catch-binding": "^7.8.3", + "@babel/plugin-syntax-optional-chaining": "^7.8.3", + "@babel/plugin-syntax-top-level-await": "^7.12.13", + "@babel/plugin-transform-arrow-functions": "^7.13.0", + "@babel/plugin-transform-async-to-generator": "^7.13.0", + "@babel/plugin-transform-block-scoped-functions": "^7.12.13", + "@babel/plugin-transform-block-scoping": "^7.12.13", + "@babel/plugin-transform-classes": "^7.13.0", + "@babel/plugin-transform-computed-properties": "^7.13.0", + "@babel/plugin-transform-destructuring": "^7.13.0", + "@babel/plugin-transform-dotall-regex": "^7.12.13", + "@babel/plugin-transform-duplicate-keys": "^7.12.13", + "@babel/plugin-transform-exponentiation-operator": "^7.12.13", + "@babel/plugin-transform-for-of": "^7.13.0", + "@babel/plugin-transform-function-name": "^7.12.13", + "@babel/plugin-transform-literals": "^7.12.13", + "@babel/plugin-transform-member-expression-literals": "^7.12.13", + "@babel/plugin-transform-modules-amd": "^7.13.0", + "@babel/plugin-transform-modules-commonjs": "^7.13.8", + "@babel/plugin-transform-modules-systemjs": "^7.13.8", + "@babel/plugin-transform-modules-umd": "^7.13.0", + "@babel/plugin-transform-named-capturing-groups-regex": "^7.12.13", + "@babel/plugin-transform-new-target": "^7.12.13", + "@babel/plugin-transform-object-super": "^7.12.13", + "@babel/plugin-transform-parameters": "^7.13.0", + "@babel/plugin-transform-property-literals": "^7.12.13", + "@babel/plugin-transform-regenerator": "^7.12.13", + "@babel/plugin-transform-reserved-words": "^7.12.13", + "@babel/plugin-transform-shorthand-properties": "^7.12.13", + "@babel/plugin-transform-spread": "^7.13.0", + "@babel/plugin-transform-sticky-regex": "^7.12.13", + "@babel/plugin-transform-template-literals": "^7.13.0", + "@babel/plugin-transform-typeof-symbol": "^7.12.13", + "@babel/plugin-transform-unicode-escapes": "^7.12.13", + "@babel/plugin-transform-unicode-regex": "^7.12.13", + "@babel/preset-modules": "^0.1.4", + "@babel/types": "^7.13.0", + "babel-plugin-polyfill-corejs2": "^0.1.4", + "babel-plugin-polyfill-corejs3": "^0.1.3", + "babel-plugin-polyfill-regenerator": "^0.1.2", + "core-js-compat": "^3.9.0", + "semver": "^6.3.0" + }, + "dependencies": { + "semver": { + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", + "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==", + "dev": true + } } }, "@babel/preset-modules": { @@ -1138,69 +1193,69 @@ } }, "@babel/preset-react": { - "version": "7.12.10", - "resolved": "https://registry.npmjs.org/@babel/preset-react/-/preset-react-7.12.10.tgz", - "integrity": "sha512-vtQNjaHRl4DUpp+t+g4wvTHsLQuye+n0H/wsXIZRn69oz/fvNC7gQ4IK73zGJBaxvHoxElDvnYCthMcT7uzFoQ==", + "version": "7.12.13", + "resolved": "https://registry.npmjs.org/@babel/preset-react/-/preset-react-7.12.13.tgz", + "integrity": "sha512-TYM0V9z6Abb6dj1K7i5NrEhA13oS5ujUYQYDfqIBXYHOc2c2VkFgc+q9kyssIyUfy4/hEwqrgSlJ/Qgv8zJLsA==", "dev": true, "requires": { - "@babel/helper-plugin-utils": "^7.10.4", - "@babel/plugin-transform-react-display-name": "^7.12.1", - "@babel/plugin-transform-react-jsx": "^7.12.10", - "@babel/plugin-transform-react-jsx-development": "^7.12.7", + "@babel/helper-plugin-utils": "^7.12.13", + "@babel/plugin-transform-react-display-name": "^7.12.13", + "@babel/plugin-transform-react-jsx": "^7.12.13", + "@babel/plugin-transform-react-jsx-development": "^7.12.12", "@babel/plugin-transform-react-pure-annotations": "^7.12.1" } }, "@babel/preset-typescript": { - "version": "7.12.7", - "resolved": "https://registry.npmjs.org/@babel/preset-typescript/-/preset-typescript-7.12.7.tgz", - "integrity": "sha512-nOoIqIqBmHBSEgBXWR4Dv/XBehtIFcw9PqZw6rFYuKrzsZmOQm3PR5siLBnKZFEsDb03IegG8nSjU/iXXXYRmw==", + "version": "7.13.0", + "resolved": "https://registry.npmjs.org/@babel/preset-typescript/-/preset-typescript-7.13.0.tgz", + "integrity": "sha512-LXJwxrHy0N3f6gIJlYbLta1D9BDtHpQeqwzM0LIfjDlr6UE/D5Mc7W4iDiQzaE+ks0sTjT26ArcHWnJVt0QiHw==", "dev": true, "requires": { - "@babel/helper-plugin-utils": "^7.10.4", - "@babel/helper-validator-option": "^7.12.1", - "@babel/plugin-transform-typescript": "^7.12.1" + "@babel/helper-plugin-utils": "^7.13.0", + "@babel/helper-validator-option": "^7.12.17", + "@babel/plugin-transform-typescript": "^7.13.0" } }, "@babel/runtime": { - "version": "7.12.5", - "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.12.5.tgz", - "integrity": "sha512-plcc+hbExy3McchJCEQG3knOsuh3HH+Prx1P6cLIkET/0dLuQDEnrT+s27Axgc9bqfsmNUNHfscgMUdBpC9xfg==", + "version": "7.13.9", + "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.13.9.tgz", + "integrity": "sha512-aY2kU+xgJ3dJ1eU6FMB9EH8dIe8dmusF1xEku52joLvw6eAFN0AI+WxCLDnpev2LEejWBAy2sBvBOBAjI3zmvA==", "requires": { "regenerator-runtime": "^0.13.4" } }, "@babel/runtime-corejs3": { - "version": "7.12.5", - "resolved": "https://registry.npmjs.org/@babel/runtime-corejs3/-/runtime-corejs3-7.12.5.tgz", - "integrity": "sha512-roGr54CsTmNPPzZoCP1AmDXuBoNao7tnSA83TXTwt+UK5QVyh1DIJnrgYRPWKCF2flqZQXwa7Yr8v7VmLzF0YQ==", + "version": "7.13.9", + "resolved": "https://registry.npmjs.org/@babel/runtime-corejs3/-/runtime-corejs3-7.13.9.tgz", + "integrity": "sha512-p6WSr71+5u/VBf1KDS/Y4dK3ZwbV+DD6wQO3X2EbUVluEOiyXUk09DzcwSaUH4WomYXrEPC+i2rqzuthhZhOJw==", "requires": { "core-js-pure": "^3.0.0", "regenerator-runtime": "^0.13.4" } }, "@babel/template": { - "version": "7.12.7", - "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.12.7.tgz", - "integrity": "sha512-GkDzmHS6GV7ZeXfJZ0tLRBhZcMcY0/Lnb+eEbXDBfCAcZCjrZKe6p3J4we/D24O9Y8enxWAg1cWwof59yLh2ow==", + "version": "7.12.13", + "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.12.13.tgz", + "integrity": "sha512-/7xxiGA57xMo/P2GVvdEumr8ONhFOhfgq2ihK3h1e6THqzTAkHbkXgB0xI9yeTfIUoH3+oAeHhqm/I43OTbbjA==", "dev": true, "requires": { - "@babel/code-frame": "^7.10.4", - "@babel/parser": "^7.12.7", - "@babel/types": "^7.12.7" + "@babel/code-frame": "^7.12.13", + "@babel/parser": "^7.12.13", + "@babel/types": "^7.12.13" } }, "@babel/traverse": { - "version": "7.12.12", - "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.12.12.tgz", - "integrity": "sha512-s88i0X0lPy45RrLM8b9mz8RPH5FqO9G9p7ti59cToE44xFm1Q+Pjh5Gq4SXBbtb88X7Uy7pexeqRIQDDMNkL0w==", + "version": "7.13.0", + "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.13.0.tgz", + "integrity": "sha512-xys5xi5JEhzC3RzEmSGrs/b3pJW/o87SypZ+G/PhaE7uqVQNv/jlmVIBXuoh5atqQ434LfXV+sf23Oxj0bchJQ==", "dev": true, "requires": { - "@babel/code-frame": "^7.12.11", - "@babel/generator": "^7.12.11", - "@babel/helper-function-name": "^7.12.11", - "@babel/helper-split-export-declaration": "^7.12.11", - "@babel/parser": "^7.12.11", - "@babel/types": "^7.12.12", + "@babel/code-frame": "^7.12.13", + "@babel/generator": "^7.13.0", + "@babel/helper-function-name": "^7.12.13", + "@babel/helper-split-export-declaration": "^7.12.13", + "@babel/parser": "^7.13.0", + "@babel/types": "^7.13.0", "debug": "^4.1.0", "globals": "^11.1.0", "lodash": "^4.17.19" @@ -1224,9 +1279,9 @@ } }, "@babel/types": { - "version": "7.12.12", - "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.12.12.tgz", - "integrity": "sha512-lnIX7piTxOH22xE7fDXDbSHg9MM1/6ORnafpJmov5rs0kX5g4BZxeXNJLXsMRiO0U5Rb8/FvMS6xlTnTHvxonQ==", + "version": "7.13.0", + "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.13.0.tgz", + "integrity": "sha512-hE+HE8rnG1Z6Wzo+MhaKE5lM5eMx71T4EHJgku2E3xIfaULhDcxiiRxUYgwX8qwP1BBSlag+TdGOt6JAidIZTA==", "requires": { "@babel/helper-validator-identifier": "^7.12.11", "lodash": "^4.17.19", @@ -1908,33 +1963,12 @@ "@types/yargs-parser": "*" } }, - "babel-plugin-istanbul": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/babel-plugin-istanbul/-/babel-plugin-istanbul-5.2.0.tgz", - "integrity": "sha512-5LphC0USA8t4i1zCtjbbNb6jJj/9+X6P37Qfirc/70EQ34xKlMW+a1RHGwxGI+SwWpNwZ27HqvzAobeqaXwiZw==", - "dev": true, - "requires": { - "@babel/helper-plugin-utils": "^7.0.0", - "find-up": "^3.0.0", - "istanbul-lib-instrument": "^3.3.0", - "test-exclude": "^5.2.3" - } - }, "escape-string-regexp": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-2.0.0.tgz", "integrity": "sha512-UpzcLCXolUWcNu5HtVMHYdXJjArjsF9C0aNnquZYY4uW/Vu0miy5YoWvbV345HauVvcAUnpRuhMMcqTcGOY2+w==", "dev": true }, - "find-up": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/find-up/-/find-up-3.0.0.tgz", - "integrity": "sha512-1yD6RmLI1XBfxugvORwlck6f75tYL+iR0jqwsOrOxMZyGYqUuDhJ0l4AXdO1iX/FTs9cBAMEk1gWSEx1kSbylg==", - "dev": true, - "requires": { - "locate-path": "^3.0.0" - } - }, "jest-message-util": { "version": "24.9.0", "resolved": "https://registry.npmjs.org/jest-message-util/-/jest-message-util-24.9.0.tgz", @@ -1986,98 +2020,6 @@ "source-map": "^0.6.0" } }, - "load-json-file": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/load-json-file/-/load-json-file-4.0.0.tgz", - "integrity": "sha1-L19Fq5HjMhYjT9U62rZo607AmTs=", - "dev": true, - "requires": { - "graceful-fs": "^4.1.2", - "parse-json": "^4.0.0", - "pify": "^3.0.0", - "strip-bom": "^3.0.0" - } - }, - "locate-path": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-3.0.0.tgz", - "integrity": "sha512-7AO748wWnIhNqAuaty2ZWHkQHRSNfPVIsPIfwEOWO22AmaoVrWavlOcMR5nzTLNYvp36X220/maaRsrec1G65A==", - "dev": true, - "requires": { - "p-locate": "^3.0.0", - "path-exists": "^3.0.0" - } - }, - "p-limit": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.3.0.tgz", - "integrity": "sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w==", - "dev": true, - "requires": { - "p-try": "^2.0.0" - } - }, - "p-locate": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-3.0.0.tgz", - "integrity": "sha512-x+12w/To+4GFfgJhBEpiDcLozRJGegY+Ei7/z0tSLkMmxGZNybVMSfWj9aJn8Z5Fc7dBUNJOOVgPv2H7IwulSQ==", - "dev": true, - "requires": { - "p-limit": "^2.0.0" - } - }, - "p-try": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/p-try/-/p-try-2.2.0.tgz", - "integrity": "sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ==", - "dev": true - }, - "parse-json": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/parse-json/-/parse-json-4.0.0.tgz", - "integrity": "sha1-vjX1Qlvh9/bHRxhPmKeIy5lHfuA=", - "dev": true, - "requires": { - "error-ex": "^1.3.1", - "json-parse-better-errors": "^1.0.1" - } - }, - "path-type": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/path-type/-/path-type-3.0.0.tgz", - "integrity": "sha512-T2ZUsdZFHgA3u4e5PfPbjd7HDDpxPnQb5jN0SrDsjNSuVXHJqtwTnWqG0B1jZrgmJ/7lj1EmVIByWt1gxGkWvg==", - "dev": true, - "requires": { - "pify": "^3.0.0" - } - }, - "pify": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/pify/-/pify-3.0.0.tgz", - "integrity": "sha1-5aSs0sEB/fPZpNB/DbxNtJ3SgXY=", - "dev": true - }, - "read-pkg": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/read-pkg/-/read-pkg-3.0.0.tgz", - "integrity": "sha1-nLxoaXj+5l0WwA4rGcI3/Pbjg4k=", - "dev": true, - "requires": { - "load-json-file": "^4.0.0", - "normalize-package-data": "^2.3.2", - "path-type": "^3.0.0" - } - }, - "read-pkg-up": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/read-pkg-up/-/read-pkg-up-4.0.0.tgz", - "integrity": "sha512-6etQSH7nJGsK0RbG/2TeDzZFa8shjQ1um+SwQQ5cwKy0dhSXdOncEhb1CPpvQG4h7FyOV6EB6YlV0yJvZQNAkA==", - "dev": true, - "requires": { - "find-up": "^3.0.0", - "read-pkg": "^3.0.0" - } - }, "source-map": { "version": "0.6.1", "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", @@ -2092,18 +2034,6 @@ "requires": { "escape-string-regexp": "^2.0.0" } - }, - "test-exclude": { - "version": "5.2.3", - "resolved": "https://registry.npmjs.org/test-exclude/-/test-exclude-5.2.3.tgz", - "integrity": "sha512-M+oxtseCFO3EDtAaGH7iiej3CBkzXqFMbzqYAACdzKui4eZA+pq3tZEwChvOdNfa7xxy8BfbmgJSIr43cC/+2g==", - "dev": true, - "requires": { - "glob": "^7.1.3", - "minimatch": "^3.0.4", - "read-pkg-up": "^4.0.0", - "require-main-filename": "^2.0.0" - } } } }, @@ -2172,13 +2102,13 @@ } }, "@material-ui/core": { - "version": "4.11.2", - "resolved": "https://registry.npmjs.org/@material-ui/core/-/core-4.11.2.tgz", - "integrity": "sha512-/D1+AQQeYX/WhT/FUk78UCRj8ch/RCglsQLYujYTIqPSJlwZHKcvHidNeVhODXeApojeXjkl0tWdk5C9ofwOkQ==", + "version": "4.11.3", + "resolved": "https://registry.npmjs.org/@material-ui/core/-/core-4.11.3.tgz", + "integrity": "sha512-Adt40rGW6Uds+cAyk3pVgcErpzU/qxc7KBR94jFHBYretU4AtWZltYcNsbeMn9tXL86jjVL1kuGcIHsgLgFGRw==", "requires": { "@babel/runtime": "^7.4.4", - "@material-ui/styles": "^4.11.2", - "@material-ui/system": "^4.11.2", + "@material-ui/styles": "^4.11.3", + "@material-ui/system": "^4.11.3", "@material-ui/types": "^5.1.0", "@material-ui/utils": "^4.11.2", "@types/react-transition-group": "^4.2.0", @@ -2224,9 +2154,9 @@ } }, "@material-ui/styles": { - "version": "4.11.2", - "resolved": "https://registry.npmjs.org/@material-ui/styles/-/styles-4.11.2.tgz", - "integrity": "sha512-xbItf8zkfD3FuGoD9f2vlcyPf9jTEtj9YTJoNNV+NMWaSAHXgrW6geqRoo/IwBuMjqpwqsZhct13e2nUyU9Ljw==", + "version": "4.11.3", + "resolved": "https://registry.npmjs.org/@material-ui/styles/-/styles-4.11.3.tgz", + "integrity": "sha512-HzVzCG+PpgUGMUYEJ2rTEmQYeonGh41BYfILNFb/1ueqma+p1meSdu4RX6NjxYBMhf7k+jgfHFTTz+L1SXL/Zg==", "requires": { "@babel/runtime": "^7.4.4", "@emotion/hash": "^0.8.0", @@ -2235,21 +2165,21 @@ "clsx": "^1.0.4", "csstype": "^2.5.2", "hoist-non-react-statics": "^3.3.2", - "jss": "^10.0.3", - "jss-plugin-camel-case": "^10.0.3", - "jss-plugin-default-unit": "^10.0.3", - "jss-plugin-global": "^10.0.3", - "jss-plugin-nested": "^10.0.3", - "jss-plugin-props-sort": "^10.0.3", - "jss-plugin-rule-value-function": "^10.0.3", - "jss-plugin-vendor-prefixer": "^10.0.3", + "jss": "^10.5.1", + "jss-plugin-camel-case": "^10.5.1", + "jss-plugin-default-unit": "^10.5.1", + "jss-plugin-global": "^10.5.1", + "jss-plugin-nested": "^10.5.1", + "jss-plugin-props-sort": "^10.5.1", + "jss-plugin-rule-value-function": "^10.5.1", + "jss-plugin-vendor-prefixer": "^10.5.1", "prop-types": "^15.7.2" } }, "@material-ui/system": { - "version": "4.11.2", - "resolved": "https://registry.npmjs.org/@material-ui/system/-/system-4.11.2.tgz", - "integrity": "sha512-BELFJEel5E+5DMiZb6XXT3peWRn6UixRvBtKwSxqntmD0+zwbbfCij6jtGwwdJhN1qX/aXrKu10zX31GBaeR7A==", + "version": "4.11.3", + "resolved": "https://registry.npmjs.org/@material-ui/system/-/system-4.11.3.tgz", + "integrity": "sha512-SY7otguNGol41Mu2Sg6KbBP1ZRFIbFLHGK81y4KYbsV2yIcaEPOmsCK6zwWlp+2yTV3J/VwT6oSBARtGIVdXPw==", "requires": { "@babel/runtime": "^7.4.4", "@material-ui/utils": "^4.11.2", @@ -2566,9 +2496,9 @@ } }, "@testing-library/react-hooks": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/@testing-library/react-hooks/-/react-hooks-5.0.0.tgz", - "integrity": "sha512-c/wvcz/Set+KOvbi07EQO7tujsUIp5HnNAygJoSpMTVkIDcp7JtSemhjRDg1WL6Qsw076inWobTKCepK3mgi8A==", + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/@testing-library/react-hooks/-/react-hooks-5.1.0.tgz", + "integrity": "sha512-ChRyyA14e0CeVkWGp24v8q/IiWUqH+B8daRx4lGZme4dsudmMNWz+Qo2Q2NzbD2O5rAVXh2hSbS/KTKeqHYhkw==", "dev": true, "requires": { "@babel/runtime": "^7.12.5", @@ -2676,9 +2606,9 @@ } }, "@types/json-schema": { - "version": "7.0.6", - "resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.6.tgz", - "integrity": "sha512-3c+yGKvVP5Y9TYBEibGNR+kLtijnj7mYrXRg+WpFb2X9xm04g/DXYkfg4hmzJQosc9snFNUPkbYIhu+KAm6jJw==", + "version": "7.0.7", + "resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.7.tgz", + "integrity": "sha512-cxWFQVseBm6O9Gbw1IWb8r6OS4OhSt3hPZLkFApLjM8TEXROBuQGLAH2i2gZpcXdLBIrpXuTDhH7Vbm1iXmNGA==", "dev": true }, "@types/json5": { @@ -2694,9 +2624,9 @@ "dev": true }, "@types/node": { - "version": "14.14.20", - "resolved": "https://registry.npmjs.org/@types/node/-/node-14.14.20.tgz", - "integrity": "sha512-Y93R97Ouif9JEOWPIUyU+eyIdyRqQR0I8Ez1dzku4hDx34NWh4HbtIc3WNzwB1Y9ULvNGeu5B8h8bVL5cAk4/A==", + "version": "14.14.32", + "resolved": "https://registry.npmjs.org/@types/node/-/node-14.14.32.tgz", + "integrity": "sha512-/Ctrftx/zp4m8JOujM5ZhwzlWLx22nbQJiVqz8/zE15gOeEW+uly3FSX4fGFpcfEvFzXcMCJwq9lGVWgyARXhg==", "dev": true }, "@types/parse-json": { @@ -2722,34 +2652,35 @@ "optional": true }, "@types/react": { - "version": "17.0.0", - "resolved": "https://registry.npmjs.org/@types/react/-/react-17.0.0.tgz", - "integrity": "sha512-aj/L7RIMsRlWML3YB6KZiXB3fV2t41+5RBGYF8z+tAKU43Px8C3cYUZsDvf1/+Bm4FK21QWBrDutu8ZJ/70qOw==", + "version": "17.0.3", + "resolved": "https://registry.npmjs.org/@types/react/-/react-17.0.3.tgz", + "integrity": "sha512-wYOUxIgs2HZZ0ACNiIayItyluADNbONl7kt8lkLjVK8IitMH5QMyAh75Fwhmo37r1m7L2JaFj03sIfxBVDvRAg==", "requires": { "@types/prop-types": "*", + "@types/scheduler": "*", "csstype": "^3.0.2" }, "dependencies": { "csstype": { - "version": "3.0.6", - "resolved": "https://registry.npmjs.org/csstype/-/csstype-3.0.6.tgz", - "integrity": "sha512-+ZAmfyWMT7TiIlzdqJgjMb7S4f1beorDbWbsocyK4RaiqA5RTX3K14bnBWmmA9QEM0gRdsjyyrEmcyga8Zsxmw==" + "version": "3.0.7", + "resolved": "https://registry.npmjs.org/csstype/-/csstype-3.0.7.tgz", + "integrity": "sha512-KxnUB0ZMlnUWCsx2Z8MUsr6qV6ja1w9ArPErJaJaF8a5SOWoHLIszeCTKGRGRgtLgYrs1E8CHkNSP1VZTTPc9g==" } } }, "@types/react-dom": { - "version": "17.0.0", - "resolved": "https://registry.npmjs.org/@types/react-dom/-/react-dom-17.0.0.tgz", - "integrity": "sha512-lUqY7OlkF/RbNtD5nIq7ot8NquXrdFrjSOR6+w9a9RFQevGi1oZO1dcJbXMeONAPKtZ2UrZOEJ5UOCVsxbLk/g==", + "version": "17.0.2", + "resolved": "https://registry.npmjs.org/@types/react-dom/-/react-dom-17.0.2.tgz", + "integrity": "sha512-Icd9KEgdnFfJs39KyRyr0jQ7EKhq8U6CcHRMGAS45fp5qgUvxL3ujUCfWFttUK2UErqZNj97t9gsVPNAqcwoCg==", "dev": true, "requires": { "@types/react": "*" } }, "@types/react-router": { - "version": "5.1.11", - "resolved": "https://registry.npmjs.org/@types/react-router/-/react-router-5.1.11.tgz", - "integrity": "sha512-ofHbZMlp0Y2baOHgsWBQ4K3AttxY61bDMkwTiBOkPg7U6C/3UwwB5WaIx28JmSVi/eX3uFEMRo61BV22fDQIvg==", + "version": "5.1.12", + "resolved": "https://registry.npmjs.org/@types/react-router/-/react-router-5.1.12.tgz", + "integrity": "sha512-0bhXQwHYfMeJlCh7mGhc0VJTRm0Gk+Z8T00aiP4702mDUuLs9SMhnd2DitpjWFjdOecx2UXtICK14H9iMnziGA==", "requires": { "@types/history": "*", "@types/react": "*" @@ -2766,22 +2697,27 @@ } }, "@types/react-test-renderer": { - "version": "17.0.0", - "resolved": "https://registry.npmjs.org/@types/react-test-renderer/-/react-test-renderer-17.0.0.tgz", - "integrity": "sha512-nvw+F81OmyzpyIE1S0xWpLonLUZCMewslPuA8BtjSKc5XEbn8zEQBXS7KuOLHTNnSOEM2Pum50gHOoZ62tqTRg==", + "version": "17.0.1", + "resolved": "https://registry.npmjs.org/@types/react-test-renderer/-/react-test-renderer-17.0.1.tgz", + "integrity": "sha512-3Fi2O6Zzq/f3QR9dRnlnHso9bMl7weKCviFmfF6B4LS1Uat6Hkm15k0ZAQuDz+UBq6B3+g+NM6IT2nr5QgPzCw==", "dev": true, "requires": { "@types/react": "*" } }, "@types/react-transition-group": { - "version": "4.4.0", - "resolved": "https://registry.npmjs.org/@types/react-transition-group/-/react-transition-group-4.4.0.tgz", - "integrity": "sha512-/QfLHGpu+2fQOqQaXh8MG9q03bFENooTb/it4jr5kKaZlDQfWvjqWZg48AwzPVMBHlRuTRAY7hRHCEOXz5kV6w==", + "version": "4.4.1", + "resolved": "https://registry.npmjs.org/@types/react-transition-group/-/react-transition-group-4.4.1.tgz", + "integrity": "sha512-vIo69qKKcYoJ8wKCJjwSgCTM+z3chw3g18dkrDfVX665tMH7tmbDxEAnPdey4gTlwZz5QuHGzd+hul0OVZDqqQ==", "requires": { "@types/react": "*" } }, + "@types/scheduler": { + "version": "0.16.1", + "resolved": "https://registry.npmjs.org/@types/scheduler/-/scheduler-0.16.1.tgz", + "integrity": "sha512-EaCxbanVeyxDRTQBkdLb3Bvl/HK7PBK6UJjsSixB0iHKoWxE5uu2Q/DgtpOhPIojN0Zl1whvOd7PoHs2P0s5eA==" + }, "@types/sockjs-client": { "version": "1.5.0", "resolved": "https://registry.npmjs.org/@types/sockjs-client/-/sockjs-client-1.5.0.tgz", @@ -2802,9 +2738,9 @@ } }, "@types/yargs": { - "version": "15.0.12", - "resolved": "https://registry.npmjs.org/@types/yargs/-/yargs-15.0.12.tgz", - "integrity": "sha512-f+fD/fQAo3BCbCDlrUpznF1A5Zp9rB0noS5vnoormHSIPFKL0Z2DcUJ3Gxp5ytH4uLRNxy7AwYUC9exZzqGMAw==", + "version": "15.0.13", + "resolved": "https://registry.npmjs.org/@types/yargs/-/yargs-15.0.13.tgz", + "integrity": "sha512-kQ5JNTrbDv3Rp5X2n/iUu37IJBDU2gsZ5R/g1/KHOOEc5IKfUFjXT6DENPGduh08I/pamwtEq4oul7gUqKTQDQ==", "dev": true, "requires": { "@types/yargs-parser": "*" @@ -3407,15 +3343,15 @@ "dev": true }, "array-includes": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/array-includes/-/array-includes-3.1.2.tgz", - "integrity": "sha512-w2GspexNQpx+PutG3QpT437/BenZBj0M/MZGn5mzv/MofYqo0xmRHzn4lFsoDlWJ+THYsGJmFlW68WlDFx7VRw==", + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/array-includes/-/array-includes-3.1.3.tgz", + "integrity": "sha512-gcem1KlBU7c9rB+Rq8/3PPKsK2kjqeEBa3bD5kkQo4nYlOHQCJqIJFqBXDEfwaRuYTT4E+FxA9xez7Gf/e3Q7A==", "dev": true, "requires": { - "call-bind": "^1.0.0", + "call-bind": "^1.0.2", "define-properties": "^1.1.3", - "es-abstract": "^1.18.0-next.1", - "get-intrinsic": "^1.0.1", + "es-abstract": "^1.18.0-next.2", + "get-intrinsic": "^1.1.1", "is-string": "^1.0.5" } }, @@ -3497,9 +3433,9 @@ }, "dependencies": { "bn.js": { - "version": "4.11.9", - "resolved": "https://registry.npmjs.org/bn.js/-/bn.js-4.11.9.tgz", - "integrity": "sha512-E6QoYqCKZfgatHTdHzs1RRKP7ip4vvm+EyRUeE2RF0NblwVvb0p6jSVeNTOFxPn26QXN2o6SMfNxKp6kU8zQaw==", + "version": "4.12.0", + "resolved": "https://registry.npmjs.org/bn.js/-/bn.js-4.12.0.tgz", + "integrity": "sha512-c98Bf3tPniI+scsdk237ku1Dc3ujXQTSgyiPUDEOe7tRkhrqridvh8klBv0HCEso1OLOYcHuCv/cS6DNxKH+ZA==", "dev": true } } @@ -3615,9 +3551,9 @@ "dev": true }, "axe-core": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/axe-core/-/axe-core-4.1.1.tgz", - "integrity": "sha512-5Kgy8Cz6LPC9DJcNb3yjAXTu3XihQgEdnIg50c//zOC/MyLP0Clg+Y8Sh9ZjjnvBrDZU4DgXS9C3T9r4/scGZQ==", + "version": "4.1.3", + "resolved": "https://registry.npmjs.org/axe-core/-/axe-core-4.1.3.tgz", + "integrity": "sha512-vwPpH4Aj4122EW38mxO/fxhGKtwWTMLDIJfZ1He0Edbtjcfna/R3YB67yVhezUMzqc3Jr3+Ii50KRntlENL4xQ==", "dev": true }, "axobject-query": { @@ -3752,131 +3688,6 @@ "requires": { "@types/yargs-parser": "*" } - }, - "babel-plugin-istanbul": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/babel-plugin-istanbul/-/babel-plugin-istanbul-5.2.0.tgz", - "integrity": "sha512-5LphC0USA8t4i1zCtjbbNb6jJj/9+X6P37Qfirc/70EQ34xKlMW+a1RHGwxGI+SwWpNwZ27HqvzAobeqaXwiZw==", - "dev": true, - "requires": { - "@babel/helper-plugin-utils": "^7.0.0", - "find-up": "^3.0.0", - "istanbul-lib-instrument": "^3.3.0", - "test-exclude": "^5.2.3" - } - }, - "find-up": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/find-up/-/find-up-3.0.0.tgz", - "integrity": "sha512-1yD6RmLI1XBfxugvORwlck6f75tYL+iR0jqwsOrOxMZyGYqUuDhJ0l4AXdO1iX/FTs9cBAMEk1gWSEx1kSbylg==", - "dev": true, - "requires": { - "locate-path": "^3.0.0" - } - }, - "load-json-file": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/load-json-file/-/load-json-file-4.0.0.tgz", - "integrity": "sha1-L19Fq5HjMhYjT9U62rZo607AmTs=", - "dev": true, - "requires": { - "graceful-fs": "^4.1.2", - "parse-json": "^4.0.0", - "pify": "^3.0.0", - "strip-bom": "^3.0.0" - } - }, - "locate-path": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-3.0.0.tgz", - "integrity": "sha512-7AO748wWnIhNqAuaty2ZWHkQHRSNfPVIsPIfwEOWO22AmaoVrWavlOcMR5nzTLNYvp36X220/maaRsrec1G65A==", - "dev": true, - "requires": { - "p-locate": "^3.0.0", - "path-exists": "^3.0.0" - } - }, - "p-limit": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.3.0.tgz", - "integrity": "sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w==", - "dev": true, - "requires": { - "p-try": "^2.0.0" - } - }, - "p-locate": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-3.0.0.tgz", - "integrity": "sha512-x+12w/To+4GFfgJhBEpiDcLozRJGegY+Ei7/z0tSLkMmxGZNybVMSfWj9aJn8Z5Fc7dBUNJOOVgPv2H7IwulSQ==", - "dev": true, - "requires": { - "p-limit": "^2.0.0" - } - }, - "p-try": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/p-try/-/p-try-2.2.0.tgz", - "integrity": "sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ==", - "dev": true - }, - "parse-json": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/parse-json/-/parse-json-4.0.0.tgz", - "integrity": "sha1-vjX1Qlvh9/bHRxhPmKeIy5lHfuA=", - "dev": true, - "requires": { - "error-ex": "^1.3.1", - "json-parse-better-errors": "^1.0.1" - } - }, - "path-type": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/path-type/-/path-type-3.0.0.tgz", - "integrity": "sha512-T2ZUsdZFHgA3u4e5PfPbjd7HDDpxPnQb5jN0SrDsjNSuVXHJqtwTnWqG0B1jZrgmJ/7lj1EmVIByWt1gxGkWvg==", - "dev": true, - "requires": { - "pify": "^3.0.0" - } - }, - "pify": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/pify/-/pify-3.0.0.tgz", - "integrity": "sha1-5aSs0sEB/fPZpNB/DbxNtJ3SgXY=", - "dev": true - }, - "read-pkg": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/read-pkg/-/read-pkg-3.0.0.tgz", - "integrity": "sha1-nLxoaXj+5l0WwA4rGcI3/Pbjg4k=", - "dev": true, - "requires": { - "load-json-file": "^4.0.0", - "normalize-package-data": "^2.3.2", - "path-type": "^3.0.0" - } - }, - "read-pkg-up": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/read-pkg-up/-/read-pkg-up-4.0.0.tgz", - "integrity": "sha512-6etQSH7nJGsK0RbG/2TeDzZFa8shjQ1um+SwQQ5cwKy0dhSXdOncEhb1CPpvQG4h7FyOV6EB6YlV0yJvZQNAkA==", - "dev": true, - "requires": { - "find-up": "^3.0.0", - "read-pkg": "^3.0.0" - } - }, - "test-exclude": { - "version": "5.2.3", - "resolved": "https://registry.npmjs.org/test-exclude/-/test-exclude-5.2.3.tgz", - "integrity": "sha512-M+oxtseCFO3EDtAaGH7iiej3CBkzXqFMbzqYAACdzKui4eZA+pq3tZEwChvOdNfa7xxy8BfbmgJSIr43cC/+2g==", - "dev": true, - "requires": { - "glob": "^7.1.3", - "minimatch": "^3.0.4", - "read-pkg-up": "^4.0.0", - "require-main-filename": "^2.0.0" - } } } }, @@ -3936,9 +3747,9 @@ } }, "babel-plugin-emotion": { - "version": "10.0.33", - "resolved": "https://registry.npmjs.org/babel-plugin-emotion/-/babel-plugin-emotion-10.0.33.tgz", - "integrity": "sha512-bxZbTTGz0AJQDHm8k6Rf3RQJ8tX2scsfsRyKVgAbiUPUNIRtlK+7JxP+TAd1kRLABFxe0CFm2VdK4ePkoA9FxQ==", + "version": "10.2.2", + "resolved": "https://registry.npmjs.org/babel-plugin-emotion/-/babel-plugin-emotion-10.2.2.tgz", + "integrity": "sha512-SMSkGoqTbTyUTDeuVuPIWifPdUGkTk1Kf9BWRiXIOIcuyMfsdp2EjeiiFvOzX8NOBvEh/ypKYvUh2rkgAJMCLA==", "requires": { "@babel/helper-module-imports": "^7.0.0", "@emotion/hash": "0.8.0", @@ -3952,6 +3763,63 @@ "source-map": "^0.5.7" } }, + "babel-plugin-istanbul": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/babel-plugin-istanbul/-/babel-plugin-istanbul-5.2.0.tgz", + "integrity": "sha512-5LphC0USA8t4i1zCtjbbNb6jJj/9+X6P37Qfirc/70EQ34xKlMW+a1RHGwxGI+SwWpNwZ27HqvzAobeqaXwiZw==", + "dev": true, + "requires": { + "@babel/helper-plugin-utils": "^7.0.0", + "find-up": "^3.0.0", + "istanbul-lib-instrument": "^3.3.0", + "test-exclude": "^5.2.3" + }, + "dependencies": { + "find-up": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-3.0.0.tgz", + "integrity": "sha512-1yD6RmLI1XBfxugvORwlck6f75tYL+iR0jqwsOrOxMZyGYqUuDhJ0l4AXdO1iX/FTs9cBAMEk1gWSEx1kSbylg==", + "dev": true, + "requires": { + "locate-path": "^3.0.0" + } + }, + "locate-path": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-3.0.0.tgz", + "integrity": "sha512-7AO748wWnIhNqAuaty2ZWHkQHRSNfPVIsPIfwEOWO22AmaoVrWavlOcMR5nzTLNYvp36X220/maaRsrec1G65A==", + "dev": true, + "requires": { + "p-locate": "^3.0.0", + "path-exists": "^3.0.0" + } + }, + "p-limit": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.3.0.tgz", + "integrity": "sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w==", + "dev": true, + "requires": { + "p-try": "^2.0.0" + } + }, + "p-locate": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-3.0.0.tgz", + "integrity": "sha512-x+12w/To+4GFfgJhBEpiDcLozRJGegY+Ei7/z0tSLkMmxGZNybVMSfWj9aJn8Z5Fc7dBUNJOOVgPv2H7IwulSQ==", + "dev": true, + "requires": { + "p-limit": "^2.0.0" + } + }, + "p-try": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/p-try/-/p-try-2.2.0.tgz", + "integrity": "sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ==", + "dev": true + } + } + }, "babel-plugin-jest-hoist": { "version": "24.9.0", "resolved": "https://registry.npmjs.org/babel-plugin-jest-hoist/-/babel-plugin-jest-hoist-24.9.0.tgz", @@ -3977,6 +3845,44 @@ "integrity": "sha512-squySRkf+6JGnvjoUtDEjSREJEBirnXi9NqP6rjSYsylxQxqBTz+pkmf395i9E2zsvmYUaI40BHo6SqZUdydlw==", "dev": true }, + "babel-plugin-polyfill-corejs2": { + "version": "0.1.10", + "resolved": "https://registry.npmjs.org/babel-plugin-polyfill-corejs2/-/babel-plugin-polyfill-corejs2-0.1.10.tgz", + "integrity": "sha512-DO95wD4g0A8KRaHKi0D51NdGXzvpqVLnLu5BTvDlpqUEpTmeEtypgC1xqesORaWmiUOQI14UHKlzNd9iZ2G3ZA==", + "dev": true, + "requires": { + "@babel/compat-data": "^7.13.0", + "@babel/helper-define-polyfill-provider": "^0.1.5", + "semver": "^6.1.1" + }, + "dependencies": { + "semver": { + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", + "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==", + "dev": true + } + } + }, + "babel-plugin-polyfill-corejs3": { + "version": "0.1.7", + "resolved": "https://registry.npmjs.org/babel-plugin-polyfill-corejs3/-/babel-plugin-polyfill-corejs3-0.1.7.tgz", + "integrity": "sha512-u+gbS9bbPhZWEeyy1oR/YaaSpod/KDT07arZHb80aTpl8H5ZBq+uN1nN9/xtX7jQyfLdPfoqI4Rue/MQSWJquw==", + "dev": true, + "requires": { + "@babel/helper-define-polyfill-provider": "^0.1.5", + "core-js-compat": "^3.8.1" + } + }, + "babel-plugin-polyfill-regenerator": { + "version": "0.1.6", + "resolved": "https://registry.npmjs.org/babel-plugin-polyfill-regenerator/-/babel-plugin-polyfill-regenerator-0.1.6.tgz", + "integrity": "sha512-OUrYG9iKPKz8NxswXbRAdSwF0GhRdIEMTloQATJi4bDuFqrXaXcCUT/VGNrr8pBcjMh1RxZ7Xt9cytVJTJfvMg==", + "dev": true, + "requires": { + "@babel/helper-define-polyfill-provider": "^0.1.5" + } + }, "babel-plugin-syntax-jsx": { "version": "6.18.0", "resolved": "https://registry.npmjs.org/babel-plugin-syntax-jsx/-/babel-plugin-syntax-jsx-6.18.0.tgz", @@ -4362,9 +4268,9 @@ "dev": true }, "bn.js": { - "version": "5.1.3", - "resolved": "https://registry.npmjs.org/bn.js/-/bn.js-5.1.3.tgz", - "integrity": "sha512-GkTiFpjFtUzU9CbMeJ5iazkCzGL3jrhzerzZIuqLABjbwRaFt33I9tUdSNryIptM+RxDet6OKm2WnLXzW51KsQ==", + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/bn.js/-/bn.js-5.2.0.tgz", + "integrity": "sha512-D7iWRBvnZE8ecXiLj/9wbxH7Tk79fAh8IHaTNq1RWRixsS02W+5qS+iE9yq6RYl0asXx5tw0bLhmT5pIfbSquw==", "dev": true }, "body-parser": { @@ -4576,16 +4482,16 @@ } }, "browserslist": { - "version": "4.16.1", - "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.16.1.tgz", - "integrity": "sha512-UXhDrwqsNcpTYJBTZsbGATDxZbiVDsx6UjpmRUmtnP10pr8wAYr5LgFoEFw9ixriQH2mv/NX2SfGzE/o8GndLA==", + "version": "4.16.3", + "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.16.3.tgz", + "integrity": "sha512-vIyhWmIkULaq04Gt93txdh+j02yX/JzlyhLYbV3YQCn/zvES3JnY7TifHHvvr1w5hTDluNKMkV05cs4vy8Q7sw==", "dev": true, "requires": { - "caniuse-lite": "^1.0.30001173", + "caniuse-lite": "^1.0.30001181", "colorette": "^1.2.1", - "electron-to-chromium": "^1.3.634", + "electron-to-chromium": "^1.3.649", "escalade": "^3.1.1", - "node-releases": "^1.1.69" + "node-releases": "^1.1.70" } }, "bser": { @@ -4804,9 +4710,9 @@ } }, "caniuse-lite": { - "version": "1.0.30001177", - "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001177.tgz", - "integrity": "sha512-6Ld7t3ifCL02jTj3MxPMM5wAYjbo4h/TAQGFTgv1inihP1tWnWp8mxxT4ut4JBEHLbpFXEXJJQ119JCJTBkYDw==", + "version": "1.0.30001197", + "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001197.tgz", + "integrity": "sha512-8aE+sqBqtXz4G8g35Eg/XEaFr2N7rd/VQ6eABGBmNtcB8cN6qNJhMi6oSFy4UWWZgqgL3filHT8Nha4meu3tsw==", "dev": true }, "canvg": { @@ -4861,9 +4767,9 @@ "dev": true }, "chokidar": { - "version": "3.5.0", - "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.5.0.tgz", - "integrity": "sha512-JgQM9JS92ZbFR4P90EvmzNpSGhpPBGBSj10PILeDyYFwp4h2/D9OM03wsJ4zW1fEp4ka2DGrnUeD7FuvQ2aZ2Q==", + "version": "3.5.1", + "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.5.1.tgz", + "integrity": "sha512-9+s+Od+W0VJJzawDma/gvBNQqkTiqYTWLuZoyAsivsI4AaWTCzHG06/TMjsf1cYe9Cb97UCEhjz7HvnPk2p/tw==", "dev": true, "requires": { "anymatch": "~3.1.1", @@ -4911,9 +4817,9 @@ } }, "glob-parent": { - "version": "5.1.1", - "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.1.tgz", - "integrity": "sha512-FnI+VGOpnlGHWZxthPGR+QhR78fuiK0sNLkHQv+bL9fQi57lNNdquIbna/WrfROrolq8GK5Ek6BiMwqL/voRYQ==", + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz", + "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==", "dev": true, "requires": { "is-glob": "^4.0.1" @@ -5167,9 +5073,9 @@ "integrity": "sha1-p9BVi9icQveV3UIyj3QIMcpTvCU=" }, "color-string": { - "version": "1.5.4", - "resolved": "https://registry.npmjs.org/color-string/-/color-string-1.5.4.tgz", - "integrity": "sha512-57yF5yt8Xa3czSEW1jfQDE79Idk0+AkN/4KWad6tbdxUmAs3MvjxlWSWD4deYytcRfoZ9nhKyFl1kj5tBvidbw==", + "version": "1.5.5", + "resolved": "https://registry.npmjs.org/color-string/-/color-string-1.5.5.tgz", + "integrity": "sha512-jgIoum0OfQfq9Whcfc2z/VhCNcmQjWbey6qBX0vqt7YICflUmBCh9E9CiQD5GSJ+Uehixm3NUwHVhqUAWRivZg==", "dev": true, "requires": { "color-name": "^1.0.0", @@ -5177,9 +5083,9 @@ } }, "colorette": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/colorette/-/colorette-1.2.1.tgz", - "integrity": "sha512-puCDz0CzydiSYOrnXpz/PKd69zRrribezjtE9yd4zvytoRc8+RY/KJPvtPFKZS3E3wP6neGyMe0vOTlHO5L3Pw==", + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/colorette/-/colorette-1.2.2.tgz", + "integrity": "sha512-MKGMzyfeuutC/ZJ1cba9NqcNpfeqMUcYmyF1ZFY6/Cn7CNSAKx6a+s48sqLqyAiZuaP2TcqMhoo+dlwFnVxT9w==", "dev": true }, "combined-stream": { @@ -5448,17 +5354,17 @@ } }, "core-js": { - "version": "3.8.2", - "resolved": "https://registry.npmjs.org/core-js/-/core-js-3.8.2.tgz", - "integrity": "sha512-FfApuSRgrR6G5s58casCBd9M2k+4ikuu4wbW6pJyYU7bd9zvFc9qf7vr5xmrZOhT9nn+8uwlH1oRR9jTnFoA3A==" + "version": "3.9.1", + "resolved": "https://registry.npmjs.org/core-js/-/core-js-3.9.1.tgz", + "integrity": "sha512-gSjRvzkxQc1zjM/5paAmL4idJBFzuJoo+jDjF1tStYFMV2ERfD02HhahhCGXUyHxQRG4yFKVSdO6g62eoRMcDg==" }, "core-js-compat": { - "version": "3.8.2", - "resolved": "https://registry.npmjs.org/core-js-compat/-/core-js-compat-3.8.2.tgz", - "integrity": "sha512-LO8uL9lOIyRRrQmZxHZFl1RV+ZbcsAkFWTktn5SmH40WgLtSNYN4m4W2v9ONT147PxBY/XrRhrWq8TlvObyUjQ==", + "version": "3.9.1", + "resolved": "https://registry.npmjs.org/core-js-compat/-/core-js-compat-3.9.1.tgz", + "integrity": "sha512-jXAirMQxrkbiiLsCx9bQPJFA6llDadKMpYrBJQJ3/c4/vsPP/fAf29h24tviRlvwUL6AmY5CHLu2GvjuYviQqA==", "dev": true, "requires": { - "browserslist": "^4.16.0", + "browserslist": "^4.16.3", "semver": "7.0.0" }, "dependencies": { @@ -5471,9 +5377,9 @@ } }, "core-js-pure": { - "version": "3.8.2", - "resolved": "https://registry.npmjs.org/core-js-pure/-/core-js-pure-3.8.2.tgz", - "integrity": "sha512-v6zfIQqL/pzTVAbZvYUozsxNfxcFb6Ks3ZfEbuneJl3FW9Jb8F6vLWB6f+qTmAu72msUdyb84V8d/yBFf7FNnw==" + "version": "3.9.1", + "resolved": "https://registry.npmjs.org/core-js-pure/-/core-js-pure-3.9.1.tgz", + "integrity": "sha512-laz3Zx0avrw9a4QEIdmIblnVuJz8W51leY9iLThatCsFawWxC3sE4guASC78JbCin+DkwMpCdp1AVAuzL/GN7A==" }, "core-util-is": { "version": "1.0.2", @@ -5504,9 +5410,9 @@ }, "dependencies": { "bn.js": { - "version": "4.11.9", - "resolved": "https://registry.npmjs.org/bn.js/-/bn.js-4.11.9.tgz", - "integrity": "sha512-E6QoYqCKZfgatHTdHzs1RRKP7ip4vvm+EyRUeE2RF0NblwVvb0p6jSVeNTOFxPn26QXN2o6SMfNxKp6kU8zQaw==", + "version": "4.12.0", + "resolved": "https://registry.npmjs.org/bn.js/-/bn.js-4.12.0.tgz", + "integrity": "sha512-c98Bf3tPniI+scsdk237ku1Dc3ujXQTSgyiPUDEOe7tRkhrqridvh8klBv0HCEso1OLOYcHuCv/cS6DNxKH+ZA==", "dev": true } } @@ -5976,9 +5882,9 @@ } }, "csstype": { - "version": "2.6.14", - "resolved": "https://registry.npmjs.org/csstype/-/csstype-2.6.14.tgz", - "integrity": "sha512-2mSc+VEpGPblzAxyeR+vZhJKgYg0Og0nnRi7pmRXFYYxSfnOnW8A5wwQb4n4cE2nIOzqKOAzLCaEX6aBmNEv8A==" + "version": "2.6.16", + "resolved": "https://registry.npmjs.org/csstype/-/csstype-2.6.16.tgz", + "integrity": "sha512-61FBWoDHp/gRtsoDkq/B1nWrCUG/ok1E3tUrcNbZjsE9Cxd9yzUirjS3+nAATB8U4cTtaQmAHbNndoFz5L6C9Q==" }, "customize-cra": { "version": "1.0.0", @@ -6343,9 +6249,9 @@ }, "dependencies": { "bn.js": { - "version": "4.11.9", - "resolved": "https://registry.npmjs.org/bn.js/-/bn.js-4.11.9.tgz", - "integrity": "sha512-E6QoYqCKZfgatHTdHzs1RRKP7ip4vvm+EyRUeE2RF0NblwVvb0p6jSVeNTOFxPn26QXN2o6SMfNxKp6kU8zQaw==", + "version": "4.12.0", + "resolved": "https://registry.npmjs.org/bn.js/-/bn.js-4.12.0.tgz", + "integrity": "sha512-c98Bf3tPniI+scsdk237ku1Dc3ujXQTSgyiPUDEOe7tRkhrqridvh8klBv0HCEso1OLOYcHuCv/cS6DNxKH+ZA==", "dev": true } } @@ -6439,9 +6345,9 @@ }, "dependencies": { "csstype": { - "version": "3.0.6", - "resolved": "https://registry.npmjs.org/csstype/-/csstype-3.0.6.tgz", - "integrity": "sha512-+ZAmfyWMT7TiIlzdqJgjMb7S4f1beorDbWbsocyK4RaiqA5RTX3K14bnBWmmA9QEM0gRdsjyyrEmcyga8Zsxmw==" + "version": "3.0.7", + "resolved": "https://registry.npmjs.org/csstype/-/csstype-3.0.7.tgz", + "integrity": "sha512-KxnUB0ZMlnUWCsx2Z8MUsr6qV6ja1w9ArPErJaJaF8a5SOWoHLIszeCTKGRGRgtLgYrs1E8CHkNSP1VZTTPc9g==" } } }, @@ -6465,9 +6371,9 @@ "integrity": "sha512-LsTgx/L5VpD+Q8lmsXSHW2WpA+eBlZ9HPf3erD1IoPF00/3JKHZ3BknUVA2QGDNu69ZNmyFmCWBSO45XjYKC5w==" }, "entities": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/entities/-/entities-2.1.0.tgz", - "integrity": "sha512-hCx1oky9PFrJ611mf0ifBLBRW8lUUVRlFolb5gWRfIELabBlbp9xZvrqZLZAs+NxFnbfQoeGd8wDkygjg7U85w==" + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/entities/-/entities-2.2.0.tgz", + "integrity": "sha512-p92if5Nz619I0w+akJrLZH0MX0Pb5DX39XOwQTtXSdQQOaYH03S1uIQp4mhOZtAXrxq4ViO67YTiLBo2638o9A==" } } }, @@ -6636,38 +6542,38 @@ "dev": true }, "electron-to-chromium": { - "version": "1.3.639", - "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.3.639.tgz", - "integrity": "sha512-bwl6/U6xb3d3CNufQU9QeO1L32ueouFwW4bWANSwdXR7LVqyLzWjNbynoKNfuC38QFB5Qn7O0l2KLqBkcXnC3Q==", + "version": "1.3.683", + "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.3.683.tgz", + "integrity": "sha512-8mFfiAesXdEdE0DhkMKO7W9U6VU/9T3VTWwZ+4g84/YMP4kgwgFtQgUxuu7FUMcvSeKSNhFQNU+WZ68BQTLT5A==", "dev": true }, "elliptic": { - "version": "6.5.3", - "resolved": "https://registry.npmjs.org/elliptic/-/elliptic-6.5.3.tgz", - "integrity": "sha512-IMqzv5wNQf+E6aHeIqATs0tOLeOTwj1QKbRcS3jBbYkl5oLAserA8yJTT7/VyHUYG91PRmPyeQDObKLPpeS4dw==", + "version": "6.5.4", + "resolved": "https://registry.npmjs.org/elliptic/-/elliptic-6.5.4.tgz", + "integrity": "sha512-iLhC6ULemrljPZb+QutR5TQGB+pdW6KGD5RSegS+8sorOZT+rdQFbsQFJgvN3eRqNALqJer4oQ16YvJHlU8hzQ==", "dev": true, "requires": { - "bn.js": "^4.4.0", - "brorand": "^1.0.1", + "bn.js": "^4.11.9", + "brorand": "^1.1.0", "hash.js": "^1.0.0", - "hmac-drbg": "^1.0.0", - "inherits": "^2.0.1", - "minimalistic-assert": "^1.0.0", - "minimalistic-crypto-utils": "^1.0.0" + "hmac-drbg": "^1.0.1", + "inherits": "^2.0.4", + "minimalistic-assert": "^1.0.1", + "minimalistic-crypto-utils": "^1.0.1" }, "dependencies": { "bn.js": { - "version": "4.11.9", - "resolved": "https://registry.npmjs.org/bn.js/-/bn.js-4.11.9.tgz", - "integrity": "sha512-E6QoYqCKZfgatHTdHzs1RRKP7ip4vvm+EyRUeE2RF0NblwVvb0p6jSVeNTOFxPn26QXN2o6SMfNxKp6kU8zQaw==", + "version": "4.12.0", + "resolved": "https://registry.npmjs.org/bn.js/-/bn.js-4.12.0.tgz", + "integrity": "sha512-c98Bf3tPniI+scsdk237ku1Dc3ujXQTSgyiPUDEOe7tRkhrqridvh8klBv0HCEso1OLOYcHuCv/cS6DNxKH+ZA==", "dev": true } } }, "emoji-regex": { - "version": "9.2.0", - "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-9.2.0.tgz", - "integrity": "sha512-DNc3KFPK18bPdElMJnf/Pkv5TXhxFU3YFDEuGLDRtPmV4rkmCjBkCSEp22u6rBHdSN9Vlp/GK7k98prmE1Jgug==", + "version": "9.2.2", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-9.2.2.tgz", + "integrity": "sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg==", "dev": true }, "emojis-list": { @@ -6773,23 +6679,27 @@ } }, "es-abstract": { - "version": "1.18.0-next.1", - "resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.18.0-next.1.tgz", - "integrity": "sha512-I4UGspA0wpZXWENrdA0uHbnhte683t3qT/1VFH9aX2dA5PPSf6QW5HHXf5HImaqPmjXaVeVk4RGWnaylmV7uAA==", + "version": "1.18.0", + "resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.18.0.tgz", + "integrity": "sha512-LJzK7MrQa8TS0ja2w3YNLzUgJCGPdPOV1yVvezjNnS89D+VR08+Szt2mz3YB2Dck/+w5tfIq/RoUAFqJJGM2yw==", "dev": true, "requires": { + "call-bind": "^1.0.2", "es-to-primitive": "^1.2.1", "function-bind": "^1.1.1", + "get-intrinsic": "^1.1.1", "has": "^1.0.3", - "has-symbols": "^1.0.1", - "is-callable": "^1.2.2", - "is-negative-zero": "^2.0.0", - "is-regex": "^1.1.1", - "object-inspect": "^1.8.0", + "has-symbols": "^1.0.2", + "is-callable": "^1.2.3", + "is-negative-zero": "^2.0.1", + "is-regex": "^1.1.2", + "is-string": "^1.0.5", + "object-inspect": "^1.9.0", "object-keys": "^1.1.1", - "object.assign": "^4.1.1", - "string.prototype.trimend": "^1.0.1", - "string.prototype.trimstart": "^1.0.1" + "object.assign": "^4.1.2", + "string.prototype.trimend": "^1.0.4", + "string.prototype.trimstart": "^1.0.4", + "unbox-primitive": "^1.0.0" } }, "es-to-primitive": { @@ -6947,9 +6857,9 @@ } }, "glob-parent": { - "version": "5.1.1", - "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.1.tgz", - "integrity": "sha512-FnI+VGOpnlGHWZxthPGR+QhR78fuiK0sNLkHQv+bL9fQi57lNNdquIbna/WrfROrolq8GK5Ek6BiMwqL/voRYQ==", + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz", + "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==", "dev": true, "requires": { "is-glob": "^4.0.1" @@ -7260,9 +7170,9 @@ "dev": true }, "esquery": { - "version": "1.3.1", - "resolved": "https://registry.npmjs.org/esquery/-/esquery-1.3.1.tgz", - "integrity": "sha512-olpvt9QG0vniUBZspVRN6lwB7hOZoTRtT+jzR+tS4ffYx2mzbw+z0XCOk44aaLYKApNX5nMm+E+P6o25ip/DHQ==", + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/esquery/-/esquery-1.4.0.tgz", + "integrity": "sha512-cCDispWt5vHHtwMY2YrAQ4ibFkAL8RbH5YGBnZBc90MolvvfkkQcJro/aZiAQUlQ3qgrYS6D6v8Gc5G5CQsc9w==", "dev": true, "requires": { "estraverse": "^5.1.0" @@ -7318,9 +7228,9 @@ "dev": true }, "events": { - "version": "3.2.0", - "resolved": "https://registry.npmjs.org/events/-/events-3.2.0.tgz", - "integrity": "sha512-/46HWwbfCX2xTawVfkKLGxMifJYQBWMwY1mjywRtb4c9x8l5NP3KoJtnIOiL1hfdRkIuYhETxQlo62IF8tcnlg==", + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/events/-/events-3.3.0.tgz", + "integrity": "sha512-mQw+2fkQbALzQ7V0MY0IqdnXNOeTtP4r0lN9z7AAawCXgqea7bDii20AYrIBrFd/Hx0M2Ocz6S111CaFkUcb0Q==", "dev": true }, "eventsource": { @@ -7558,9 +7468,9 @@ }, "dependencies": { "type": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/type/-/type-2.1.0.tgz", - "integrity": "sha512-G9absDWvhAWCV2gmF1zKud3OyC61nZDwWvBL2DApaVFogI07CprggiQAOOjvp2NRjYWFzPyu7vwtDrQFq8jeSA==", + "version": "2.5.0", + "resolved": "https://registry.npmjs.org/type/-/type-2.5.0.tgz", + "integrity": "sha512-180WMDQaIMm3+7hGXWf12GtdniDEy7nYcyFMKJn/eZz/6tSLXrUN9V0wKSbMjej0I1WHWbpREDEKHtqPQa9NNw==", "dev": true } } @@ -8017,9 +7927,9 @@ } }, "follow-redirects": { - "version": "1.13.1", - "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.13.1.tgz", - "integrity": "sha512-SSG5xmZh1mkPGyKzjZP8zLjltIfpW32Y5QpdNJyjcfGxK3qo3NDDkZOZSFiGn1A6SclQxY9GzEwAHQ3dmYRWpg==", + "version": "1.13.3", + "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.13.3.tgz", + "integrity": "sha512-DUgl6+HDzB0iEptNQEXLx/KhTmDb8tZUHSeLqpnjpknR70H0nC2t9N73BK6fN4hOvJ84pKlIQVQ4k5FFlBedKA==", "dev": true }, "font-awesome": { @@ -8227,9 +8137,9 @@ "dev": true }, "fsevents": { - "version": "2.3.1", - "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.1.tgz", - "integrity": "sha512-YR47Eg4hChJGAB1O3yEAOkGO+rlzutoICGqGo9EZ4lKWokzZRSyIW1QmTzqjtw8MJdj9srP869CuWw/hyzSiBw==", + "version": "2.3.2", + "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.2.tgz", + "integrity": "sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA==", "dev": true, "optional": true }, @@ -8257,9 +8167,9 @@ "dev": true }, "get-intrinsic": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.0.2.tgz", - "integrity": "sha512-aeX0vrFm21ILl3+JpFFRNe9aUvp6VFZb2/CTbgLb8j75kOhvoNYjt9d8KA/tJG4gSo8nzEDedRl0h7vDmBYRVg==", + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.1.1.tgz", + "integrity": "sha512-kWZrnVM42QCiEA2Ig1bG8zjoIMOgxWwYCEeNdwY6Tv/cOSeGpcoX4pXHfKUxNKVoArnrEr2e9srnAxxGIraS9Q==", "dev": true, "requires": { "function-bind": "^1.1.1", @@ -8400,9 +8310,9 @@ } }, "graceful-fs": { - "version": "4.2.4", - "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.4.tgz", - "integrity": "sha512-WjKPNJF79dtJAVniUlGGWHYGz2jWxT6VhN/4m1NdkbZ2nOsEF+cI1Edgql5zCRhs/VsQYRvrXctxktVXZUkixw==", + "version": "4.2.6", + "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.6.tgz", + "integrity": "sha512-nTnJ528pbqxYanhpDYsi4Rd8MAeaBA67+RZ10CM1m3bTAVFEDcd5AuA4a6W5YkGZ1iNXHzZz8T6TBKLeBuNriQ==", "dev": true }, "growly": { @@ -8474,15 +8384,21 @@ } } }, + "has-bigints": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/has-bigints/-/has-bigints-1.0.1.tgz", + "integrity": "sha512-LSBS2LjbNBTf6287JEbEzvJgftkF5qFkmCo9hDRpAzKhUOlJ+hx8dd4USs00SgsUNwc4617J9ki5YtEClM2ffA==", + "dev": true + }, "has-flag": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", "integrity": "sha1-tdRU3CGZriJWmfNGfloH87lVuv0=" }, "has-symbols": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.0.1.tgz", - "integrity": "sha512-PLcsoqu++dmEIZB+6totNFKq/7Do+Z0u4oT0zKOJNl3lYK6vGwwu2hjHs+68OEZbTjiUE9bgOABXbP/GvrS0Kg==", + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.0.2.tgz", + "integrity": "sha512-chXa79rL/UC2KlX17jo3vRGz0azaWEx5tGqZg5pO3NUyEJVB17dMruQlzCCOfUvElghKcm5194+BCRvi2Rv/Gw==", "dev": true }, "has-value": { @@ -8771,9 +8687,9 @@ } }, "entities": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/entities/-/entities-2.1.0.tgz", - "integrity": "sha512-hCx1oky9PFrJ611mf0ifBLBRW8lUUVRlFolb5gWRfIELabBlbp9xZvrqZLZAs+NxFnbfQoeGd8wDkygjg7U85w==", + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/entities/-/entities-2.2.0.tgz", + "integrity": "sha512-p92if5Nz619I0w+akJrLZH0MX0Pb5DX39XOwQTtXSdQQOaYH03S1uIQp4mhOZtAXrxq4ViO67YTiLBo2638o9A==", "dev": true }, "htmlparser2": { @@ -9231,9 +9147,9 @@ "dev": true }, "rxjs": { - "version": "6.6.3", - "resolved": "https://registry.npmjs.org/rxjs/-/rxjs-6.6.3.tgz", - "integrity": "sha512-trsQc+xYYXZ3urjOiJOuCOa5N3jAZ3eiSpQB5hIT8zGlL2QfnHLJ2r7GMkBGuIausdJN1OneaI6gQlsqNHHmZQ==", + "version": "6.6.6", + "resolved": "https://registry.npmjs.org/rxjs/-/rxjs-6.6.6.tgz", + "integrity": "sha512-/oTwee4N4iWzAMAL9xdGKjkEHmIwupR3oXbQjCKywF1BeFohswF3vZdogbmEF6pZkOsXTzWkrZszrWpQTByYVg==", "dev": true, "requires": { "tslib": "^1.9.0" @@ -9270,35 +9186,14 @@ } }, "internal-slot": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/internal-slot/-/internal-slot-1.0.2.tgz", - "integrity": "sha512-2cQNfwhAfJIkU4KZPkDI+Gj5yNNnbqi40W9Gge6dfnk4TocEVm00B3bdiL+JINrbGJil2TeHvM4rETGzk/f/0g==", + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/internal-slot/-/internal-slot-1.0.3.tgz", + "integrity": "sha512-O0DB1JC/sPyZl7cIo78n5dR7eUSwwpYPiXRhTzNxZVAMUuB8vlnRFyLxdrVToks6XPLVnFfbzaVd5WLjhgg+vA==", "dev": true, "requires": { - "es-abstract": "^1.17.0-next.1", + "get-intrinsic": "^1.1.0", "has": "^1.0.3", - "side-channel": "^1.0.2" - }, - "dependencies": { - "es-abstract": { - "version": "1.17.7", - "resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.17.7.tgz", - "integrity": "sha512-VBl/gnfcJ7OercKA9MVaegWsBHFjV492syMudcnQZvt/Dw8ezpcOHYZXa/J96O8vx+g4x65YKhxOwDUh63aS5g==", - "dev": true, - "requires": { - "es-to-primitive": "^1.2.1", - "function-bind": "^1.1.1", - "has": "^1.0.3", - "has-symbols": "^1.0.1", - "is-callable": "^1.2.2", - "is-regex": "^1.1.1", - "object-inspect": "^1.8.0", - "object-keys": "^1.1.1", - "object.assign": "^4.1.1", - "string.prototype.trimend": "^1.0.1", - "string.prototype.trimstart": "^1.0.1" - } - } + "side-channel": "^1.0.4" } }, "invariant": { @@ -9368,6 +9263,12 @@ "resolved": "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.2.1.tgz", "integrity": "sha1-d8mYQFJ6qOyxqLppe4BkWnqSap0=" }, + "is-bigint": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/is-bigint/-/is-bigint-1.0.1.tgz", + "integrity": "sha512-J0ELF4yHFxHy0cmSxZuheDOz2luOdVvqjwmEcj8H/L1JHeuEDSDbeRP+Dk9kFVk5RTFzbucJ2Kb9F7ixY2QaCg==", + "dev": true + }, "is-binary-path": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/is-binary-path/-/is-binary-path-1.0.1.tgz", @@ -9377,6 +9278,15 @@ "binary-extensions": "^1.0.0" } }, + "is-boolean-object": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/is-boolean-object/-/is-boolean-object-1.1.0.tgz", + "integrity": "sha512-a7Uprx8UtD+HWdyYwnD1+ExtTgqQtD2k/1yJgtXP6wnMm8byhkoTZRl+95LLThpzNZJ5aEvi46cdH+ayMFRwmA==", + "dev": true, + "requires": { + "call-bind": "^1.0.0" + } + }, "is-buffer": { "version": "1.1.6", "resolved": "https://registry.npmjs.org/is-buffer/-/is-buffer-1.1.6.tgz", @@ -9384,9 +9294,9 @@ "dev": true }, "is-callable": { - "version": "1.2.2", - "resolved": "https://registry.npmjs.org/is-callable/-/is-callable-1.2.2.tgz", - "integrity": "sha512-dnMqspv5nU3LoewK2N/y7KLtxtakvTuaCsU9FU50/QDmdbHNy/4/JuRtMHqRU22o3q+W89YQndQEeCVwK+3qrA==", + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/is-callable/-/is-callable-1.2.3.tgz", + "integrity": "sha512-J1DcMe8UYTBSrKezuIUTUwjXsho29693unXM2YhJUTR2txK/eG47bvNa/wipPFmZFgr/N6f1GA66dv0mEyTIyQ==", "dev": true }, "is-ci": { @@ -9541,6 +9451,12 @@ } } }, + "is-number-object": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/is-number-object/-/is-number-object-1.0.4.tgz", + "integrity": "sha512-zohwelOAur+5uXtk8O3GPQ1eAcu4ZX3UwxQhUlfFFMNpUd83gXgjbhJh6HmB6LUNV/ieOLQuDwJO3dWJosUeMw==", + "dev": true + }, "is-obj": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/is-obj/-/is-obj-2.0.0.tgz", @@ -9587,11 +9503,12 @@ } }, "is-regex": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/is-regex/-/is-regex-1.1.1.tgz", - "integrity": "sha512-1+QkEcxiLlB7VEyFtyBg94e08OAsvq7FUBgApTq/w2ymCLyKJgDPsybBENVtA7XCQEgEXxKPonG+mvYRxh/LIg==", + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/is-regex/-/is-regex-1.1.2.tgz", + "integrity": "sha512-axvdhb5pdhEVThqJzYXwMlVuZwC+FF2DpcOhTS+y/8jVq4trxyPgfcwIxIKiyeuLlSQYKkmUaPQJ8ZE4yNKXDg==", "dev": true, "requires": { + "call-bind": "^1.0.2", "has-symbols": "^1.0.1" } }, @@ -9927,9 +9844,9 @@ } }, "jest-canvas-mock": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/jest-canvas-mock/-/jest-canvas-mock-2.3.0.tgz", - "integrity": "sha512-3TMyR66VG2MzAW8Negzec03bbcIjVJMfGNvKzrEnbws1CYKqMNkvIJ8LbkoGYfp42tKqDmhIpQq3v+MNLW2A2w==", + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/jest-canvas-mock/-/jest-canvas-mock-2.3.1.tgz", + "integrity": "sha512-5FnSZPrX3Q2ZfsbYNE3wqKR3+XorN8qFzDzB5o0golWgt6EOX1+emBnpOc9IAQ+NXFj8Nzm3h7ZdE/9H0ylBcg==", "dev": true, "requires": { "cssfontparser": "^1.2.1", @@ -12619,9 +12536,9 @@ "integrity": "sha512-c7/8mbUsKigAbLkD5B010BK4D9LZm7A1pNItkEwiUZRpIN66exu/e7YQWysGun+TRKaJp8MhemM+VkfWv42aCA==" }, "json5": { - "version": "2.1.3", - "resolved": "https://registry.npmjs.org/json5/-/json5-2.1.3.tgz", - "integrity": "sha512-KXPvOm8K9IJKFM0bmdn8QXh7udDh1g/giieX0NLCaMnb4hEiVFqnop2ImTXCc5e0/oHz3LTqmHGtExn5hfMkOA==", + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/json5/-/json5-2.2.0.tgz", + "integrity": "sha512-f+8cldu7X/y7RAJurMEJmdoKXGB/X550w2Nr3tTbezL6RwEE/iMcm+tZnXeoZtKuOq6ft8+CqzEkrIgx1fPoQA==", "dev": true, "requires": { "minimist": "^1.2.5" @@ -12673,9 +12590,9 @@ } }, "jss": { - "version": "10.5.0", - "resolved": "https://registry.npmjs.org/jss/-/jss-10.5.0.tgz", - "integrity": "sha512-B6151NvG+thUg3murLNHRPLxTLwQ13ep4SH5brj4d8qKtogOx/jupnpfkPGSHPqvcwKJaCLctpj2lEk+5yGwMw==", + "version": "10.5.1", + "resolved": "https://registry.npmjs.org/jss/-/jss-10.5.1.tgz", + "integrity": "sha512-hbbO3+FOTqVdd7ZUoTiwpHzKXIo5vGpMNbuXH1a0wubRSWLWSBvwvaq4CiHH/U42CmjOnp6lVNNs/l+Z7ZdDmg==", "requires": { "@babel/runtime": "^7.3.1", "csstype": "^3.0.2", @@ -12685,77 +12602,77 @@ }, "dependencies": { "csstype": { - "version": "3.0.6", - "resolved": "https://registry.npmjs.org/csstype/-/csstype-3.0.6.tgz", - "integrity": "sha512-+ZAmfyWMT7TiIlzdqJgjMb7S4f1beorDbWbsocyK4RaiqA5RTX3K14bnBWmmA9QEM0gRdsjyyrEmcyga8Zsxmw==" + "version": "3.0.7", + "resolved": "https://registry.npmjs.org/csstype/-/csstype-3.0.7.tgz", + "integrity": "sha512-KxnUB0ZMlnUWCsx2Z8MUsr6qV6ja1w9ArPErJaJaF8a5SOWoHLIszeCTKGRGRgtLgYrs1E8CHkNSP1VZTTPc9g==" } } }, "jss-plugin-camel-case": { - "version": "10.5.0", - "resolved": "https://registry.npmjs.org/jss-plugin-camel-case/-/jss-plugin-camel-case-10.5.0.tgz", - "integrity": "sha512-GSjPL0adGAkuoqeYiXTgO7PlIrmjv5v8lA6TTBdfxbNYpxADOdGKJgIEkffhlyuIZHlPuuiFYTwUreLUmSn7rg==", + "version": "10.5.1", + "resolved": "https://registry.npmjs.org/jss-plugin-camel-case/-/jss-plugin-camel-case-10.5.1.tgz", + "integrity": "sha512-9+oymA7wPtswm+zxVti1qiowC5q7bRdCJNORtns2JUj/QHp2QPXYwSNRD8+D2Cy3/CEMtdJzlNnt5aXmpS6NAg==", "requires": { "@babel/runtime": "^7.3.1", "hyphenate-style-name": "^1.0.3", - "jss": "10.5.0" + "jss": "10.5.1" } }, "jss-plugin-default-unit": { - "version": "10.5.0", - "resolved": "https://registry.npmjs.org/jss-plugin-default-unit/-/jss-plugin-default-unit-10.5.0.tgz", - "integrity": "sha512-rsbTtZGCMrbcb9beiDd+TwL991NGmsAgVYH0hATrYJtue9e+LH/Gn4yFD1ENwE+3JzF3A+rPnM2JuD9L/SIIWw==", + "version": "10.5.1", + "resolved": "https://registry.npmjs.org/jss-plugin-default-unit/-/jss-plugin-default-unit-10.5.1.tgz", + "integrity": "sha512-D48hJBc9Tj3PusvlillHW8Fz0y/QqA7MNmTYDQaSB/7mTrCZjt7AVRROExoOHEtd2qIYKOYJW3Jc2agnvsXRlQ==", "requires": { "@babel/runtime": "^7.3.1", - "jss": "10.5.0" + "jss": "10.5.1" } }, "jss-plugin-global": { - "version": "10.5.0", - "resolved": "https://registry.npmjs.org/jss-plugin-global/-/jss-plugin-global-10.5.0.tgz", - "integrity": "sha512-FZd9+JE/3D7HMefEG54fEC0XiQ9rhGtDHAT/ols24y8sKQ1D5KIw6OyXEmIdKFmACgxZV2ARQ5pAUypxkk2IFQ==", + "version": "10.5.1", + "resolved": "https://registry.npmjs.org/jss-plugin-global/-/jss-plugin-global-10.5.1.tgz", + "integrity": "sha512-jX4XpNgoaB8yPWw/gA1aPXJEoX0LNpvsROPvxlnYe+SE0JOhuvF7mA6dCkgpXBxfTWKJsno7cDSCgzHTocRjCQ==", "requires": { "@babel/runtime": "^7.3.1", - "jss": "10.5.0" + "jss": "10.5.1" } }, "jss-plugin-nested": { - "version": "10.5.0", - "resolved": "https://registry.npmjs.org/jss-plugin-nested/-/jss-plugin-nested-10.5.0.tgz", - "integrity": "sha512-ejPlCLNlEGgx8jmMiDk/zarsCZk+DV0YqXfddpgzbO9Toamo0HweCFuwJ3ZO40UFOfqKwfpKMVH/3HUXgxkTMg==", + "version": "10.5.1", + "resolved": "https://registry.npmjs.org/jss-plugin-nested/-/jss-plugin-nested-10.5.1.tgz", + "integrity": "sha512-xXkWKOCljuwHNjSYcXrCxBnjd8eJp90KVFW1rlhvKKRXnEKVD6vdKXYezk2a89uKAHckSvBvBoDGsfZrldWqqQ==", "requires": { "@babel/runtime": "^7.3.1", - "jss": "10.5.0", + "jss": "10.5.1", "tiny-warning": "^1.0.2" } }, "jss-plugin-props-sort": { - "version": "10.5.0", - "resolved": "https://registry.npmjs.org/jss-plugin-props-sort/-/jss-plugin-props-sort-10.5.0.tgz", - "integrity": "sha512-kTLRvrOetFKz5vM88FAhLNeJIxfjhCepnvq65G7xsAQ/Wgy7HwO1BS/2wE5mx8iLaAWC6Rj5h16mhMk9sKdZxg==", + "version": "10.5.1", + "resolved": "https://registry.npmjs.org/jss-plugin-props-sort/-/jss-plugin-props-sort-10.5.1.tgz", + "integrity": "sha512-t+2vcevNmMg4U/jAuxlfjKt46D/jHzCPEjsjLRj/J56CvP7Iy03scsUP58Iw8mVnaV36xAUZH2CmAmAdo8994g==", "requires": { "@babel/runtime": "^7.3.1", - "jss": "10.5.0" + "jss": "10.5.1" } }, "jss-plugin-rule-value-function": { - "version": "10.5.0", - "resolved": "https://registry.npmjs.org/jss-plugin-rule-value-function/-/jss-plugin-rule-value-function-10.5.0.tgz", - "integrity": "sha512-jXINGr8BSsB13JVuK274oEtk0LoooYSJqTBCGeBu2cG/VJ3+4FPs1gwLgsq24xTgKshtZ+WEQMVL34OprLidRA==", + "version": "10.5.1", + "resolved": "https://registry.npmjs.org/jss-plugin-rule-value-function/-/jss-plugin-rule-value-function-10.5.1.tgz", + "integrity": "sha512-3gjrSxsy4ka/lGQsTDY8oYYtkt2esBvQiceGBB4PykXxHoGRz14tbCK31Zc6DHEnIeqsjMUGbq+wEly5UViStQ==", "requires": { "@babel/runtime": "^7.3.1", - "jss": "10.5.0", + "jss": "10.5.1", "tiny-warning": "^1.0.2" } }, "jss-plugin-vendor-prefixer": { - "version": "10.5.0", - "resolved": "https://registry.npmjs.org/jss-plugin-vendor-prefixer/-/jss-plugin-vendor-prefixer-10.5.0.tgz", - "integrity": "sha512-rux3gmfwDdOKCLDx0IQjTwTm03IfBa+Rm/hs747cOw5Q7O3RaTUIMPKjtVfc31Xr/XI9Abz2XEupk1/oMQ7zRA==", + "version": "10.5.1", + "resolved": "https://registry.npmjs.org/jss-plugin-vendor-prefixer/-/jss-plugin-vendor-prefixer-10.5.1.tgz", + "integrity": "sha512-cLkH6RaPZWHa1TqSfd2vszNNgxT1W0omlSjAd6hCFHp3KIocSrW21gaHjlMU26JpTHwkc+tJTCQOmE/O1A4FKQ==", "requires": { "@babel/runtime": "^7.3.1", "css-vendor": "^2.0.8", - "jss": "10.5.0" + "jss": "10.5.1" } }, "jsx-ast-utils": { @@ -12966,9 +12883,9 @@ } }, "lodash": { - "version": "4.17.20", - "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.20.tgz", - "integrity": "sha512-PlhdFcillOINfeV7Ni6oF1TAEayyZBoZ8bcshTHqOYJYlrqzRK5hagpagky5o4HfCzzd1TRkXPMFq6cKk9rGmA==" + "version": "4.17.21", + "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz", + "integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==" }, "lodash._reinterpolate": { "version": "3.0.0", @@ -12976,6 +12893,12 @@ "integrity": "sha1-DM8tiRZq8Ds2Y8eWU4t1rG4RTZ0=", "dev": true }, + "lodash.debounce": { + "version": "4.0.8", + "resolved": "https://registry.npmjs.org/lodash.debounce/-/lodash.debounce-4.0.8.tgz", + "integrity": "sha1-gteb/zCmfEAF/9XiUVMArZyk168=", + "dev": true + }, "lodash.flow": { "version": "3.5.0", "resolved": "https://registry.npmjs.org/lodash.flow/-/lodash.flow-3.5.0.tgz", @@ -13302,32 +13225,32 @@ }, "dependencies": { "bn.js": { - "version": "4.11.9", - "resolved": "https://registry.npmjs.org/bn.js/-/bn.js-4.11.9.tgz", - "integrity": "sha512-E6QoYqCKZfgatHTdHzs1RRKP7ip4vvm+EyRUeE2RF0NblwVvb0p6jSVeNTOFxPn26QXN2o6SMfNxKp6kU8zQaw==", + "version": "4.12.0", + "resolved": "https://registry.npmjs.org/bn.js/-/bn.js-4.12.0.tgz", + "integrity": "sha512-c98Bf3tPniI+scsdk237ku1Dc3ujXQTSgyiPUDEOe7tRkhrqridvh8klBv0HCEso1OLOYcHuCv/cS6DNxKH+ZA==", "dev": true } } }, "mime": { - "version": "2.4.7", - "resolved": "https://registry.npmjs.org/mime/-/mime-2.4.7.tgz", - "integrity": "sha512-dhNd1uA2u397uQk3Nv5LM4lm93WYDUXFn3Fu291FJerns4jyTudqhIWe4W04YLy7Uk1tm1Ore04NpjRvQp/NPA==", + "version": "2.5.2", + "resolved": "https://registry.npmjs.org/mime/-/mime-2.5.2.tgz", + "integrity": "sha512-tqkh47FzKeCPD2PUiPB6pkbMzsCasjxAfC62/Wap5qrUWcb+sFasXUC5I3gYM5iBM8v/Qpn4UK0x+j0iHyFPDg==", "dev": true }, "mime-db": { - "version": "1.45.0", - "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.45.0.tgz", - "integrity": "sha512-CkqLUxUk15hofLoLyljJSrukZi8mAtgd+yE5uO4tqRZsdsAJKv0O+rFMhVDRJgozy+yG6md5KwuXhD4ocIoP+w==", + "version": "1.46.0", + "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.46.0.tgz", + "integrity": "sha512-svXaP8UQRZ5K7or+ZmfNhg2xX3yKDMUzqadsSqi4NCH/KomcH75MAMYAGVlvXn4+b/xOPhS3I2uHKRUzvjY7BQ==", "dev": true }, "mime-types": { - "version": "2.1.28", - "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.28.tgz", - "integrity": "sha512-0TO2yJ5YHYr7M2zzT7gDU1tbwHxEUWBCLt0lscSNpcdAfFyJOVEpRYNS7EXVcTLNj/25QO8gulHC5JtTzSE2UQ==", + "version": "2.1.29", + "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.29.tgz", + "integrity": "sha512-Y/jMt/S5sR9OaqteJtslsFZKWOIIqMACsJSiHghlCAyhf7jfVYjKBmLiX8OgpWeW+fjJ2b+Az69aPFPkUOY6xQ==", "dev": true, "requires": { - "mime-db": "1.45.0" + "mime-db": "1.46.0" } }, "mimic-fn": { @@ -13769,9 +13692,9 @@ } }, "node-releases": { - "version": "1.1.69", - "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-1.1.69.tgz", - "integrity": "sha512-DGIjo79VDEyAnRlfSqYTsy+yoHd2IOjJiKUozD2MV2D85Vso6Bug56mb9tT/fY5Urt0iqk01H7x+llAruDR2zA==", + "version": "1.1.71", + "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-1.1.71.tgz", + "integrity": "sha512-zR6HoT6LrLCRBwukmrVbHv0EpEQjksO6GmFcZQQuCAy139BEsoVKPYnf3jongYW83fAa1torLGYwxxky/p28sg==", "dev": true }, "normalize-package-data": { @@ -16989,12 +16912,12 @@ "dev": true }, "object-is": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/object-is/-/object-is-1.1.4.tgz", - "integrity": "sha512-1ZvAZ4wlF7IyPVOcE1Omikt7UpaFlOQq0HlSti+ZvDH3UiD2brwGMwDbyV43jao2bKJ+4+WdPJHSd7kgzKYVqg==", + "version": "1.1.5", + "resolved": "https://registry.npmjs.org/object-is/-/object-is-1.1.5.tgz", + "integrity": "sha512-3cyDsyHgtmi7I7DfSSI2LDp6SK2lwvtbg0p0R1e0RvTqF5ceGx+K2dfSjm1bKDMVCFEDAQvy+o8c6a7VujOddw==", "dev": true, "requires": { - "call-bind": "^1.0.0", + "call-bind": "^1.0.2", "define-properties": "^1.1.3" } }, @@ -17038,26 +16961,26 @@ } }, "object.fromentries": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/object.fromentries/-/object.fromentries-2.0.3.tgz", - "integrity": "sha512-IDUSMXs6LOSJBWE++L0lzIbSqHl9KDCfff2x/JSEIDtEUavUnyMYC2ZGay/04Zq4UT8lvd4xNhU4/YHKibAOlw==", + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/object.fromentries/-/object.fromentries-2.0.4.tgz", + "integrity": "sha512-EsFBshs5RUUpQEY1D4q/m59kMfz4YJvxuNCJcv/jWwOJr34EaVnG11ZrZa0UHB3wnzV1wx8m58T4hQL8IuNXlQ==", "dev": true, "requires": { - "call-bind": "^1.0.0", + "call-bind": "^1.0.2", "define-properties": "^1.1.3", - "es-abstract": "^1.18.0-next.1", + "es-abstract": "^1.18.0-next.2", "has": "^1.0.3" } }, "object.getownpropertydescriptors": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/object.getownpropertydescriptors/-/object.getownpropertydescriptors-2.1.1.tgz", - "integrity": "sha512-6DtXgZ/lIZ9hqx4GtZETobXLR/ZLaa0aqV0kzbn80Rf8Z2e/XFnhA0I7p07N2wH8bBBltr2xQPi6sbKWAY2Eng==", + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/object.getownpropertydescriptors/-/object.getownpropertydescriptors-2.1.2.tgz", + "integrity": "sha512-WtxeKSzfBjlzL+F9b7M7hewDzMwy+C8NRssHd1YrNlzHzIDrXcXiNOMrezdAEM4UXixgV+vvnyBeN7Rygl2ttQ==", "dev": true, "requires": { - "call-bind": "^1.0.0", + "call-bind": "^1.0.2", "define-properties": "^1.1.3", - "es-abstract": "^1.18.0-next.1" + "es-abstract": "^1.18.0-next.2" } }, "object.pick": { @@ -17070,14 +16993,14 @@ } }, "object.values": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/object.values/-/object.values-1.1.2.tgz", - "integrity": "sha512-MYC0jvJopr8EK6dPBiO8Nb9mvjdypOachO5REGk6MXzujbBrAisKo3HmdEI6kZDL6fC31Mwee/5YbtMebixeag==", + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/object.values/-/object.values-1.1.3.tgz", + "integrity": "sha512-nkF6PfDB9alkOUxpf1HNm/QlkeW3SReqL5WXeBLpEJJnlPSvRaDQpW3gQTksTN3fgJX4hL42RzKyOin6ff3tyw==", "dev": true, "requires": { - "call-bind": "^1.0.0", + "call-bind": "^1.0.2", "define-properties": "^1.1.3", - "es-abstract": "^1.18.0-next.1", + "es-abstract": "^1.18.0-next.2", "has": "^1.0.3" } }, @@ -17121,9 +17044,9 @@ } }, "open": { - "version": "7.3.1", - "resolved": "https://registry.npmjs.org/open/-/open-7.3.1.tgz", - "integrity": "sha512-f2wt9DCBKKjlFbjzGb8MOAW8LH8F0mrs1zc7KTjAJ9PZNQbfenzWbNP1VZJvw6ICMG9r14Ah6yfwPn7T7i646A==", + "version": "7.4.2", + "resolved": "https://registry.npmjs.org/open/-/open-7.4.2.tgz", + "integrity": "sha512-MVHddDVweXZF3awtlAS+6pgKLlm/JgxZ90+/NBurBoQctVOOB/zDdVjcyPzQ+0laDGbsWgrRkflI65sQeOgT9Q==", "dev": true, "requires": { "is-docker": "^2.0.0", @@ -17362,9 +17285,9 @@ } }, "parse-json": { - "version": "5.1.0", - "resolved": "https://registry.npmjs.org/parse-json/-/parse-json-5.1.0.tgz", - "integrity": "sha512-+mi/lmVVNKFNVyLXV31ERiy2CY5E1/F6QtJFEzoChPRwwngMNXRDQ9GJ5WdE2Z2P4AujsOi0/+2qHID68KwfIQ==", + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/parse-json/-/parse-json-5.2.0.tgz", + "integrity": "sha512-ayCKvm/phCGxOkYRSCM82iDwct8/EonSEgCSxWxD7ve6jHggsFl4fZVQBPRNgQoKiuV/odhFrGzQXZwbifC8Rg==", "requires": { "@babel/code-frame": "^7.0.0", "error-ex": "^1.3.1", @@ -18715,9 +18638,9 @@ "dev": true }, "pretty-bytes": { - "version": "5.5.0", - "resolved": "https://registry.npmjs.org/pretty-bytes/-/pretty-bytes-5.5.0.tgz", - "integrity": "sha512-p+T744ZyjjiaFlMUZZv6YPC5JrkNj8maRmPaQCWFJFplUAzpIUTRaTcS+7wmZtUoFXHtESJb23ISliaWyz3SHA==", + "version": "5.6.0", + "resolved": "https://registry.npmjs.org/pretty-bytes/-/pretty-bytes-5.6.0.tgz", + "integrity": "sha512-FFw039TmrBqFK8ma/7OL3sDz/VytdtJr044/QUJtH0wK9lb9jLq9tJyIxUwtQJHwar2BqtiA4iCWSwo9JLkzFg==", "dev": true }, "pretty-error": { @@ -18865,9 +18788,9 @@ }, "dependencies": { "bn.js": { - "version": "4.11.9", - "resolved": "https://registry.npmjs.org/bn.js/-/bn.js-4.11.9.tgz", - "integrity": "sha512-E6QoYqCKZfgatHTdHzs1RRKP7ip4vvm+EyRUeE2RF0NblwVvb0p6jSVeNTOFxPn26QXN2o6SMfNxKp6kU8zQaw==", + "version": "4.12.0", + "resolved": "https://registry.npmjs.org/bn.js/-/bn.js-4.12.0.tgz", + "integrity": "sha512-c98Bf3tPniI+scsdk237ku1Dc3ujXQTSgyiPUDEOe7tRkhrqridvh8klBv0HCEso1OLOYcHuCv/cS6DNxKH+ZA==", "dev": true } } @@ -19307,9 +19230,9 @@ "dev": true }, "rxjs": { - "version": "6.6.3", - "resolved": "https://registry.npmjs.org/rxjs/-/rxjs-6.6.3.tgz", - "integrity": "sha512-trsQc+xYYXZ3urjOiJOuCOa5N3jAZ3eiSpQB5hIT8zGlL2QfnHLJ2r7GMkBGuIausdJN1OneaI6gQlsqNHHmZQ==", + "version": "6.6.6", + "resolved": "https://registry.npmjs.org/rxjs/-/rxjs-6.6.6.tgz", + "integrity": "sha512-/oTwee4N4iWzAMAL9xdGKjkEHmIwupR3oXbQjCKywF1BeFohswF3vZdogbmEF6pZkOsXTzWkrZszrWpQTByYVg==", "dev": true, "requires": { "tslib": "^1.9.0" @@ -19375,17 +19298,17 @@ "integrity": "sha1-6RWrjLO5WYdwdfSUNt6/2wQoj+Q=" }, "react-error-boundary": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/react-error-boundary/-/react-error-boundary-3.1.0.tgz", - "integrity": "sha512-lmPrdi5SLRJR+AeJkqdkGlW/CRkAUvZnETahK58J4xb5wpbfDngasEGu+w0T1iXEhVrYBJZeW+c4V1hILCnMWQ==", + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/react-error-boundary/-/react-error-boundary-3.1.1.tgz", + "integrity": "sha512-W3xCd9zXnanqrTUeViceufD3mIW8Ut29BUD+S2f0eO2XCOU8b6UrJfY46RDGe5lxCJzfe4j0yvIfh0RbTZhKJw==", "requires": { "@babel/runtime": "^7.12.5" } }, "react-error-overlay": { - "version": "6.0.8", - "resolved": "https://registry.npmjs.org/react-error-overlay/-/react-error-overlay-6.0.8.tgz", - "integrity": "sha512-HvPuUQnLp5H7TouGq3kzBeioJmXms1wHy9EGjz2OURWBp4qZO6AfGEcnxts1D/CbwPLRAgTMPCEgYhA3sEM4vw==", + "version": "6.0.9", + "resolved": "https://registry.npmjs.org/react-error-overlay/-/react-error-overlay-6.0.9.tgz", + "integrity": "sha512-nQTTcUu+ATDbrSD1BZHr5kgSD4oF8OFjxun8uAaL8RwPBacGBNPf/yAuVVdx17N8XNzRDMrZ9XcKZHCjPW+9ew==", "dev": true }, "react-from-dom": { @@ -19777,12 +19700,12 @@ } }, "resolve": { - "version": "1.19.0", - "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.19.0.tgz", - "integrity": "sha512-rArEXAgsBG4UgRGcynxWIWKFvh/XZCcS8UJdHhwy91zwAvCZIbcs+vAbflgBnNjYMs/i/i+/Ux6IZhML1yPvxg==", + "version": "1.20.0", + "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.20.0.tgz", + "integrity": "sha512-wENBPt4ySzg4ybFQW2TT1zMQucPK95HSh/nq2CFTZVOGut2+pQvSsgtda4d26YrYcr067wjbmzOG8byDPBX63A==", "dev": true, "requires": { - "is-core-module": "^2.1.0", + "is-core-module": "^2.2.0", "path-parse": "^1.0.6" } } @@ -20044,34 +19967,13 @@ "dev": true }, "regexp.prototype.flags": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/regexp.prototype.flags/-/regexp.prototype.flags-1.3.0.tgz", - "integrity": "sha512-2+Q0C5g951OlYlJz6yu5/M33IcsESLlLfsyIaLJaG4FA2r4yP8MvVMJUUP/fVBkSpbbbZlS5gynbEWLipiiXiQ==", + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/regexp.prototype.flags/-/regexp.prototype.flags-1.3.1.tgz", + "integrity": "sha512-JiBdRBq91WlY7uRJ0ds7R+dU02i6LKi8r3BuQhNXn+kmeLN+EfHhfjqMRis1zJxnlu88hq/4dx0P2OP3APRTOA==", "dev": true, "requires": { - "define-properties": "^1.1.3", - "es-abstract": "^1.17.0-next.1" - }, - "dependencies": { - "es-abstract": { - "version": "1.17.7", - "resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.17.7.tgz", - "integrity": "sha512-VBl/gnfcJ7OercKA9MVaegWsBHFjV492syMudcnQZvt/Dw8ezpcOHYZXa/J96O8vx+g4x65YKhxOwDUh63aS5g==", - "dev": true, - "requires": { - "es-to-primitive": "^1.2.1", - "function-bind": "^1.1.1", - "has": "^1.0.3", - "has-symbols": "^1.0.1", - "is-callable": "^1.2.2", - "is-regex": "^1.1.1", - "object-inspect": "^1.8.0", - "object-keys": "^1.1.1", - "object.assign": "^4.1.1", - "string.prototype.trimend": "^1.0.1", - "string.prototype.trimstart": "^1.0.1" - } - } + "call-bind": "^1.0.2", + "define-properties": "^1.1.3" } }, "regexpp": { @@ -20101,9 +20003,9 @@ "dev": true }, "regjsparser": { - "version": "0.6.6", - "resolved": "https://registry.npmjs.org/regjsparser/-/regjsparser-0.6.6.tgz", - "integrity": "sha512-jjyuCp+IEMIm3N1H1LLTJW1EISEJV9+5oHdEyrt43Pg9cDSb6rrLZei2cVWpl0xTjmmlpec/lEQGYgM7xfpGCQ==", + "version": "0.6.7", + "resolved": "https://registry.npmjs.org/regjsparser/-/regjsparser-0.6.7.tgz", + "integrity": "sha512-ib77G0uxsA2ovgiYbCVGx4Pv3PSttAx2vIwidqQzbL2U5S4Q+j00HdSAneSBuyVcMvEnTXMjiGgB+DlXozVhpQ==", "dev": true, "requires": { "jsesc": "~0.5.0" @@ -20237,11 +20139,11 @@ "integrity": "sha1-kl0mAdOaxIXgkc8NpcbmlNw9yv8=" }, "resolve": { - "version": "1.19.0", - "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.19.0.tgz", - "integrity": "sha512-rArEXAgsBG4UgRGcynxWIWKFvh/XZCcS8UJdHhwy91zwAvCZIbcs+vAbflgBnNjYMs/i/i+/Ux6IZhML1yPvxg==", + "version": "1.20.0", + "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.20.0.tgz", + "integrity": "sha512-wENBPt4ySzg4ybFQW2TT1zMQucPK95HSh/nq2CFTZVOGut2+pQvSsgtda4d26YrYcr067wjbmzOG8byDPBX63A==", "requires": { - "is-core-module": "^2.1.0", + "is-core-module": "^2.2.0", "path-parse": "^1.0.6" } }, @@ -21165,9 +21067,9 @@ } }, "source-map-url": { - "version": "0.4.0", - "resolved": "https://registry.npmjs.org/source-map-url/-/source-map-url-0.4.0.tgz", - "integrity": "sha1-PpNdfd1zYxuXZZlW1VEo6HtQhKM=", + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/source-map-url/-/source-map-url-0.4.1.tgz", + "integrity": "sha512-cPiFOTLUKvJFIg4SKVScy4ilPPW6rFgMgfuZJPNoDuMs3nC1HbMUycBoJw77xFIp6z1UJQJOfx6C9GMH80DiTw==", "dev": true }, "spdx-correct": { @@ -21329,9 +21231,9 @@ } }, "stackblur-canvas": { - "version": "2.4.0", - "resolved": "https://registry.npmjs.org/stackblur-canvas/-/stackblur-canvas-2.4.0.tgz", - "integrity": "sha512-Z+HixfgYV0ss3C342DxPwc+UvN1SYWqoz7Wsi3xEDWEnaBkSCL3Ey21gF4io+WlLm8/RIrSnCrDBIEcH4O+q5Q==", + "version": "2.5.0", + "resolved": "https://registry.npmjs.org/stackblur-canvas/-/stackblur-canvas-2.5.0.tgz", + "integrity": "sha512-EeNzTVfj+1In7aSLPKDD03F/ly4RxEuF/EX0YcOG0cKoPXs+SLZxDawQbexQDBzwROs4VKLWTOaZQlZkGBFEIQ==", "optional": true }, "static-extend": { @@ -21516,9 +21418,9 @@ } }, "string-width": { - "version": "4.2.0", - "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.0.tgz", - "integrity": "sha512-zUz5JD+tgqtuDjMhwIg5uFVV3dtqZ9yQJlZVfq4I01/K5Paj5UHj7VyrQOJvzawSVlKpObApbfD0Ed6yJc+1eg==", + "version": "4.2.2", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.2.tgz", + "integrity": "sha512-XBJbT3N4JhVumXE0eoLU9DCjcaF92KLNqTmFCnG1pf8duUxFGwtP6AD6nkjw9a3IdiRtL3E2w3JDiE/xi3vOeA==", "dev": true, "requires": { "emoji-regex": "^8.0.0", @@ -21544,37 +21446,37 @@ } }, "string.prototype.matchall": { - "version": "4.0.3", - "resolved": "https://registry.npmjs.org/string.prototype.matchall/-/string.prototype.matchall-4.0.3.tgz", - "integrity": "sha512-OBxYDA2ifZQ2e13cP82dWFMaCV9CGF8GzmN4fljBVw5O5wep0lu4gacm1OL6MjROoUnB8VbkWRThqkV2YFLNxw==", + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/string.prototype.matchall/-/string.prototype.matchall-4.0.4.tgz", + "integrity": "sha512-pknFIWVachNcyqRfaQSeu/FUfpvJTe4uskUSZ9Wc1RijsPuzbZ8TyYT8WCNnntCjUEqQ3vUHMAfVj2+wLAisPQ==", "dev": true, "requires": { - "call-bind": "^1.0.0", + "call-bind": "^1.0.2", "define-properties": "^1.1.3", - "es-abstract": "^1.18.0-next.1", + "es-abstract": "^1.18.0-next.2", "has-symbols": "^1.0.1", - "internal-slot": "^1.0.2", - "regexp.prototype.flags": "^1.3.0", - "side-channel": "^1.0.3" + "internal-slot": "^1.0.3", + "regexp.prototype.flags": "^1.3.1", + "side-channel": "^1.0.4" } }, "string.prototype.trimend": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/string.prototype.trimend/-/string.prototype.trimend-1.0.3.tgz", - "integrity": "sha512-ayH0pB+uf0U28CtjlLvL7NaohvR1amUvVZk+y3DYb0Ey2PUV5zPkkKy9+U1ndVEIXO8hNg18eIv9Jntbii+dKw==", + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/string.prototype.trimend/-/string.prototype.trimend-1.0.4.tgz", + "integrity": "sha512-y9xCjw1P23Awk8EvTpcyL2NIr1j7wJ39f+k6lvRnSMz+mz9CGz9NYPelDk42kOz6+ql8xjfK8oYzy3jAP5QU5A==", "dev": true, "requires": { - "call-bind": "^1.0.0", + "call-bind": "^1.0.2", "define-properties": "^1.1.3" } }, "string.prototype.trimstart": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/string.prototype.trimstart/-/string.prototype.trimstart-1.0.3.tgz", - "integrity": "sha512-oBIBUy5lea5tt0ovtOFiEQaBkoBBkyJhZXzJYrSmDo5IUUqbOPvVezuRs/agBIdZ2p2Eo1FD6bD9USyBLfl3xg==", + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/string.prototype.trimstart/-/string.prototype.trimstart-1.0.4.tgz", + "integrity": "sha512-jh6e984OBfvxS50tdY2nRZnoC5/mLFKOREQfw8t5yytkoUsJRNxvI/E39qu1sD0OtWI3OC0XgKSmcWwziwYuZw==", "dev": true, "requires": { - "call-bind": "^1.0.0", + "call-bind": "^1.0.2", "define-properties": "^1.1.3" } }, @@ -21805,9 +21707,9 @@ } }, "svgo-loader": { - "version": "2.2.1", - "resolved": "https://registry.npmjs.org/svgo-loader/-/svgo-loader-2.2.1.tgz", - "integrity": "sha512-9dyz/h6ae04pAVRz7QY8bLXtMbwA19NPpCPfCixgW0qXNDCOlHbDRqvtT5/2gzRxfuibWCUP6ZBQmZWF9rjWhQ==", + "version": "2.2.2", + "resolved": "https://registry.npmjs.org/svgo-loader/-/svgo-loader-2.2.2.tgz", + "integrity": "sha512-UeE/4yZEK96LoYqvxwh8YqCOJCjXwRY9K6YT99vXE+nYhs/W8hAY2hNf5zg/lRsyKshJkR79V+4beV3cbGL40Q==", "dev": true, "requires": { "js-yaml": "^3.13.1", @@ -22060,6 +21962,121 @@ } } }, + "test-exclude": { + "version": "5.2.3", + "resolved": "https://registry.npmjs.org/test-exclude/-/test-exclude-5.2.3.tgz", + "integrity": "sha512-M+oxtseCFO3EDtAaGH7iiej3CBkzXqFMbzqYAACdzKui4eZA+pq3tZEwChvOdNfa7xxy8BfbmgJSIr43cC/+2g==", + "dev": true, + "requires": { + "glob": "^7.1.3", + "minimatch": "^3.0.4", + "read-pkg-up": "^4.0.0", + "require-main-filename": "^2.0.0" + }, + "dependencies": { + "find-up": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-3.0.0.tgz", + "integrity": "sha512-1yD6RmLI1XBfxugvORwlck6f75tYL+iR0jqwsOrOxMZyGYqUuDhJ0l4AXdO1iX/FTs9cBAMEk1gWSEx1kSbylg==", + "dev": true, + "requires": { + "locate-path": "^3.0.0" + } + }, + "load-json-file": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/load-json-file/-/load-json-file-4.0.0.tgz", + "integrity": "sha1-L19Fq5HjMhYjT9U62rZo607AmTs=", + "dev": true, + "requires": { + "graceful-fs": "^4.1.2", + "parse-json": "^4.0.0", + "pify": "^3.0.0", + "strip-bom": "^3.0.0" + } + }, + "locate-path": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-3.0.0.tgz", + "integrity": "sha512-7AO748wWnIhNqAuaty2ZWHkQHRSNfPVIsPIfwEOWO22AmaoVrWavlOcMR5nzTLNYvp36X220/maaRsrec1G65A==", + "dev": true, + "requires": { + "p-locate": "^3.0.0", + "path-exists": "^3.0.0" + } + }, + "p-limit": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.3.0.tgz", + "integrity": "sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w==", + "dev": true, + "requires": { + "p-try": "^2.0.0" + } + }, + "p-locate": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-3.0.0.tgz", + "integrity": "sha512-x+12w/To+4GFfgJhBEpiDcLozRJGegY+Ei7/z0tSLkMmxGZNybVMSfWj9aJn8Z5Fc7dBUNJOOVgPv2H7IwulSQ==", + "dev": true, + "requires": { + "p-limit": "^2.0.0" + } + }, + "p-try": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/p-try/-/p-try-2.2.0.tgz", + "integrity": "sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ==", + "dev": true + }, + "parse-json": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/parse-json/-/parse-json-4.0.0.tgz", + "integrity": "sha1-vjX1Qlvh9/bHRxhPmKeIy5lHfuA=", + "dev": true, + "requires": { + "error-ex": "^1.3.1", + "json-parse-better-errors": "^1.0.1" + } + }, + "path-type": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/path-type/-/path-type-3.0.0.tgz", + "integrity": "sha512-T2ZUsdZFHgA3u4e5PfPbjd7HDDpxPnQb5jN0SrDsjNSuVXHJqtwTnWqG0B1jZrgmJ/7lj1EmVIByWt1gxGkWvg==", + "dev": true, + "requires": { + "pify": "^3.0.0" + } + }, + "pify": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/pify/-/pify-3.0.0.tgz", + "integrity": "sha1-5aSs0sEB/fPZpNB/DbxNtJ3SgXY=", + "dev": true + }, + "read-pkg": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/read-pkg/-/read-pkg-3.0.0.tgz", + "integrity": "sha1-nLxoaXj+5l0WwA4rGcI3/Pbjg4k=", + "dev": true, + "requires": { + "load-json-file": "^4.0.0", + "normalize-package-data": "^2.3.2", + "path-type": "^3.0.0" + } + }, + "read-pkg-up": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/read-pkg-up/-/read-pkg-up-4.0.0.tgz", + "integrity": "sha512-6etQSH7nJGsK0RbG/2TeDzZFa8shjQ1um+SwQQ5cwKy0dhSXdOncEhb1CPpvQG4h7FyOV6EB6YlV0yJvZQNAkA==", + "dev": true, + "requires": { + "find-up": "^3.0.0", + "read-pkg": "^3.0.0" + } + } + } + }, "text-table": { "version": "0.2.0", "resolved": "https://registry.npmjs.org/text-table/-/text-table-0.2.0.tgz", @@ -22295,9 +22312,9 @@ "integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==" }, "tsutils": { - "version": "3.19.1", - "resolved": "https://registry.npmjs.org/tsutils/-/tsutils-3.19.1.tgz", - "integrity": "sha512-GEdoBf5XI324lu7ycad7s6laADfnAqCw6wLGI+knxvw9vsIYBaJfYdmeCEG3FMMUiSm3OGgNb+m6utsWf5h9Vw==", + "version": "3.21.0", + "resolved": "https://registry.npmjs.org/tsutils/-/tsutils-3.21.0.tgz", + "integrity": "sha512-mHKK3iUXL+3UF6xL5k0PEhKRUBKPBCv/+RkEOpjRWxxx27KKRBmmA60A9pgOUvMi8GKhRMPEmjBRPzs2W7O1OA==", "dev": true, "requires": { "tslib": "^1.8.1" @@ -22373,11 +22390,23 @@ "integrity": "sha512-xlEvjfaTvFnr3ZmwM6fs5/KOKLpAuNksAUxiYfa6dKqZham2oTGm0gI1j/tcgxh1reihhrKbTFfFjtCV9wwxXA==" }, "typescript": { - "version": "3.9.7", - "resolved": "https://registry.npmjs.org/typescript/-/typescript-3.9.7.tgz", - "integrity": "sha512-BLbiRkiBzAwsjut4x/dsibSTB6yWpwT5qWmC2OfuCg3GgVQCSgMs4vEctYPhsaGtd0AeuuHMkjZ2h2WG8MSzRw==", + "version": "3.9.9", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-3.9.9.tgz", + "integrity": "sha512-kdMjTiekY+z/ubJCATUPlRDl39vXYiMV9iyeMuEuXZh2we6zz80uovNN2WlAxmmdE/Z/YQe+EbOEXB5RHEED3w==", "dev": true }, + "unbox-primitive": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/unbox-primitive/-/unbox-primitive-1.0.0.tgz", + "integrity": "sha512-P/51NX+JXyxK/aigg1/ZgyccdAxm5K1+n8+tvqSntjOivPt19gvm1VC49RWYetsiub8WViUchdxl/KWHHB0kzA==", + "dev": true, + "requires": { + "function-bind": "^1.1.1", + "has-bigints": "^1.0.0", + "has-symbols": "^1.0.0", + "which-boxed-primitive": "^1.0.1" + } + }, "unicode-canonical-property-names-ecmascript": { "version": "1.0.4", "resolved": "https://registry.npmjs.org/unicode-canonical-property-names-ecmascript/-/unicode-canonical-property-names-ecmascript-1.0.4.tgz", @@ -22605,9 +22634,9 @@ } }, "url-parse": { - "version": "1.4.7", - "resolved": "https://registry.npmjs.org/url-parse/-/url-parse-1.4.7.tgz", - "integrity": "sha512-d3uaVyzDB9tQoSXFvuSUNFibTd9zxd2bkVrDRvF5TmvWWQwqE4lgYJ5m+x1DbecWkw+LK4RNl2CU1hHuOKPVlg==", + "version": "1.5.1", + "resolved": "https://registry.npmjs.org/url-parse/-/url-parse-1.5.1.tgz", + "integrity": "sha512-HOfCOUJt7iSYzEx/UqgtwKRMC6EU91NFhsCHMv9oM03VJcVo2Qrp8T8kI9D7amFf1cu+/3CEhgb3rF9zL7k85Q==", "requires": { "querystringify": "^2.1.1", "requires-port": "^1.0.0" @@ -22620,9 +22649,9 @@ "dev": true }, "use-memo-one": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/use-memo-one/-/use-memo-one-1.1.1.tgz", - "integrity": "sha512-oFfsyun+bP7RX8X2AskHNTxu+R3QdE/RC5IefMbqptmACAA/gfol1KDD5KRzPsGMa62sWxGZw+Ui43u6x4ddoQ==" + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/use-memo-one/-/use-memo-one-1.1.2.tgz", + "integrity": "sha512-u2qFKtxLsia/r8qG0ZKkbytbztzRb317XCkT7yP8wxL0tZ/CzK2G+WWie5vWvpyeP7+YoPIwbJoIHJ4Ba4k0oQ==" }, "util": { "version": "0.11.1", @@ -22656,27 +22685,6 @@ "es-abstract": "^1.17.2", "has-symbols": "^1.0.1", "object.getownpropertydescriptors": "^2.1.0" - }, - "dependencies": { - "es-abstract": { - "version": "1.17.7", - "resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.17.7.tgz", - "integrity": "sha512-VBl/gnfcJ7OercKA9MVaegWsBHFjV492syMudcnQZvt/Dw8ezpcOHYZXa/J96O8vx+g4x65YKhxOwDUh63aS5g==", - "dev": true, - "requires": { - "es-to-primitive": "^1.2.1", - "function-bind": "^1.1.1", - "has": "^1.0.3", - "has-symbols": "^1.0.1", - "is-callable": "^1.2.2", - "is-regex": "^1.1.1", - "object-inspect": "^1.8.0", - "object-keys": "^1.1.1", - "object.assign": "^4.1.1", - "string.prototype.trimend": "^1.0.1", - "string.prototype.trimstart": "^1.0.1" - } - } } }, "utila": { @@ -22698,9 +22706,9 @@ "dev": true }, "v8-compile-cache": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/v8-compile-cache/-/v8-compile-cache-2.2.0.tgz", - "integrity": "sha512-gTpR5XQNKFwOd4clxfnhaqvfqMpqEwr4tOtCyz4MtYZX2JYhfr1JvBFKdS+7K/9rfpZR3VLX+YWBbKoxCgS43Q==", + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/v8-compile-cache/-/v8-compile-cache-2.3.0.tgz", + "integrity": "sha512-l8lCEmLcLYZh4nbunNZvQCJc5pv7+RCwa8q/LdUx8u7lsWvPDKmpodJAJNwkAhJC//dFY48KuIEmjtd4RViDrA==", "dev": true }, "validate-npm-package-license": { @@ -23258,9 +23266,9 @@ } }, "whatwg-fetch": { - "version": "3.5.0", - "resolved": "https://registry.npmjs.org/whatwg-fetch/-/whatwg-fetch-3.5.0.tgz", - "integrity": "sha512-jXkLtsR42xhXg7akoDKvKWE40eJeI+2KZqcp2h3NsOrRnDvtWX36KcKl30dy+hxECivdk2BVUHVNrPtoMBUx6A==", + "version": "3.6.2", + "resolved": "https://registry.npmjs.org/whatwg-fetch/-/whatwg-fetch-3.6.2.tgz", + "integrity": "sha512-bJlen0FcuU/0EMLrdbJ7zOnW6ITZLrZMIarMUVmdKtsGvZna8vxKYaexICWPfZ8qwf9fzNq+UEIZrnSaApt6RA==", "dev": true }, "whatwg-mimetype": { @@ -23289,6 +23297,19 @@ "isexe": "^2.0.0" } }, + "which-boxed-primitive": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/which-boxed-primitive/-/which-boxed-primitive-1.0.2.tgz", + "integrity": "sha512-bwZdv0AKLpplFY2KZRX6TvyuN7ojjr7lwkg6ml0roIy9YeuSr7JS372qlNW18UQYzgYK9ziGcerWqZOmEn9VNg==", + "dev": true, + "requires": { + "is-bigint": "^1.0.1", + "is-boolean-object": "^1.1.0", + "is-number-object": "^1.0.4", + "is-string": "^1.0.5", + "is-symbol": "^1.0.3" + } + }, "which-module": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/which-module/-/which-module-2.0.0.tgz", @@ -23489,9 +23510,9 @@ } }, "worker-loader": { - "version": "3.0.7", - "resolved": "https://registry.npmjs.org/worker-loader/-/worker-loader-3.0.7.tgz", - "integrity": "sha512-LjYLuYJw6kqQKDoygpoD5vWeR1CbZjuVSW3/8pFsptMlUl8gatNM/pszhasSDAWt+dYxMipWB6695k+1zId+iQ==", + "version": "3.0.8", + "resolved": "https://registry.npmjs.org/worker-loader/-/worker-loader-3.0.8.tgz", + "integrity": "sha512-XQyQkIFeRVC7f7uRhFdNMe/iJOdO6zxAaR3EWbDp45v3mDhrTi+++oswKNxShUNjPC/1xUp5DB29YKLhFo129g==", "dev": true, "requires": { "loader-utils": "^2.0.0", diff --git a/package.json b/package.json index 615c8538..bc959738 100644 --- a/package.json +++ b/package.json @@ -1,13 +1,13 @@ { "name": "portal-core-components", - "version": "1.6.3", + "version": "1.6.4", "main": "./lib/index.js", "private": true, "homepage": "http://localhost:3010/core-components", "dependencies": { "@date-io/moment": "^1.3.9", - "@material-ui/core": "^4.11.0", - "@material-ui/icons": "^4.9.1", + "@material-ui/core": "^4.11.3", + "@material-ui/icons": "^4.11.2", "@material-ui/lab": "^4.0.0-alpha.56", "@material-ui/pickers": "^3.2.10", "@material-ui/styles": "^4.10.0", @@ -105,9 +105,12 @@ "verbose": true, "moduleNameMapper": { "typeface-inter": "/src/__mocks__/fileMock.js", - "\\.(jpg|jpeg|png|gif|eot|otf|webp|svg|ttf|woff|woff2|mp4|webm|wav|mp3|m4a|aac|oga)$": "/src/__mocks__/fileMock.js", "\\.(css|less)$": "/src/__mocks__/styleMock.js" }, + "transform": { + "\\.(js|jsx|ts|tsx)$": "babel-jest", + "\\.(jpg|jpeg|png|gif|eot|otf|webp|svg|ttf|woff|woff2|mp4|webm|wav|mp3|m4a|aac|oga)$": "/src/__mocks__/fileTransformer.js" + }, "setupFiles": [ "jest-canvas-mock" ], @@ -132,6 +135,8 @@ "/node_modules/", "/src/lib_components/remoteAssets/", "/src/lib_components/types/", + "/src/lib_components/images/", + "/src/lib_components/components/SiteMap/svg/", "src/sampleData", "StyleGuide" ] diff --git a/src/__mocks__/fileTransformer.js b/src/__mocks__/fileTransformer.js new file mode 100644 index 00000000..dee01da5 --- /dev/null +++ b/src/__mocks__/fileTransformer.js @@ -0,0 +1,7 @@ +const path = require('path'); + +module.exports = { + process(src, filename) { + return `module.exports = ${JSON.stringify(path.basename(filename))};`; + }, +}; diff --git a/src/components/PropsTable.jsx b/src/components/PropsTable.jsx index c0f61886..8d91da7e 100644 --- a/src/components/PropsTable.jsx +++ b/src/components/PropsTable.jsx @@ -25,11 +25,11 @@ const useStyles = makeStyles((theme) => ({ })); const PropsTable = (props) => { - const { props: propRows } = props; + const { props: propRows, fullHeight } = props; const classes = useStyles(Theme); return ( - + @@ -74,6 +74,11 @@ PropsTable.propTypes = { required: PropTypes.bool, }), ).isRequired, + fullHeight: PropTypes.bool, +}; + +PropsTable.defaultProps = { + fullHeight: false, }; export default PropsTable; diff --git a/src/components/__tests__/PropsTable.jsx b/src/components/__tests__/PropsTable.jsx index 5ce38daa..0bd622a0 100644 --- a/src/components/__tests__/PropsTable.jsx +++ b/src/components/__tests__/PropsTable.jsx @@ -48,4 +48,22 @@ describe('PropsTable', () => { const tree = renderer.create().toJSON(); expect(tree).toMatchSnapshot(); }); + test('renders at full height', () => { + const props = [ + { + name: 'foo', + type: 'string', + description: 'lorem ipsum', + examples: '"abc", "def"', + required: true, + }, + { + name: 'bar', + type:
type node
, + description:
description node
, + }, + ]; + const tree = renderer.create().toJSON(); + expect(tree).toMatchSnapshot(); + }); }); diff --git a/src/components/__tests__/__snapshots__/PropsTable.jsx.snap b/src/components/__tests__/__snapshots__/PropsTable.jsx.snap index e0d5e045..e27d61fb 100644 --- a/src/components/__tests__/__snapshots__/PropsTable.jsx.snap +++ b/src/components/__tests__/__snapshots__/PropsTable.jsx.snap @@ -419,3 +419,161 @@ exports[`PropsTable renders a multiple rows with optional strings 1`] = `
`; + +exports[`PropsTable renders at full height 1`] = ` +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ Prop + + Type + + Default + + Examples +
+ + foo + +
+ REQUIRED +
+
+ string + + + + "abc", "def" +
+ lorem ipsum +
+ + bar + + +
+ type node +
+
+ + + -- +
+
+ description node +
+
+
+`; diff --git a/src/lib_components/components/AopDataViewer/__tests__/__snapshots__/AopDataViewer.jsx.snap b/src/lib_components/components/AopDataViewer/__tests__/__snapshots__/AopDataViewer.jsx.snap index 7b74223a..2261a3b0 100644 --- a/src/lib_components/components/AopDataViewer/__tests__/__snapshots__/AopDataViewer.jsx.snap +++ b/src/lib_components/components/AopDataViewer/__tests__/__snapshots__/AopDataViewer.jsx.snap @@ -331,7 +331,7 @@ exports[`AopDataViewer renders with a specified initial site 1`] = ` - San Joaquin + San Joaquin Experimental Range - San Joaquin + San Joaquin Experimental Range - San Joaquin + San Joaquin Experimental Range @@ -2313,7 +2313,7 @@ exports[`AopDataViewer renders with only a productCode 1`] = ` - San Joaquin + San Joaquin Experimental Range - San Joaquin + San Joaquin Experimental Range TIME.START_YEAR + idx); diff --git a/src/lib_components/components/DataProductAvailability/BasicAvailabilityGrid.js b/src/lib_components/components/DataProductAvailability/BasicAvailabilityGrid.js index 93aa0f67..086e47df 100644 --- a/src/lib_components/components/DataProductAvailability/BasicAvailabilityGrid.js +++ b/src/lib_components/components/DataProductAvailability/BasicAvailabilityGrid.js @@ -126,8 +126,7 @@ export default function BasicAvailabilityGrid(config) { const getYearStartX = (year) => { const intYear = parseInt(year, 10); return getLabelWidth() + SVG.END_PADDING - + (TIME.YEARS.indexOf(intYear) * SVG.YEAR_WIDTH) - + (TIME.YEARS.indexOf(intYear) * SVG.YEAR_PADDING); + + (TIME.YEARS.indexOf(intYear) * (SVG.YEAR_WIDTH + SVG.YEAR_PADDING)); }; const getYearCenterX = (year) => getYearStartX(year) + (SVG.YEAR_WIDTH / 2); const getYearMonthStartX = (yearMonth) => { @@ -293,7 +292,8 @@ export default function BasicAvailabilityGrid(config) { }; // Get the yearMonth string that's next to a given yearMonth on either side. // Stays within the selectable range unless selectable is false, in which case - // is stays within the chart's global min and max. + // it stays within the chart's global min and max. + /* const getAdjacentYearMonth = (yearMonth, side = 'left', selectable = true) => { const year = parseInt(yearMonth.substr(0, 4), 10); const month = parseInt(yearMonth.substr(5, 2), 10); @@ -318,6 +318,39 @@ export default function BasicAvailabilityGrid(config) { return adjacent; } }; + */ + // Get the string for the yearmonth closest to a given pixel x-offset on either side. + // Stays within the selectable range unless selectable is false, in which case + // it stays within the chart's global min and max. + const getXOffsetYearMonth = (xOffset, dragOffset, side = 'left', selectable = true) => { + /***/ + const basis = Math.min(Math.max(xOffset, getLabelWidth()), svgWidth) - dragOffset; + const yearIdx = Math.floor((basis - getLabelWidth()) / (SVG.YEAR_WIDTH + SVG.YEAR_PADDING)); + const boundedYearIdx = Math.max(Math.min(yearIdx, TIME.YEARS.length - 1), 0); + let year = TIME.YEARS[boundedYearIdx]; + let yearStartX = getYearStartX(year); + if (basis < yearStartX && side === 'left' && boundedYearIdx !== 0) { + year = parseInt(year, 10) - 1; + yearStartX = getYearStartX(year); + } else if ( + basis > (yearStartX + SVG.YEAR_WIDTH) + && side === 'right' + && boundedYearIdx !== (TIME.YEARS.length - 1) + ) { + year = parseInt(year, 10) + 1; + yearStartX = getYearStartX(year); + } + const roundFunc = side === 'left' ? 'floor' : 'ceil'; + const month = Math[roundFunc]( + (basis - yearStartX) / SVG.YEAR_MONTH_WIDTH, + ) + 1; + const boundedMonth = Math.max(Math.min(month, 12), 1); + const yearMonth = `${year}-${boundedMonth.toString().padStart(2, '0')}`; + const bounds = selectable ? dateRange.validValues : [TIME.MIN_YEAR_MONTH, TIME.MAX_YEAR_MONTH]; + if (yearMonth < bounds[0]) { return bounds[0]; } + if (yearMonth > bounds[1]) { return bounds[1]; } + return yearMonth; + }; /** SVG: Row Hover @@ -654,7 +687,7 @@ export default function BasicAvailabilityGrid(config) { .attr('y', 0) .attr('width', SVG.DATE_RANGE_MASK_WIDTH) .attr('height', svgHeight) - .style('cursor', 'ew-resize') + .style('cursor', 'crosshair') // 'ew-resize' .style('outline', 'none') .attr('fill', 'red') .style('opacity', 0) @@ -828,15 +861,16 @@ export default function BasicAvailabilityGrid(config) { }) .on('drag', () => { draggingDateRange[0].centerDragX += event.dx; - dragDateRangeStartMask.attr('x', draggingDateRange[0].centerDragX - (SVG.DATE_RANGE_MASK_WIDTH / 2)); - const adjacentYearMonth = getAdjacentYearMonth(dateRange.value[0], event.dx > 0 ? 'right' : 'left'); - const adjacentYearMonthStartX = getYearMonthGutterX(adjacentYearMonth, 'left'); + const { centerDragX } = draggingDateRange[0]; + dragDateRangeStartMask.attr('x', centerDragX - (SVG.DATE_RANGE_MASK_WIDTH / 2)); + const nextYearMonth = getXOffsetYearMonth(centerDragX, getTimeOffset(), 'right'); + const nextYearMonthStartX = getYearMonthGutterX(nextYearMonth, 'left'); const currentYearMonthStartX = getYearMonthGutterX(dateRange.value[0], 'left'); const insideClipCenterDragX = draggingDateRange[0].centerDragX - getTimeOffset(); - const distanceToAdjacent = Math.abs(insideClipCenterDragX - adjacentYearMonthStartX); + const distanceToNext = Math.abs(insideClipCenterDragX - nextYearMonthStartX); const distanceToCurrent = Math.abs(insideClipCenterDragX - currentYearMonthStartX); - if (adjacentYearMonth !== dateRange.value[0] && distanceToAdjacent < distanceToCurrent) { - dateRange.value[0] = adjacentYearMonth; + if (nextYearMonth !== dateRange.value[0] && distanceToNext < distanceToCurrent) { + dateRange.value[0] = nextYearMonth; redrawSelections(); } }) @@ -869,15 +903,16 @@ export default function BasicAvailabilityGrid(config) { }) .on('drag', () => { draggingDateRange[1].centerDragX += event.dx; - dragDateRangeEndMask.attr('x', draggingDateRange[1].centerDragX - (SVG.DATE_RANGE_MASK_WIDTH / 2)); - const adjacentYearMonth = getAdjacentYearMonth(dateRange.value[1], event.dx > 0 ? 'right' : 'left'); - const adjacentYearMonthEndX = getYearMonthGutterX(adjacentYearMonth, 'right'); + const { centerDragX } = draggingDateRange[1]; + dragDateRangeEndMask.attr('x', centerDragX - (SVG.DATE_RANGE_MASK_WIDTH / 2)); + const nextYearMonth = getXOffsetYearMonth(centerDragX, getTimeOffset(), 'left'); + const nextYearMonthEndX = getYearMonthGutterX(nextYearMonth, 'right'); const currentYearMonthEndX = getYearMonthGutterX(dateRange.value[1], 'right'); const insideClipCenterDragX = draggingDateRange[1].centerDragX - getTimeOffset(); - const distanceToAdjacent = Math.abs(insideClipCenterDragX - adjacentYearMonthEndX); + const distanceToNext = Math.abs(insideClipCenterDragX - nextYearMonthEndX); const distanceToCurrent = Math.abs(insideClipCenterDragX - currentYearMonthEndX); - if (adjacentYearMonth !== dateRange.value[1] && distanceToAdjacent < distanceToCurrent) { - dateRange.value[1] = adjacentYearMonth; + if (nextYearMonth !== dateRange.value[1] && distanceToNext < distanceToCurrent) { + dateRange.value[1] = nextYearMonth; redrawSelections(); } }) diff --git a/src/lib_components/components/DataProductAvailability/__tests__/AvailabilityContext.jsx b/src/lib_components/components/DataProductAvailability/__tests__/AvailabilityContext.jsx index 1978d8ec..3c8c7d73 100644 --- a/src/lib_components/components/DataProductAvailability/__tests__/AvailabilityContext.jsx +++ b/src/lib_components/components/DataProductAvailability/__tests__/AvailabilityContext.jsx @@ -153,8 +153,8 @@ describe('DataProductAvailability - AvailabilityContext', () => { }, }, rowTitles: { - 'AK-D18-BARR-T1': 'Alaska - Tundra - Utqiaġvik Environmental Observatory - t1', - 'AK-D18-BARR-T3': 'Alaska - Tundra - Utqiaġvik Environmental Observatory - t3', + 'AK-D18-BARR-T1': 'Alaska - Tundra - Utqiaġvik - t1', + 'AK-D18-BARR-T3': 'Alaska - Tundra - Utqiaġvik - t3', 'TX-D11-CLBJ-T2': 'Texas - Southern Plains - LBJ National Grassland - t2', 'TX-D11-CLBJ-T3': 'Texas - Southern Plains - LBJ National Grassland - t3', 'WA-D16-ABBY-T1': 'Washington - Pacific Northwest - Abby Road - t1', diff --git a/src/lib_components/components/DataThemeIcon/DataThemeIcon.jsx b/src/lib_components/components/DataThemeIcon/DataThemeIcon.jsx index 8eae5eac..244dff5e 100644 --- a/src/lib_components/components/DataThemeIcon/DataThemeIcon.jsx +++ b/src/lib_components/components/DataThemeIcon/DataThemeIcon.jsx @@ -29,7 +29,7 @@ const dataThemes = { }, landcover: { title: 'Land Cover & Processes', - aliases: ['landuse', 'Land Use, Land Cover, and Land Processes'], + aliases: ['landuse', 'Land Cover and Processes', 'Land Use, Land Cover, and Land Processes'], src: LandCoverSVG, }, organisms: { @@ -47,10 +47,12 @@ const DataThemeIcon = (props) => { className, ...other } = props; - const dataTheme = dataThemes[theme] - || Object.values(dataThemes).find( - (entry) => theme === entry.title || entry.aliases.includes(theme), - ); + const dataTheme = dataThemes[theme] || Object.values(dataThemes).find( + (entry) => theme === entry.title || entry.aliases.includes(theme), + ); + + if (!dataTheme) { return null; } + const elementProps = { src: dataTheme.src, alt: dataTheme.title, diff --git a/src/lib_components/components/DataThemeIcon/__tests__/__snapshots__/DataThemeIcon.jsx.snap b/src/lib_components/components/DataThemeIcon/__tests__/__snapshots__/DataThemeIcon.jsx.snap index e48e0c24..274c510e 100644 --- a/src/lib_components/components/DataThemeIcon/__tests__/__snapshots__/DataThemeIcon.jsx.snap +++ b/src/lib_components/components/DataThemeIcon/__tests__/__snapshots__/DataThemeIcon.jsx.snap @@ -15,7 +15,7 @@ exports[`DataThemeIcon renders in avatar mode 1`] = ` Land Cover & Processes `; @@ -26,7 +26,7 @@ exports[`DataThemeIcon renders with a custom className 1`] = ` className="foo" data-selenium="data-theme-icon.organisms" height={40} - src="test-file-stub" + src="organisms.svg" title="Organisms, Populations, and Communities" width={40} /> @@ -38,7 +38,7 @@ exports[`DataThemeIcon renders with custom size 1`] = ` className={null} data-selenium="data-theme-icon.ecohydrology" height={32} - src="test-file-stub" + src="ecohydrology.svg" title="Ecohydrology" width={32} /> @@ -50,7 +50,7 @@ exports[`DataThemeIcon renders with only a theme prop 1`] = ` className={null} data-selenium="data-theme-icon.atmosphere" height={40} - src="test-file-stub" + src="atmosphere.svg" title="Atmosphere" width={40} /> @@ -62,7 +62,7 @@ exports[`DataThemeIcon renders with only a theme prop alias 1`] = ` className={null} data-selenium="data-theme-icon.biogeo" height={40} - src="test-file-stub" + src="biogeochemistry.svg" title="Biogeochemistry" width={40} /> diff --git a/src/lib_components/components/DownloadDataContext/DownloadDataContext.jsx b/src/lib_components/components/DownloadDataContext/DownloadDataContext.jsx index c8f58642..0f4edf2f 100644 --- a/src/lib_components/components/DownloadDataContext/DownloadDataContext.jsx +++ b/src/lib_components/components/DownloadDataContext/DownloadDataContext.jsx @@ -1189,5 +1189,7 @@ export const getTestableItems = () => ( regenerateS3FilesFiltersAndValidValues, getAndValidateNewState, ALL_POSSIBLE_VALID_DATE_RANGE, + ALL_POSSIBLE_VALID_DOCUMENTATION, + ALL_POSSIBLE_VALID_PACKAGE_TYPE, } ); diff --git a/src/lib_components/components/DownloadDataContext/__tests__/DownloadDataContext.jsx b/src/lib_components/components/DownloadDataContext/__tests__/DownloadDataContext.jsx index 5f5c7b1b..6a7bb9e9 100644 --- a/src/lib_components/components/DownloadDataContext/__tests__/DownloadDataContext.jsx +++ b/src/lib_components/components/DownloadDataContext/__tests__/DownloadDataContext.jsx @@ -23,13 +23,15 @@ const { newStateIsValid, mutateNewStateIntoRange, estimatePostSize, - // getValidValuesFromProductData, - // getInitialStateFromProps, + getValidValuesFromProductData, + getInitialStateFromProps, // getS3FilesFilteredFileCount, // getAndValidateNewS3FilesState, // regenerateS3FilesFiltersAndValidValues, // getAndValidateNewState, ALL_POSSIBLE_VALID_DATE_RANGE, + ALL_POSSIBLE_VALID_DOCUMENTATION, + ALL_POSSIBLE_VALID_PACKAGE_TYPE, } = getTestableItems(); describe('DownloadDataContext', () => { @@ -288,17 +290,217 @@ describe('DownloadDataContext', () => { }); }); - /* describe('getValidValuesFromProductData()', () => { - test('', () => { + test('extracts releases', () => { + expect(getValidValuesFromProductData({}, 'release')).toStrictEqual([]); + const productData = { + releases: [{ release: 'foo' }, { release: 'bar' }], + }; + expect(getValidValuesFromProductData(productData, 'release')).toStrictEqual(['foo', 'bar']); + }); + test('extracts sites', () => { + expect(getValidValuesFromProductData({}, 'sites')).toStrictEqual([]); + const productData = { + siteCodes: [{ siteCode: 'foo' }, { siteCode: 'bar' }], + }; + expect(getValidValuesFromProductData(productData, 'sites')).toStrictEqual(['foo', 'bar']); + }); + test('extracts dateRange', () => { + expect(getValidValuesFromProductData({}, 'dateRange')).toStrictEqual([null, null]); + let productData = { + siteCodes: [ + { siteCode: 'foo' }, + { siteCode: 'bar' }, + ], + }; + expect(getValidValuesFromProductData(productData, 'dateRange')).toStrictEqual([null, null]); + productData = { + siteCodes: [ + { siteCode: 'foo', availableMonths: ['2015-04', '2015-08', '2015-09'] }, + { siteCode: 'bar', availableMonths: ['2015-07', '2015-10', '2015-11'] }, + ], + }; + expect(getValidValuesFromProductData(productData, 'dateRange')) + .toStrictEqual(['2015-04', '2015-11']); + }); + test('extracts documentation', () => { + expect(getValidValuesFromProductData({}, 'documentation')).toStrictEqual([ + ...ALL_POSSIBLE_VALID_DOCUMENTATION, + ]); + }); + test('extracts packageType', () => { + expect(getValidValuesFromProductData({}, 'packageType')).toStrictEqual([ + ...ALL_POSSIBLE_VALID_PACKAGE_TYPE, + ]); + }); + test('returns null for policies', () => { + expect(getValidValuesFromProductData({}, 'policies')).toBe(null); + }); + test('returns empty array for unrecognized attribute', () => { + expect(getValidValuesFromProductData({}, 'foo')).toStrictEqual([]); }); }); describe('getInitialStateFromProps()', () => { - test('', () => { + test('returns default state with no required steps if productData is invalid', () => { + const initialState = getInitialStateFromProps({ + productData: {}, + availabilityView: 'domains', + }); + expect(initialState.availabilityView).toBe('domains'); + expect(initialState.requiredSteps).toStrictEqual([]); + }); + test('builds required steps appropriately when AOP pipeline is detected', () => { + const props = { + productData: { + productName: 'foo', + siteCodes: [ + { siteCode: 'foo', availableMonths: ['2015-04', '2015-08', '2015-09'] }, + { siteCode: 'bar', availableMonths: ['2015-07', '2015-10', '2015-11'] }, + ], + productScienceTeam: 'AOP', + productPublicationFormatType: 'AOP', + releases: [ + { release: 'r0', generationDate: '2019-01-01T00:00:00' }, + { release: 'r1', generationDate: '2020-01-01T00:00:00' }, + ], + }, + availabilityView: 'domains', + }; + const initialState = getInitialStateFromProps(props); + expect(initialState.availabilityView).toBe(props.availabilityView); + expect(initialState.productData).toStrictEqual(props.productData); + expect(initialState.latestRelease).toBe('r1'); + expect(initialState.fromManifest).toBe(false); + expect(initialState.fromAOPManifest).toBe(true); + expect(initialState.fromExternalHost).toBe(false); + expect(initialState.requiredSteps).toStrictEqual([ + { key: 'sitesAndDateRange', isComplete: false }, + { key: 's3Files', isComplete: false }, + { key: 'documentation', isComplete: true }, + { key: 'policies', isComplete: false }, + { key: 'summary', isComplete: null }, + ]); + expect(initialState.s3FileFetches).toStrictEqual({ + 'foo.2015-04': 'notRequested', + 'foo.2015-08': 'notRequested', + 'foo.2015-09': 'notRequested', + 'bar.2015-07': 'notRequested', + 'bar.2015-10': 'notRequested', + 'bar.2015-11': 'notRequested', + }); + }); + test('removes packageType required step is product does not have expanded data', () => { + const props = { + productData: { + productName: 'foo', + productHasExpanded: false, + siteCodes: [], + releases: [ + { release: 'r1', generationDate: '2020-01-01T00:00:00' }, + { release: 'r0', generationDate: '2019-01-01T00:00:00' }, + ], + }, + availabilityView: 'states', + }; + const initialState = getInitialStateFromProps(props); + expect(initialState.availabilityView).toBe(props.availabilityView); + expect(initialState.productData).toStrictEqual(props.productData); + expect(initialState.latestRelease).toBe('r1'); + expect(initialState.fromManifest).toBe(true); + expect(initialState.fromAOPManifest).toBe(false); + expect(initialState.fromExternalHost).toBe(false); + expect(initialState.requiredSteps).toStrictEqual([ + { key: 'sitesAndDateRange', isComplete: false }, + { key: 'documentation', isComplete: true }, + { key: 'policies', isComplete: false }, + { key: 'summary', isComplete: null }, + ]); + }); + test('preserves packageType required step is product has expanded data', () => { + const props = { + productData: { + productName: 'foo', + productHasExpanded: true, + siteCodes: [], + releases: [ + { release: 'r1', generationDate: '2020-01-01T00:00:00' }, + { release: 'r0', generationDate: '2019-01-01T00:00:00' }, + ], + }, + availabilityView: 'states', + }; + const initialState = getInitialStateFromProps(props); + expect(initialState.availabilityView).toBe(props.availabilityView); + expect(initialState.productData).toStrictEqual(props.productData); + expect(initialState.latestRelease).toBe('r1'); + expect(initialState.fromManifest).toBe(true); + expect(initialState.fromAOPManifest).toBe(false); + expect(initialState.fromExternalHost).toBe(false); + expect(initialState.requiredSteps).toStrictEqual([ + { key: 'sitesAndDateRange', isComplete: false }, + { key: 'documentation', isComplete: true }, + { key: 'packageType', isComplete: false }, + { key: 'policies', isComplete: false }, + { key: 'summary', isComplete: null }, + ]); + }); + test('properly detects externally hosted products - reformatted data', () => { + const props = { + productData: { + productName: 'foo', + productCode: 'DP1.00001.001', // Host-type: reformatted data + productHasExpanded: true, + siteCodes: [], + releases: [ + { release: 'r1', generationDate: '2020-01-01T00:00:00' }, + { release: 'r0', generationDate: '2019-01-01T00:00:00' }, + ], + }, + availabilityView: 'sites', + }; + const initialState = getInitialStateFromProps(props); + expect(initialState.availabilityView).toBe(props.availabilityView); + expect(initialState.productData).toStrictEqual(props.productData); + expect(initialState.latestRelease).toBe('r1'); + expect(initialState.fromManifest).toBe(true); + expect(initialState.fromAOPManifest).toBe(false); + expect(initialState.fromExternalHost).toBe(true); + expect(initialState.requiredSteps).toStrictEqual([ + { key: 'sitesAndDateRange', isComplete: false }, + { key: 'documentation', isComplete: true }, + { key: 'packageType', isComplete: false }, + { key: 'policies', isComplete: false }, + { key: 'summary', isComplete: null }, + ]); + }); + test('properly detects externally hosted products - exclusive data', () => { + const props = { + productData: { + productName: 'foo', + productCode: 'DP1.00043.001', // Host-type: exclusive data + productHasExpanded: true, + siteCodes: [], + releases: [ + { release: 'r1', generationDate: '2020-01-01T00:00:00' }, + ], + }, + availabilityView: 'sites', + }; + const initialState = getInitialStateFromProps(props); + expect(initialState.availabilityView).toBe(props.availabilityView); + expect(initialState.productData).toStrictEqual(props.productData); + expect(initialState.latestRelease).toBe('r1'); + expect(initialState.fromManifest).toBe(false); + expect(initialState.fromAOPManifest).toBe(false); + expect(initialState.fromExternalHost).toBe(true); + expect(initialState.requiredSteps).toStrictEqual([ + { key: 'externalExclusive', isComplete: null }, + ]); }); }); + /* describe('getS3FilesFilteredFileCount()', () => { test('', () => { }); diff --git a/src/lib_components/components/ExternalHostProductSpecificLinks/__tests__/__snapshots__/ExternalHostProductSpecificLinks.jsx.snap b/src/lib_components/components/ExternalHostProductSpecificLinks/__tests__/__snapshots__/ExternalHostProductSpecificLinks.jsx.snap index 9cecaa77..02e314d7 100644 --- a/src/lib_components/components/ExternalHostProductSpecificLinks/__tests__/__snapshots__/ExternalHostProductSpecificLinks.jsx.snap +++ b/src/lib_components/components/ExternalHostProductSpecificLinks/__tests__/__snapshots__/ExternalHostProductSpecificLinks.jsx.snap @@ -93,7 +93,7 @@ exports[`ExternalHostProductSpecificLinks renders with valid product code and no rel="noopener noreferrer" target="_blank" > - BARR - - Utqiaġvik Environmental Observatory + BARR - - Utqiaġvik
  • @@ -287,7 +287,7 @@ exports[`ExternalHostProductSpecificLinks renders with valid product code and no rel="noopener noreferrer" target="_blank" > - RMNP - - Rocky Mountain National Park + RMNP - - Rocky Mountain National Park, CASTNET
  • @@ -301,7 +301,7 @@ exports[`ExternalHostProductSpecificLinks renders with valid product code and no rel="noopener noreferrer" target="_blank" > - STER - - Sterling + STER - - North Sterling, CO
  • @@ -411,7 +411,7 @@ exports[`ExternalHostProductSpecificLinks renders with valid product code and no rel="noopener noreferrer" target="_blank" > - PUUM - - Pu-u Maka-ala Natural Area Reserve + PUUM - - Pu'u Maka'ala Natural Area Reserve @@ -443,7 +443,7 @@ exports[`ExternalHostProductSpecificLinks renders with valid product code and no rel="noopener noreferrer" target="_blank" > - KONA - - Konza Prairie Biological Station + KONA - - Konza Prairie Biological Station - Relocatable
  • @@ -801,7 +801,7 @@ exports[`ExternalHostProductSpecificLinks renders with valid product code and no rel="noopener noreferrer" target="_blank" > - GRSM - - Great Smoky Mountains National Park + GRSM - - Great Smoky Mountains National Park, Twin Creeks
  • @@ -1077,7 +1077,7 @@ exports[`ExternalHostProductSpecificLinks renders with valid product code and no rel="noopener noreferrer" target="_blank" > - YELL - - Yellowstone National Park + YELL - - Yellowstone Nothern Range (Frog Rock)
  • @@ -1156,7 +1156,7 @@ exports[`ExternalHostProductSpecificLinks renders with valid product code and no rel="noopener noreferrer" target="_blank" > - BLWA - Black Warrior River + BLWA - Black Warrior River near Dead Lake
  • @@ -1226,7 +1226,7 @@ exports[`ExternalHostProductSpecificLinks renders with valid product code and no rel="noopener noreferrer" target="_blank" > - TOMB - Tombigbee River + TOMB - Lower Tombigbee River at Choctaw Refuge
  • @@ -1258,7 +1258,7 @@ exports[`ExternalHostProductSpecificLinks renders with valid product code and no rel="noopener noreferrer" target="_blank" > - BARR - Utqiaġvik Environmental Observatory + BARR - Utqiaġvik
  • @@ -1286,7 +1286,7 @@ exports[`ExternalHostProductSpecificLinks renders with valid product code and no rel="noopener noreferrer" target="_blank" > - CARI - Caribou Creek at Caribou-Poker Creeks Research Watershed + CARI - Caribou Creek, Caribou-Poker Creeks Research Watershed
  • @@ -1448,7 +1448,7 @@ exports[`ExternalHostProductSpecificLinks renders with valid product code and no rel="noopener noreferrer" target="_blank" > - SJER - San Joaquin + SJER - San Joaquin Experimental Range
  • @@ -1578,7 +1578,7 @@ exports[`ExternalHostProductSpecificLinks renders with valid product code and no rel="noopener noreferrer" target="_blank" > - RMNP - Rocky Mountain National Park + RMNP - Rocky Mountain National Park, CASTNET
  • @@ -1592,7 +1592,7 @@ exports[`ExternalHostProductSpecificLinks renders with valid product code and no rel="noopener noreferrer" target="_blank" > - STER - Sterling + STER - North Sterling, CO
  • @@ -1638,7 +1638,7 @@ exports[`ExternalHostProductSpecificLinks renders with valid product code and no rel="noopener noreferrer" target="_blank" > - BARC - Barco Lake + BARC - Ordway-Swisher Biological Station - Barco Lake
  • @@ -1680,7 +1680,7 @@ exports[`ExternalHostProductSpecificLinks renders with valid product code and no rel="noopener noreferrer" target="_blank" > - SUGG - Suggs Lake + SUGG - Ordway-Swisher Biological Station - Suggs Lake
  • @@ -1758,7 +1758,7 @@ exports[`ExternalHostProductSpecificLinks renders with valid product code and no rel="noopener noreferrer" target="_blank" > - PUUM - Pu-u Maka-ala Natural Area Reserve + PUUM - Pu'u Maka'ala Natural Area Reserve @@ -1804,7 +1804,7 @@ exports[`ExternalHostProductSpecificLinks renders with valid product code and no rel="noopener noreferrer" target="_blank" > - KONA - Konza Prairie Biological Station + KONA - Konza Prairie Biological Station - Relocatable
  • @@ -1924,7 +1924,7 @@ exports[`ExternalHostProductSpecificLinks renders with valid product code and no rel="noopener noreferrer" target="_blank" > - HOPB - Hop Brook + HOPB - Lower Hop Brook
  • @@ -2292,7 +2292,7 @@ exports[`ExternalHostProductSpecificLinks renders with valid product code and no rel="noopener noreferrer" target="_blank" > - GRSM - Great Smoky Mountains National Park + GRSM - Great Smoky Mountains National Park, Twin Creeks
  • @@ -2708,7 +2708,7 @@ exports[`ExternalHostProductSpecificLinks renders with valid product code and no rel="noopener noreferrer" target="_blank" > - YELL - Yellowstone National Park + YELL - Yellowstone Nothern Range (Frog Rock)
  • @@ -2807,7 +2807,7 @@ exports[`ExternalHostProductSpecificLinks renders with valid product code and si rel="noopener noreferrer" target="_blank" > - BARR - - Utqiaġvik Environmental Observatory + BARR - - Utqiaġvik
  • @@ -3001,7 +3001,7 @@ exports[`ExternalHostProductSpecificLinks renders with valid product code and si rel="noopener noreferrer" target="_blank" > - RMNP - - Rocky Mountain National Park + RMNP - - Rocky Mountain National Park, CASTNET
  • @@ -3015,7 +3015,7 @@ exports[`ExternalHostProductSpecificLinks renders with valid product code and si rel="noopener noreferrer" target="_blank" > - STER - - Sterling + STER - - North Sterling, CO
  • @@ -3125,7 +3125,7 @@ exports[`ExternalHostProductSpecificLinks renders with valid product code and si rel="noopener noreferrer" target="_blank" > - PUUM - - Pu-u Maka-ala Natural Area Reserve + PUUM - - Pu'u Maka'ala Natural Area Reserve @@ -3157,7 +3157,7 @@ exports[`ExternalHostProductSpecificLinks renders with valid product code and si rel="noopener noreferrer" target="_blank" > - KONA - - Konza Prairie Biological Station + KONA - - Konza Prairie Biological Station - Relocatable
  • @@ -3515,7 +3515,7 @@ exports[`ExternalHostProductSpecificLinks renders with valid product code and si rel="noopener noreferrer" target="_blank" > - GRSM - - Great Smoky Mountains National Park + GRSM - - Great Smoky Mountains National Park, Twin Creeks
  • @@ -3791,7 +3791,7 @@ exports[`ExternalHostProductSpecificLinks renders with valid product code and si rel="noopener noreferrer" target="_blank" > - YELL - - Yellowstone National Park + YELL - - Yellowstone Nothern Range (Frog Rock)
  • @@ -3870,7 +3870,7 @@ exports[`ExternalHostProductSpecificLinks renders with valid product code and si rel="noopener noreferrer" target="_blank" > - BLWA - Black Warrior River + BLWA - Black Warrior River near Dead Lake
  • @@ -3940,7 +3940,7 @@ exports[`ExternalHostProductSpecificLinks renders with valid product code and si rel="noopener noreferrer" target="_blank" > - TOMB - Tombigbee River + TOMB - Lower Tombigbee River at Choctaw Refuge
  • @@ -3972,7 +3972,7 @@ exports[`ExternalHostProductSpecificLinks renders with valid product code and si rel="noopener noreferrer" target="_blank" > - BARR - Utqiaġvik Environmental Observatory + BARR - Utqiaġvik
  • @@ -4000,7 +4000,7 @@ exports[`ExternalHostProductSpecificLinks renders with valid product code and si rel="noopener noreferrer" target="_blank" > - CARI - Caribou Creek at Caribou-Poker Creeks Research Watershed + CARI - Caribou Creek, Caribou-Poker Creeks Research Watershed
  • @@ -4162,7 +4162,7 @@ exports[`ExternalHostProductSpecificLinks renders with valid product code and si rel="noopener noreferrer" target="_blank" > - SJER - San Joaquin + SJER - San Joaquin Experimental Range
  • @@ -4292,7 +4292,7 @@ exports[`ExternalHostProductSpecificLinks renders with valid product code and si rel="noopener noreferrer" target="_blank" > - RMNP - Rocky Mountain National Park + RMNP - Rocky Mountain National Park, CASTNET
  • @@ -4306,7 +4306,7 @@ exports[`ExternalHostProductSpecificLinks renders with valid product code and si rel="noopener noreferrer" target="_blank" > - STER - Sterling + STER - North Sterling, CO
  • @@ -4352,7 +4352,7 @@ exports[`ExternalHostProductSpecificLinks renders with valid product code and si rel="noopener noreferrer" target="_blank" > - BARC - Barco Lake + BARC - Ordway-Swisher Biological Station - Barco Lake
  • @@ -4394,7 +4394,7 @@ exports[`ExternalHostProductSpecificLinks renders with valid product code and si rel="noopener noreferrer" target="_blank" > - SUGG - Suggs Lake + SUGG - Ordway-Swisher Biological Station - Suggs Lake
  • @@ -4472,7 +4472,7 @@ exports[`ExternalHostProductSpecificLinks renders with valid product code and si rel="noopener noreferrer" target="_blank" > - PUUM - Pu-u Maka-ala Natural Area Reserve + PUUM - Pu'u Maka'ala Natural Area Reserve @@ -4518,7 +4518,7 @@ exports[`ExternalHostProductSpecificLinks renders with valid product code and si rel="noopener noreferrer" target="_blank" > - KONA - Konza Prairie Biological Station + KONA - Konza Prairie Biological Station - Relocatable
  • @@ -4638,7 +4638,7 @@ exports[`ExternalHostProductSpecificLinks renders with valid product code and si rel="noopener noreferrer" target="_blank" > - HOPB - Hop Brook + HOPB - Lower Hop Brook
  • @@ -5006,7 +5006,7 @@ exports[`ExternalHostProductSpecificLinks renders with valid product code and si rel="noopener noreferrer" target="_blank" > - GRSM - Great Smoky Mountains National Park + GRSM - Great Smoky Mountains National Park, Twin Creeks
  • @@ -5422,7 +5422,7 @@ exports[`ExternalHostProductSpecificLinks renders with valid product code and si rel="noopener noreferrer" target="_blank" > - YELL - Yellowstone National Park + YELL - Yellowstone Nothern Range (Frog Rock)
  • diff --git a/src/lib_components/components/NeonApi/NeonApi.js b/src/lib_components/components/NeonApi/NeonApi.js index ba8eca76..042e135a 100644 --- a/src/lib_components/components/NeonApi/NeonApi.js +++ b/src/lib_components/components/NeonApi/NeonApi.js @@ -138,6 +138,24 @@ const NeonApi = { return getJsonObservable(path); }, + /** + * Gets the prototype data endpoint RxJS Observable. + * @return The RxJS Ajax Observable + */ + getPrototypeDatasetsObservable: () => ( + getJsonObservable(`${NeonEnvironment.getFullApiPath('prototype')}/datasets`) + ), + getPrototypeDatasetObservable: (uuid) => ( + getJsonObservable(`${NeonEnvironment.getFullApiPath('prototype')}/datasets/${uuid}`) + ), + getPrototypeManifestRollupObservable: (uuid) => ( + // eslint-disable-next-line max-len + getJsonObservable(`${NeonEnvironment.getFullApiPath('manifest')}/prototype/manifest/rollup?uuid=${uuid}`) + ), + getPrototypeDataFileObservable: (uuid, fileName) => ( + getJsonObservable(`${NeonEnvironment.getFullApiPath('prototype')}/data/${uuid}/${fileName}`) + ), + /** * Gets the release endpoint RxJS Observable. * @return The RxJS Ajax Observable diff --git a/src/lib_components/components/NeonContext/NeonContext.jsx b/src/lib_components/components/NeonContext/NeonContext.jsx index 25972775..57281032 100644 --- a/src/lib_components/components/NeonContext/NeonContext.jsx +++ b/src/lib_components/components/NeonContext/NeonContext.jsx @@ -209,8 +209,8 @@ const parseSitesFetchResponse = (sitesArray = []) => { const Provider = (props) => { const { children, + fetchPartials, useCoreAuth, - useCoreHeader, whenFinal, } = props; @@ -220,7 +220,7 @@ const Provider = (props) => { initialState.auth.useCore = true; initialState.fetches.auth.status = FETCH_STATUS.AWAITING_CALL; } - if (!useCoreHeader) { + if (fetchPartials) { initialState.fetches[DRUPAL_HEADER_HTML].status = FETCH_STATUS.AWAITING_CALL; initialState.fetches[DRUPAL_FOOTER_HTML].status = FETCH_STATUS.AWAITING_CALL; } @@ -350,16 +350,16 @@ const ProviderPropTypes = { PropTypes.node, PropTypes.string, ]), + fetchPartials: PropTypes.bool, useCoreAuth: PropTypes.bool, - useCoreHeader: PropTypes.bool, whenFinal: PropTypes.func, }; Provider.propTypes = ProviderPropTypes; Provider.defaultProps = { children: null, + fetchPartials: false, useCoreAuth: false, - useCoreHeader: false, whenFinal: () => {}, }; diff --git a/src/lib_components/components/NeonEnvironment/NeonEnvironment.js b/src/lib_components/components/NeonEnvironment/NeonEnvironment.js index 787190c0..20c20f87 100644 --- a/src/lib_components/components/NeonEnvironment/NeonEnvironment.js +++ b/src/lib_components/components/NeonEnvironment/NeonEnvironment.js @@ -35,6 +35,7 @@ export const optionalEnvironmentVars = [ 'REACT_APP_NEON_PATH_DOWNLOAD_API', 'REACT_APP_NEON_PATH_MANIFEST_API', 'REACT_APP_NEON_PATH_PRODUCTS_API', + 'REACT_APP_NEON_PATH_PROTOTYPE_DATA_API', 'REACT_APP_NEON_PATH_RELEASES_API', 'REACT_APP_NEON_PATH_SITES_API', 'REACT_APP_NEON_PATH_LOCATIONS_API', @@ -77,6 +78,7 @@ const NeonEnvironment = { getApiPath: { aopDownload: () => process.env.REACT_APP_NEON_PATH_AOP_DOWNLOAD_API, data: () => process.env.REACT_APP_NEON_PATH_DATA_API, + prototype: () => process.env.REACT_APP_NEON_PATH_PROTOTYPE_DATA_API, documents: () => process.env.REACT_APP_NEON_PATH_DOCUMENTS_API, download: () => process.env.REACT_APP_NEON_PATH_DOWNLOAD_API, manifest: () => process.env.REACT_APP_NEON_PATH_MANIFEST_API, @@ -122,9 +124,9 @@ const NeonEnvironment = { route: { home: () => process.env.REACT_APP_NEON_ROUTER_NEON_HOME || '/home', account: () => process.env.REACT_APP_NEON_ROUTER_NEON_MYACCOUNT || '/myaccount', - getFullRoute: (route) => `${NeonEnvironment.getRouterBasePath()}${route}`, - buildRouteFromHost: (route) => ( - `${NeonEnvironment.getHost()}${NeonEnvironment.getFullRoute(route)}` + getFullRoute: (route = '') => `${NeonEnvironment.getRouterBasePath()}${route}`, + buildRouteFromHost: (route = '') => ( + `${NeonEnvironment.getHost()}${NeonEnvironment.route.getFullRoute(route)}` ), buildHomeRoute: () => ( `${NeonEnvironment.getHost()}${NeonEnvironment.route.home()}` @@ -226,9 +228,12 @@ const NeonEnvironment = { getFullJsonLdApiPath: (path = '') => { const host = NeonEnvironment.getHost(); const root = NeonEnvironment.getRootJsonLdPath(); - const appliedPath = ['products'].includes(path) - ? NeonEnvironment.getApiPath[path]() - : NeonEnvironment.getApiLdPath[path](); + let appliedPath = ''; + if (['products'].includes(path)) { + appliedPath = NeonEnvironment.getApiPath[path](); + } else if (typeof NeonEnvironment.getApiLdPath[path] === 'function') { + appliedPath = NeonEnvironment.getApiLdPath[path](); + } return appliedPath ? `${host}${root}${appliedPath}` : `${host}${root}`; diff --git a/src/lib_components/components/NeonEnvironment/__tests__/NeonEnvironment.js b/src/lib_components/components/NeonEnvironment/__tests__/NeonEnvironment.js new file mode 100644 index 00000000..48944fb1 --- /dev/null +++ b/src/lib_components/components/NeonEnvironment/__tests__/NeonEnvironment.js @@ -0,0 +1,132 @@ +import NeonEnvironment, { + requiredEnvironmentVars, + optionalEnvironmentVars, +} from '../NeonEnvironment'; + +requiredEnvironmentVars.forEach((v) => { process.env[v] = v; }); +optionalEnvironmentVars.forEach((v) => { process.env[v] = v; }); + +describe('NeonEnvironment', () => { + describe('boolean properties', () => { + [ + 'isValid', + 'isDevEnv', + 'isProdEnv', + 'isForeignEnv', + 'useGraphql', + 'showAopViewer', + 'authDisableWs', + ].forEach((prop) => { + test(`${prop}`, () => { + expect(typeof NeonEnvironment[prop]).toBe('boolean'); + }); + }); + }); + + describe('simple string access methods', () => { + [ + 'getApiName', + 'getApiVersion', + 'getRootApiPath', + 'getRootGraphqlPath', + 'getRootJsonLdPath', + 'getRootAuthApiPath', + 'getVisusProductsBaseUrl', + 'getVisusIframeBaseUrl', + 'getRouterBasePath', + 'getRouterBaseHomePath', + 'getHostOverride', + 'getWsHostOverride', + ].forEach((method) => { + test(`${method}()`, () => { + expect(typeof NeonEnvironment[method]()).toBe('string'); + }); + }); + }); + + describe('nested string access methods', () => { + [ + 'getApiPath', + 'getApiLdPath', + 'getPagePath', + 'getAuthPath', + 'getAuthApiPath', + 'authTopics', + 'route', + ].forEach((methodGroup) => { + describe(`${methodGroup} string access methods`, () => { + Object.keys(NeonEnvironment[methodGroup]).forEach((method) => { + test(`${methodGroup}.${method}()`, () => { + expect(typeof NeonEnvironment[methodGroup][method]()).toBe('string'); + }); + }); + }); + }); + }); + + describe('get full path methods', () => { + test('getFullApiPath()', () => { + const host = NeonEnvironment.getHost(); + const fullPath1 = NeonEnvironment.getFullApiPath(); + expect(fullPath1.startsWith(host)).toBe(true); + const downloadPath = NeonEnvironment.getApiPath.download(); + const fullPath2 = NeonEnvironment.getFullApiPath('download'); + expect(fullPath2.startsWith(host)).toBe(true); + expect(fullPath2.endsWith(downloadPath)).toBe(true); + }); + test('getFullJsonLdApiPath()', () => { + const host = NeonEnvironment.getHost(); + const fullPath1 = NeonEnvironment.getFullJsonLdApiPath(); + expect(fullPath1.startsWith(host)).toBe(true); + const productsPath = NeonEnvironment.getApiPath.products(); + const fullPath2 = NeonEnvironment.getFullJsonLdApiPath('products'); + expect(fullPath2.startsWith(host)).toBe(true); + expect(fullPath2.endsWith(productsPath)).toBe(true); + const repoPath = NeonEnvironment.getApiLdPath.repo(); + const fullPath3 = NeonEnvironment.getFullJsonLdApiPath('repo'); + expect(fullPath3.startsWith(host)).toBe(true); + expect(fullPath3.endsWith(repoPath)).toBe(true); + }); + test('getFullPagePath()', () => { + const host = NeonEnvironment.getHost(); + const fullPath1 = NeonEnvironment.getFullPagePath(); + expect(fullPath1.startsWith(host)).toBe(true); + const fileNamingConventionsPath = NeonEnvironment.getPagePath.fileNamingConventions(); + const fullPath2 = NeonEnvironment.getFullPagePath('fileNamingConventions'); + expect(fullPath2.startsWith(host)).toBe(true); + expect(fullPath2.endsWith(fileNamingConventionsPath)).toBe(true); + }); + test('getFullAuthPath()', () => { + const host = NeonEnvironment.getHost(); + const fullPath1 = NeonEnvironment.getFullAuthPath(); + expect(fullPath1.startsWith(host)).toBe(true); + const loginPath = NeonEnvironment.getAuthPath.login(); + const fullPath2 = NeonEnvironment.getFullAuthPath('login'); + expect(fullPath2.startsWith(host)).toBe(true); + expect(fullPath2.endsWith(loginPath)).toBe(true); + }); + test('getFullAuthApiPath()', () => { + const wsPath = NeonEnvironment.getAuthApiPath.ws(); + const host1 = NeonEnvironment.getHost(); + const fullPath1 = NeonEnvironment.getFullAuthApiPath(); + expect(fullPath1.startsWith(host1)).toBe(true); + const fullPath2 = NeonEnvironment.getFullAuthApiPath('ws'); + expect(fullPath2.startsWith(host1)).toBe(true); + expect(fullPath2.endsWith(wsPath)).toBe(true); + const host2 = NeonEnvironment.getWebSocketHost(); + const fullPath3 = NeonEnvironment.getFullAuthApiPath('', true); + expect(fullPath3.startsWith(host2)).toBe(true); + const fullPath4 = NeonEnvironment.getFullAuthApiPath('ws', true); + expect(fullPath4.startsWith(host2)).toBe(true); + expect(fullPath4.endsWith(wsPath)).toBe(true); + }); + test('getFullGraphqlath()', () => { + const host = NeonEnvironment.getHost(); + expect(NeonEnvironment.getFullGraphqlPath().startsWith(host)).toBe(true); + }); + }); + + test('getAuthSilentType()', () => { + expect(NeonEnvironment.getAuthSilentType()).toBe('DISABLED'); + }); +}); diff --git a/src/lib_components/components/NeonHeader/NeonHeader.jsx b/src/lib_components/components/NeonHeader/NeonHeader.jsx index 430a6253..ac1fa7c3 100644 --- a/src/lib_components/components/NeonHeader/NeonHeader.jsx +++ b/src/lib_components/components/NeonHeader/NeonHeader.jsx @@ -31,7 +31,7 @@ const useStyles = makeStyles((theme) => ({ // common styles textAlign: 'right', position: 'absolute', - zIndex: 10, + zIndex: 15, // viewport-specific styles [theme.breakpoints.up('lg')]: { padding: '0px', @@ -60,10 +60,10 @@ const useStyles = makeStyles((theme) => ({ '& > header': { position: 'unset !important', '& label[for="nav-trigger"]': { - zIndex: '3 !important', + zIndex: '9 !important', }, '& div.header__site-navigation': { - zIndex: '2 !important', + zIndex: '8 !important', }, [theme.breakpoints.down('sm')]: { '& .header__site-navigation': { diff --git a/src/lib_components/components/NeonJsonLd/NeonJsonLd.js b/src/lib_components/components/NeonJsonLd/NeonJsonLd.js index e26f5832..f9f221c8 100644 --- a/src/lib_components/components/NeonJsonLd/NeonJsonLd.js +++ b/src/lib_components/components/NeonJsonLd/NeonJsonLd.js @@ -11,6 +11,8 @@ import NeonEnvironment from '../NeonEnvironment/NeonEnvironment'; * Container for supplying common NEON API JSON-LD request handlers. */ const NeonJsonLd = { + CITATION_AUTHOR: 'NEON (National Ecological Observatory Network)', + /** * Convenience wrapper for obtaining an AJAX Observable from NeonApi. * @param {string} url The URL to build from. @@ -52,8 +54,9 @@ const NeonJsonLd = { * into the DOM head. * Assumes the object is a valid JSON object. * @param {Object|null|undefined} data The JSON-LD object to inject. + * @param {boolean} injectReleaseMeta Optionally inject release meta tags */ - inject: (data) => { + inject: (data, injectReleaseMeta = false) => { if (!data) return; try { const existingBlock = document.head.querySelector('script[type="application/ld+json"]'); @@ -63,20 +66,103 @@ const NeonJsonLd = { } catch (e) { console.error(e); // eslint-disable-line no-console } + if (injectReleaseMeta) { + NeonJsonLd.injectReleaseMeta(data); + } const block = document.createElement('script'); block.setAttribute('type', 'application/ld+json'); block.innerText = JSON.stringify(data); document.head.appendChild(block); }, + /** + * Injects the release meta tags into the DOM head from the supplied + * JSON-LD object. + * Assumes the object is a valid JSON object. + * @param {Object|null|undefined} data The JSON-LD object to inject. + */ + injectReleaseMeta: (data) => { + if (!data) return; + try { + const currentDoiBlock = document.head.querySelector('meta[name="citation_doi"]'); + const doiUrl = new URL(data.identifier); + const doiBlock = document.createElement('meta'); + doiBlock.setAttribute('name', 'citation_doi'); + doiBlock.setAttribute('content', doiUrl.pathname.slice(1, doiUrl.pathname.length)); + if ((typeof currentDoiBlock !== 'undefined') && (currentDoiBlock !== null)) { + document.head.replaceChild(doiBlock, currentDoiBlock); + } else { + document.head.appendChild(doiBlock); + } + + const currentTitleBlock = document.head.querySelector('meta[name="citation_title"]'); + const titleBlock = document.createElement('meta'); + titleBlock.setAttribute('name', 'citation_title'); + let addTitle = false; + if ((typeof data.name === 'string') && (data.name.length > 0)) { + if (data.name.indexOf(NeonJsonLd.CITATION_AUTHOR) >= 0) { + const titleContent = data.name.replace(NeonJsonLd.CITATION_AUTHOR, '').trim(); + titleBlock.setAttribute('content', titleContent); + } else { + titleBlock.setAttribute('content', data.name); + } + addTitle = true; + } + if (!addTitle) { + if ((typeof currentTitleBlock !== 'undefined') && (currentTitleBlock !== null)) { + currentTitleBlock.remove(); + } + } else if ((typeof currentTitleBlock !== 'undefined') && (currentTitleBlock !== null)) { + document.head.replaceChild(titleBlock, currentTitleBlock); + } else { + document.head.appendChild(titleBlock); + } + + const currentAuthorBlock = document.head.querySelector('meta[name="citation_author"]'); + const authorBlock = document.createElement('meta'); + authorBlock.setAttribute('name', 'citation_author'); + authorBlock.setAttribute('content', NeonJsonLd.CITATION_AUTHOR); + if ((typeof currentAuthorBlock !== 'undefined') && (currentAuthorBlock !== null)) { + document.head.replaceChild(authorBlock, currentAuthorBlock); + } else { + document.head.appendChild(authorBlock); + } + } catch (e) { + console.error(e); // eslint-disable-line no-console + } + }, + + /** + * Removes the release meta tags from the DOM head element when exists. + */ + removeReleaseMeta: () => { + try { + const currentDoiBlock = document.head.querySelector('meta[name="citation_doi"]'); + if ((typeof currentDoiBlock !== 'undefined') && (currentDoiBlock !== null)) { + currentDoiBlock.remove(); + } + const currentTitleBlock = document.head.querySelector('meta[name="citation_title"]'); + if ((typeof currentTitleBlock !== 'undefined') && (currentTitleBlock !== null)) { + currentTitleBlock.remove(); + } + const currentAuthorBlock = document.head.querySelector('meta[name="citation_author"]'); + if ((typeof currentAuthorBlock !== 'undefined') && (currentAuthorBlock !== null)) { + currentAuthorBlock.remove(); + } + } catch (e) { + console.error(e); // eslint-disable-line no-console + } + }, + /** * Retrieves and injects the JDON-LD data from the specified URL. * @param {string} url The URL to query. + * @param {boolean} injectReleaseMeta Optionally inject release meta tags */ - getJsonLdWithInjection: (url) => { + getJsonLdWithInjection: (url, injectReleaseMeta = false) => { const observable = NeonApi.getJsonObservable(url) .pipe( - map((response) => NeonJsonLd.inject(response)), + map((response) => NeonJsonLd.inject(response, injectReleaseMeta)), catchError((err) => { console.error(err); // eslint-disable-line no-console return of('JSON-LD not found'); @@ -96,8 +182,24 @@ const NeonJsonLd = { * Fetches and injects the product JSON-LD based on the specified product code. * @param {string} productCode The product code to query with. * @param {string} release The release to build the URL from. + * @param {boolean} injectReleaseMeta Optionally inject release meta tags + * @param {boolean} onNotExistsOnly Inject only if JSON-LD is not already injected */ - injectProduct: (productCode, release) => { + injectProduct: (productCode, release, injectReleaseMeta = false, onNotExistsOnly = false) => { + let shouldFetch = true; + if (onNotExistsOnly) { + try { + const existingBlock = document.head.querySelector('script[type="application/ld+json"]'); + if ((typeof existingBlock !== 'undefined') && (existingBlock !== null)) { + shouldFetch = false; + } + } catch (e) { + console.error(e); // eslint-disable-line no-console + } + } + if (!shouldFetch) { + return null; + } const hasRelease = (typeof release !== 'undefined') && (release !== null) && (typeof release === 'string') @@ -105,8 +207,10 @@ const NeonJsonLd = { if (hasRelease) { return NeonJsonLd.getJsonLdWithInjection( `${NeonEnvironment.getFullJsonLdApiPath('products')}/${productCode}?release=${release}`, + injectReleaseMeta, ); } + NeonJsonLd.removeReleaseMeta(); return NeonJsonLd.getJsonLdWithInjection( `${NeonEnvironment.getFullJsonLdApiPath('products')}/${productCode}`, ); diff --git a/src/lib_components/components/NeonPage/NeonPage.jsx b/src/lib_components/components/NeonPage/NeonPage.jsx index 65cdc7f3..68b917d3 100644 --- a/src/lib_components/components/NeonPage/NeonPage.jsx +++ b/src/lib_components/components/NeonPage/NeonPage.jsx @@ -30,6 +30,7 @@ import Link from '@material-ui/core/Link'; import Paper from '@material-ui/core/Paper'; import Typography from '@material-ui/core/Typography'; +import ClearIcon from '@material-ui/icons/Clear'; import CollapseIcon from '@material-ui/icons/ExpandLess'; import ErrorIcon from '@material-ui/icons/Warning'; import ExpandIcon from '@material-ui/icons/ExpandMore'; @@ -151,7 +152,7 @@ const useStyles = makeStyles(() => ({ position: 'sticky', top: '-2px', boxShadow: '0px 1px 3px rgba(0, 0, 0, 0.25), 0px 1px 1px rgba(0, 0, 0, 0.25)', - zIndex: 1, + zIndex: 2, }, [Theme.breakpoints.down('xs')]: { padding: Theme.spacing(1.5), @@ -239,7 +240,7 @@ const useStyles = makeStyles(() => ({ justifyContent: 'center', textAlign: 'center', borderRadius: '4px', - padding: Theme.spacing(3, 3, 4, 3), + padding: Theme.spacing(3), position: 'sticky', top: Theme.spacing(12), left: 0, @@ -282,6 +283,11 @@ const useStyles = makeStyles(() => ({ marginTop: Theme.spacing(3), marginBottom: Theme.spacing(4), }, + dismissOverlay: { + width: '100%', + textAlign: 'right', + marginTop: Theme.spacing(2), + }, })); /** @@ -380,6 +386,7 @@ const NeonPage = (props) => { const sidebarRef = useRef(null); const sidebarLinksContainerRef = useRef(null); const belowMd = useMediaQuery(Theme.breakpoints.down('sm')); + const [overlayDismissed, setOverlayDismissed] = useState(false); /** Sidebar Setup @@ -636,9 +643,19 @@ const NeonPage = (props) => { )); const renderOverlay = (overlayChildren) => ( - + {overlayChildren} +
    + +
    ); @@ -860,7 +877,7 @@ const NeonPage = (props) => { }; const renderedPage = neonContextIsActive ? renderNeonPage() : ( - + {renderNeonPage()} ); diff --git a/src/lib_components/components/SiteMap/SiteMapContainer.jsx b/src/lib_components/components/SiteMap/SiteMapContainer.jsx index 019e7113..732f0e8d 100644 --- a/src/lib_components/components/SiteMap/SiteMapContainer.jsx +++ b/src/lib_components/components/SiteMap/SiteMapContainer.jsx @@ -57,6 +57,7 @@ import { VIEWS, FEATURES, FEATURE_TYPES, + FEATURE_DATA_SOURCES, MIN_CONTAINER_HEIGHT, OVERLAYS, OVERLAY_GROUPS, @@ -65,6 +66,9 @@ import { const boxShadow = '0px 2px 1px -1px rgba(0,0,0,0.2), 0px 1px 1px 0px rgba(0,0,0,0.14), 0px 1px 3px 0px rgba(0,0,0,0.12)'; const useStyles = makeStyles((theme) => ({ + ':root': { + fontSize: '24px', + }, outerContainer: { zIndex: 0, width: '100%', @@ -74,7 +78,7 @@ const useStyles = makeStyles((theme) => ({ width: '100%', height: '0px', // Necessary to set a fixed aspect ratio from props (using paddingBottom) position: 'relative', - backgroundColor: theme.colors.NEON_BLUE[200], + backgroundColor: Theme.colors.NEON_BLUE[200], overflow: 'hidden', display: 'flex', justifyContent: 'center', @@ -162,23 +166,23 @@ const useStyles = makeStyles((theme) => ({ height: '26px', padding: 'unset', borderRadius: '2px 0px 2px 0px', - border: `1px solid ${theme.colors.LIGHT_BLUE[500]}`, + border: `1px solid ${Theme.colors.LIGHT_BLUE[500]}`, cursor: 'grab', '&:hover, &:active': { - color: theme.colors.LIGHT_BLUE[400], - borderColor: theme.colors.LIGHT_BLUE[400], + color: Theme.colors.LIGHT_BLUE[400], + borderColor: Theme.colors.LIGHT_BLUE[400], backgroundColor: theme.palette.grey[50], }, '&:active': { cursor: 'row-resize !important', }, '& svg': { - fontSize: '1.15rem !important', + fontSize: '17px !important', }, }, resizeBorder: { position: 'absolute', - border: `3px solid ${theme.colors.LIGHT_BLUE[500]}`, + border: `3px solid ${Theme.colors.LIGHT_BLUE[500]}`, top: '0px', left: '0px', width: '100%', @@ -336,6 +340,7 @@ const SiteMapContainer = (props) => { hideUnselectable, showSummary, }, + manualLocationData, } = state; const contentDivProps = { @@ -537,6 +542,7 @@ const SiteMapContainer = (props) => { Render - Map/Table Toggle Button Group */ const renderMapTableToggleButtonGroup = () => { + if (state.view.current === VIEWS.SPLIT) { return null; } const viewTooltips = { [VIEWS.MAP]: 'Show the observatory map', [VIEWS.TABLE]: 'Show a table of all locations currently visible in the map', @@ -554,7 +560,7 @@ const SiteMapContainer = (props) => { : classes.mapTableToggleButtonGroup )} > - {Object.keys(VIEWS).map((key) => ( + {Object.keys(VIEWS).filter((key) => key !== VIEWS.SPLIT).map((key) => ( { placement="left" enterDelay={500} enterNextDelay={200} - title={`Resize ${view === VIEWS.MAP ? 'map' : 'table'} vertically`} + title={`Resize ${view === VIEWS.TABLE ? 'table' : 'map'} vertically`} > { description, descriptionFromParentDataFeatureKey, parentDataFeatureKey, + dataSource, } = feature; + // Special case: do not include any features with a dataSource of MANUAL_LOCATIONS if + // manualLocationData is not also defined + if (dataSource === FEATURE_DATA_SOURCES.MANUAL_LOCATIONS && !manualLocationData) { + return null; + } const handleChange = (event) => { dispatch({ type: 'setLegendFeatureOptionVisibility', @@ -1249,10 +1261,10 @@ const SiteMapContainer = (props) => { viewLegendButtonsContainerClassName = `${classes.viewLegendButtonsContainer} ${classes.viewLegendButtonsContainerFullscreen}`; /* eslint-enable max-len */ } - return ( + const ret = (
    - {view === VIEWS.MAP ? : null } + {view === VIEWS.MAP || view === VIEWS.SPLIT ? : null } {view === VIEWS.TABLE ? : null } {renderVerticalResizeButton()}
    { {renderSelectionSummary()}
    {fullscreen ? null :
    } + {view === VIEWS.SPLIT ? : null }
    ); + return ret; }; SiteMapContainer.propTypes = { diff --git a/src/lib_components/components/SiteMap/SiteMapContext.jsx b/src/lib_components/components/SiteMap/SiteMapContext.jsx index 39e015ad..ce5c645a 100644 --- a/src/lib_components/components/SiteMap/SiteMapContext.jsx +++ b/src/lib_components/components/SiteMap/SiteMapContext.jsx @@ -40,6 +40,7 @@ import { SITE_MAP_PROP_TYPES, SITE_MAP_DEFAULT_PROPS, MIN_TABLE_MAX_BODY_HEIGHT, + MANUAL_LOCATION_TYPES, GRAPHQL_LOCATIONS_API_CONSTANTS, getZoomedIcons, mapIsAtFocusLocation, @@ -156,7 +157,20 @@ const centerIsValid = (center) => ( // is a site feature like a plot far from the site center so the site itself may not be seen as // "in bounds"). const calculateFeatureDataFetches = (state, requiredSites = []) => { - const sitesInMap = calculateLocationsInBounds(state.sites, state.map.bounds, true, 0.06); + // Determine which sites are in the map from the set of all current sites unless + // manualLocationData is non-empty and contains prototpye sites + const fetchablePrototypeSites = (state.manualLocationData || []).filter((manualLocation) => ( + manualLocation.manualLocationType === MANUAL_LOCATION_TYPES.PROTOTYPE_SITE + && state.sites[manualLocation.siteCode] + )); + let sitesToConsider = state.sites; + if (state.manualLocationData && fetchablePrototypeSites.length) { + sitesToConsider = {}; + fetchablePrototypeSites.forEach((manualLocation) => { + sitesToConsider[manualLocation.siteCode] = state.sites[manualLocation.siteCode]; + }); + } + const sitesInMap = calculateLocationsInBounds(sitesToConsider, state.map.bounds, true, 0.06); let requiredSitesArray = []; if (requiredSites && requiredSites.length) { requiredSitesArray = ( @@ -728,7 +742,8 @@ const reducer = (state, action) => { newState.focusLocation.current = action.location; newState.focusLocation.data = null; newState.overallFetch.expected += 1; - if (newState.view.current !== VIEWS.MAP) { newState.view.current = VIEWS.MAP; } + // Switch view to MAP if we're on TABLE (and not on SPLIT) + if (newState.view.current === VIEWS.TABLE) { newState.view.current = VIEWS.MAP; } return newState; case 'setFocusLocationFetchStarted': @@ -974,6 +989,7 @@ const Provider = (props) => { selectionLimit, onSelectionChange, tableFullHeight, + manualLocationData, children, } = props; @@ -1034,6 +1050,9 @@ const Provider = (props) => { break; } } + if (Array.isArray(manualLocationData) && manualLocationData.length > 0) { + initialState.manualLocationData = manualLocationData; + } if (neonContextIsFinal && !neonContextHasError) { initialState = hydrateNeonContextData(initialState, neonContextData); } @@ -1045,7 +1064,7 @@ const Provider = (props) => { ); /** - Effect - trigger focusLocation fetch or short circuit if found in NeonContext data + Effect - trigger focusLocation fetch or short circuit if found in NeonContext or manual data */ useEffect(() => { const noop = () => {}; @@ -1090,6 +1109,20 @@ const Provider = (props) => { }, 0); return () => window.clearTimeout(timeout); } + // If the location is found in manualLocationData then pull from there + if (state.manualLocationData) { + const manualSite = state.manualLocationData.find((ml) => ml.siteCode === current); + if (manualSite) { + const { latitude, longitude } = manualSite; + const timeout = window.setTimeout(() => { + dispatch({ + type: 'setFocusLocationFetchSucceeded', + data: { type: 'SITE', latitude, longitude }, + }); + }, 0); + return () => window.clearTimeout(timeout); + } + } // Trigger focus location fetch dispatch({ type: 'setFocusLocationFetchStarted' }); fetchManyLocationsGraphQL([current]) @@ -1105,6 +1138,7 @@ const Provider = (props) => { state.focusLocation, state.focusLocation.fetch.status, state.neonContextHydrated, + state.manualLocationData, state.featureData, ]); diff --git a/src/lib_components/components/SiteMap/SiteMapFeature.jsx b/src/lib_components/components/SiteMap/SiteMapFeature.jsx index b6b1e1ca..284c48a3 100644 --- a/src/lib_components/components/SiteMap/SiteMapFeature.jsx +++ b/src/lib_components/components/SiteMap/SiteMapFeature.jsx @@ -47,6 +47,7 @@ import { NLCD_CLASSES, KM2_TO_ACRES, MAP_MOUSE_MODES, + MANUAL_LOCATION_TYPES, HIGHLIGHT_STATUS, SELECTION_STATUS, SELECTION_PORTIONS, @@ -239,6 +240,7 @@ const SiteMapFeature = (props) => { */ const { neonContextHydrated, + manualLocationData, map: { bounds: mapBounds }, focusLocation: { current: focusLocation }, featureData: { @@ -1123,6 +1125,84 @@ const SiteMapFeature = (props) => { ); }; + /** + Render: Decommissioned Site Popup + */ + const renderDecommissionedSitePopup = (siteCode) => { + const site = featureData[siteCode] || {}; + const { [site.state]: usState = {} } = state + .featureData[FEATURE_TYPES.STATES.KEY][FEATURES.STATES.KEY]; + const { [site.domain]: domain = {} } = state + .featureData[FEATURE_TYPES.DOMAINS.KEY][FEATURES.DOMAINS.KEY]; + const stateFieldTitle = (site.stateCode === 'PR' ? 'Territory' : 'State'); + return ( + + {renderPopupTitle(`${site.siteName} (${site.siteCode})`, false)} + jumpTo(site.siteCode)} + style={{ marginLeft: '-2px', marginBottom: '8px' }} + data-selenium="sitemap-map-popup-siteLink" + > + {markerIcon} + {`Jump to ${site.siteCode} on the map`} + + + {/* Terrain and Type */} + + {feature.nameSingular} + {featureDescription} + + {/* State/Territory */} + + {stateFieldTitle} + {selectionActive ? ( + {usState.name} + ) : ( + + jumpTo(site.state)} + data-selenium="sitemap-map-popup-stateLink" + > + {markerIcon} + {usState.name} + + + )} + + {/* Latitude/Longitude */} + + {renderLatLon(site.latitude, site.longitude)} + + {/* Domain */} + + Domain + {selectionActive ? ( + {`${site.domain} - ${domain.name}`} + ) : ( + + jumpTo(site.domain)} + data-selenium="sitemap-map-popup-domainLink" + > + {markerIcon} + {`${site.domain} - ${domain.name}`} + + + )} + + + + ); + }; + /** Render - All the Rest of the Popups Convention is alphabetical listing of keys since order here doesn't matter @@ -1148,6 +1228,7 @@ const SiteMapFeature = (props) => { AQUATIC_SENSOR_STATIONS: renderLocationPopup, AQUATIC_STAFF_GAUGES: renderLocationPopup, AQUATIC_WET_DEPOSITION_POINTS: renderLocationPopup, + DECOMMISSIONED_SITES: renderDecommissionedSitePopup, DISTRIBUTED_BASE_PLOTS: (siteCode, location) => renderLocationPopup(siteCode, location, [ renderPlotSizeAndSlope, renderPlotSamplingModules, @@ -1528,15 +1609,30 @@ const SiteMapFeature = (props) => { /** Main Render */ + let renderableKeys = Object.keys(featureData); + if (Array.isArray(manualLocationData) && manualLocationData.length) { + const hasPrototypeSites = manualLocationData.some((ml) => ( + ml.manualLocationType === MANUAL_LOCATION_TYPES.PROTOTYPE_SITE + )); + if (hasPrototypeSites && featureType === FEATURE_TYPES.SITES.KEY) { + const allKeys = Object.keys(featureData); + renderableKeys = manualLocationData + .filter((ml) => ( + ml.manualLocationType === MANUAL_LOCATION_TYPES.PROTOTYPE_SITE + && allKeys.includes(ml.siteCode) + )) + .map((ml) => ml.siteCode); + } + } return ( - {Object.keys(featureData) + {renderableKeys // Valid items should render above unselecatbles .sort((a) => { if (!validItems) { return 0; } return (validItems.has(a) ? 1 : -1); }) - // Focus lcoation should render above all others + // Focus location should render above all others .sort((a) => (a === state.focusLocation.current ? 1 : -1)) .flatMap((keyA) => { if ( diff --git a/src/lib_components/components/SiteMap/SiteMapLeaflet.jsx b/src/lib_components/components/SiteMap/SiteMapLeaflet.jsx index ee1289a1..15bf8574 100644 --- a/src/lib_components/components/SiteMap/SiteMapLeaflet.jsx +++ b/src/lib_components/components/SiteMap/SiteMapLeaflet.jsx @@ -198,10 +198,10 @@ const useStyles = makeStyles((theme) => ({ height: '26px', padding: 'unset', borderRadius: '2px 0px 2px 0px', - border: `1px solid ${theme.colors.LIGHT_BLUE[500]}`, + border: `1px solid ${Theme.colors.LIGHT_BLUE[500]}`, '&:hover, &:active': { - color: theme.colors.LIGHT_BLUE[400], - borderColor: theme.colors.LIGHT_BLUE[400], + color: Theme.colors.LIGHT_BLUE[400], + borderColor: Theme.colors.LIGHT_BLUE[400], backgroundColor: theme.palette.grey[50], }, '& svg': { @@ -234,8 +234,8 @@ const useStyles = makeStyles((theme) => ({ areaSelection: { position: 'absolute', pointerEvents: 'none', - border: `3px dotted ${theme.colors.LIGHT_BLUE[500]}`, - backgroundColor: theme.colors.LIGHT_BLUE[100], + border: `3px dotted ${Theme.colors.LIGHT_BLUE[500]}`, + backgroundColor: Theme.colors.LIGHT_BLUE[100], opacity: 0.6, zIndex: 999, }, diff --git a/src/lib_components/components/SiteMap/SiteMapTable.jsx b/src/lib_components/components/SiteMap/SiteMapTable.jsx index 0d631ee5..e09f484a 100644 --- a/src/lib_components/components/SiteMap/SiteMapTable.jsx +++ b/src/lib_components/components/SiteMap/SiteMapTable.jsx @@ -1,16 +1,15 @@ /* eslint-disable jsx-a11y/anchor-is-valid, no-unused-vars */ -import React, { useRef, useEffect } from 'react'; +import React, { useRef, useEffect, useLayoutEffect } from 'react'; import { makeStyles } from '@material-ui/core/styles'; import Box from '@material-ui/core/Box'; +import Button from '@material-ui/core/Button'; import Link from '@material-ui/core/Link'; import IconButton from '@material-ui/core/IconButton'; import Tooltip from '@material-ui/core/Tooltip'; import Typography from '@material-ui/core/Typography'; import InfoIcon from '@material-ui/icons/InfoOutlined'; -import MarkerIcon from '@material-ui/icons/LocationOn'; -import ExploreDataProductsIcon from '@material-ui/icons/InsertChartOutlined'; import MaterialTable, { MTableToolbar, MTableFilterRow } from 'material-table'; @@ -25,24 +24,67 @@ import { FEATURES, NLCD_CLASSES, FEATURE_TYPES, + MANUAL_LOCATION_TYPES, MIN_TABLE_MAX_BODY_HEIGHT, PLOT_SAMPLING_MODULES, UNSELECTABLE_MARKER_FILTER, calculateLocationsInBounds, } from './SiteMapUtils'; -const ucWord = (word) => `${word.slice(0, 1).toUpperCase()}${word.slice(1).toLowerCase()}`; +const ucWord = (word = '') => `${word.slice(0, 1).toUpperCase()}${word.slice(1).toLowerCase()}`; + +/** + * Parse an input search string into discrete terms. + * Supports quoting words together as a single term. + * Example: '"foo bar" baz' => ['foo bar', 'baz'] + * @param {string} input - string to parse into discrete terms + */ +const parseSearchTerms = (input) => { + const terms = input + .replace(/[^\w\s."]/g, '') + .match(/(".*?"|[^" \s]+)(?=\s* |\s*$)/g); + return (terms || []) + .map((term) => term.replace(/"/g, '').toLowerCase()); +}; + +/** + * Apply a searchString to a list of string attributes; return boolean for a match + */ +const searchOnAttribs = (searchString, searchableAttribs = []) => { + const searchTerms = parseSearchTerms(searchString); + if (!searchTerms.length) { return true; } + return searchTerms.some((term) => ( + searchableAttribs.some((attrib) => ( + (attrib || '').toLowerCase().includes(term) + )) + )); +}; + +const getFeatureName = (featureKey) => { + if (FEATURES[featureKey]) { + return (FEATURES[featureKey].nameSingular || FEATURES[featureKey].name || featureKey); + } + return featureKey; +}; + +const calculateMaxBodyHeight = (tableRef) => { + if (!tableRef || !tableRef.current) { return MIN_TABLE_MAX_BODY_HEIGHT; } + const containerHeight = tableRef.current.clientHeight || 0; + const toolbarHeight = tableRef.current.children[0].children[0].clientHeight || 0; + const pagerHeight = tableRef.current.children[0].children[2].clientHeight || 0; + return Math.max(containerHeight - toolbarHeight - pagerHeight, MIN_TABLE_MAX_BODY_HEIGHT); +}; const useStyles = makeStyles((theme) => ({ tableContainer: { - position: 'absolute', - width: '100%', - height: '100%', backgroundColor: 'white', overflowWrap: 'normal', '& table': { margin: '0px !important', borderCollapse: 'separate', + '& tr.MuiTableRow-root:empty': { + height: '0px !important', + }, '& tr.MuiTableRow-head': { backgroundColor: theme.palette.primary.main, '& th:first-child span.MuiCheckbox-root': { @@ -64,6 +106,14 @@ const useStyles = makeStyles((theme) => ({ borderBottom: 'none', }, }, + tableContainerIntegrated: { + position: 'absolute', + width: '100%', + height: '100%', + }, + tableContainerStandalone: { + // ... + }, featureIcon: { width: theme.spacing(3), height: theme.spacing(3), @@ -80,7 +130,6 @@ const useStyles = makeStyles((theme) => ({ }, toolbarContainer: { backgroundColor: theme.palette.grey[50], - borderBottom: `1px dotted ${theme.palette.grey[300]}`, [theme.breakpoints.down('xs')]: { paddingTop: theme.spacing(4.5), }, @@ -88,11 +137,6 @@ const useStyles = makeStyles((theme) => ({ padding: theme.spacing(0, 2), backgroundColor: theme.palette.grey[50], }, - // This hides all but the search input and show columns buttons. - // No other way to have material table NOT show a selection title in the toolbar. - '& div.MuiToolbar-root > div:not(:nth-last-child(-n+2))': { - display: 'none', - }, // Make the columns button more prominent (really hard to do with component overriding) '& button': { color: theme.palette.primary.main, @@ -104,6 +148,13 @@ const useStyles = makeStyles((theme) => ({ }, }, }, + toolbarContainerNoSplit: { + // This hides all but the search input and show columns buttons. + // No other way to have material table NOT show a selection title in the toolbar. + '& div.MuiToolbar-root > div:not(:nth-last-child(-n+2))': { + display: 'none', + }, + }, toggleButtonGroup: { height: theme.spacing(4), }, @@ -132,8 +183,16 @@ const useStyles = makeStyles((theme) => ({ display: 'flex', alignItems: 'center', justifyContent: 'flex-start', - margin: Theme.spacing(1, 0, 0.5, 0), + margin: theme.spacing(1, 0, 0.5, 0), minWidth: '200px', + textAlign: 'left', + }, + siteLinksDivider: { + margin: theme.spacing(0, 1, 0, 1), + }, + siteDetailsLink: { + fontSize: '80%', + fontStyle: 'italic', }, nlcdClassContainer: { display: 'flex', @@ -145,16 +204,14 @@ const useStyles = makeStyles((theme) => ({ border: '1px solid black', marginRight: theme.spacing(1.5), }, + tableTitle: { + '& h6': { + fontSize: '1.2rem', + fontWeight: 500, + }, + }, })); -const calculateMaxBodyHeight = (tableRef) => { - if (!tableRef || !tableRef.current) { return MIN_TABLE_MAX_BODY_HEIGHT; } - const containerHeight = tableRef.current.clientHeight || 0; - const toolbarHeight = tableRef.current.children[0].children[0].clientHeight || 0; - const pagerHeight = tableRef.current.children[0].children[2].clientHeight || 0; - return Math.max(containerHeight - toolbarHeight - pagerHeight, MIN_TABLE_MAX_BODY_HEIGHT); -}; - const SiteMapTable = () => { const classes = useStyles(Theme); const tableRef = useRef(null); @@ -165,6 +222,10 @@ const SiteMapTable = () => { // Site Map State const [state, dispatch] = SiteMapContext.useSiteMapContext(); + const { + manualLocationData, + view: { current: view, initialized: viewsInitialized }, + } = state; const { focus, fullHeight, @@ -186,29 +247,95 @@ const SiteMapTable = () => { useEffect(() => { if ( !tableRef || !tableRef.current - || state.view.current !== VIEWS.TABLE || state.view.initialized[VIEWS.TABLE] + || view !== VIEWS.TABLE || viewsInitialized[VIEWS.TABLE] ) { return; } dispatch({ type: 'setViewInitialized' }); dispatch({ type: 'setTableMaxBodyHeight', height: calculateMaxBodyHeight(tableRef) }); - }, [tableRef, state.view, dispatch]); + }, [ + tableRef, + view, + viewsInitialized, + dispatch, + ]); /** Effect - Recalculate the max body height from an aspect ratio change (e.g. page resize) */ useEffect(() => { if ( - state.view.current === VIEWS.TABLE && state.view.initialized[VIEWS.TABLE] + view === VIEWS.TABLE && viewsInitialized[VIEWS.TABLE] && maxBodyHeightUpdateFromAspectRatio ) { dispatch({ type: 'setTableMaxBodyHeight', height: calculateMaxBodyHeight(tableRef) }); } }, [ tableRef, - state.view, + view, + viewsInitialized, dispatch, maxBodyHeightUpdateFromAspectRatio, ]); + /** + Layout Effect - Inject a second horizontal scrollbar above the table linked to the main + */ + useLayoutEffect(() => { + const noop = () => {}; + // This all only applies to full height table and/or split view (which behaves as full height) + if (!fullHeight && view !== VIEWS.SPLIT) { return noop; } + // Collect the nodes we pay attention to. Each one has a distinct purpose. + const tableNode = tableRef.current.querySelector('table'); + if (!tableNode) { return noop; } + const tbodyNode = tableRef.current.querySelector('tbody'); + if (!tbodyNode) { return noop; } + const scrollingNode = (tableNode.parentElement || {}).parentElement; + if (!scrollingNode) { return noop; } + const containerNode = scrollingNode.parentElement; + if (!containerNode) { return noop; } + // Initialize the new scrollbar in this scope + let scrollbar = null; + // Function to do the initial injection fo the scrollbar node + const injectScrollbar = () => { + scrollbar = document.createElement('div'); + scrollbar.appendChild(document.createElement('div')); + scrollbar.style.overflow = 'auto'; + scrollbar.style.overflowY = 'hidden'; + // eslint-disable-next-line prefer-destructuring + scrollbar.style.backgroundColor = Theme.palette.grey[50]; + scrollbar.firstChild.style.width = `${tableNode.scrollWidth || 0}px`; + scrollbar.firstChild.style.paddingTop = '1px'; + scrollbar.onscroll = () => { + scrollingNode.scrollLeft = scrollbar.scrollLeft; + }; + scrollingNode.onscroll = () => { + scrollbar.scrollLeft = scrollingNode.scrollLeft; + }; + containerNode.parentNode.insertBefore(scrollbar, containerNode); + }; + // Function to resize the scrollbar. We can't rely on the scrollWidth being accurate when we + // inject as not-yet-fully-rendered table rows may expand the scrollWidth. + const resizeScrollbar = () => { + if (!scrollbar) { return; } + scrollbar.firstChild.style.width = `${tableNode.scrollWidth || 0}px`; + scrollbar.scrollLeft = scrollingNode.scrollLeft; + }; + // Inject the scrollbar one step removed from the initial render cycle (with a 0-sec timeout) + const timeout = window.setTimeout(injectScrollbar, 0); + // Observe the childList of the tbody - i.e. rows being added or removed - to trigger a resize + // of the injected scrollbar + const observer = new MutationObserver(resizeScrollbar); + observer.observe(tbodyNode, { childList: true }); + // Clear any pending timeouts and disconnect our observer on unload to avoid memory leaks + return () => { + observer.disconnect(); + window.clearTimeout(timeout); + }; + }, [ + tableRef, + fullHeight, + view, + ]); + if (!canRender) { return null; } // Selection functions @@ -235,7 +362,15 @@ const SiteMapTable = () => { const getParent = (type, location) => { let source = null; if (type === 'SITE') { - source = { code: 'siteCode', data: state.sites }; + let data = state.sites; + if (Array.isArray(manualLocationData) && manualLocationData.length) { + data = {}; + manualLocationData.forEach((ml) => { + const { siteCode } = ml; + data[siteCode] = Object.keys(state.sites).includes(siteCode) ? state.sites[siteCode] : ml; + }); + } + source = { code: 'siteCode', data }; } else if (type === 'STATE') { source = { code: 'stateCode', data: state.featureData.STATES.STATES }; } else if (type === 'DOMAIN') { @@ -254,12 +389,6 @@ const SiteMapTable = () => { const getState = (location) => getParent('STATE', location); const getDomain = (location) => getParent('DOMAIN', location); - const getFeatureName = (featureKey) => { - if (FEATURES[featureKey]) { - return (FEATURES[featureKey].nameSingular || FEATURES[featureKey].name || featureKey); - } - return featureKey; - }; const renderFeatureIcon = (featureKey, unselectable = false) => { if (!FEATURES[featureKey] || !FEATURES[featureKey].iconSvg) { return null; } const { iconSvg } = FEATURES[featureKey]; @@ -304,6 +433,13 @@ const SiteMapTable = () => { if (selectionActive && selectableItems && hideUnselectable) { initialRows = initialRows.filter((item) => selectableItems.has(item)); } + const hasPrototypeSites = (manualLocationData || []).some((ml) => ( + ml.manualLocationType === MANUAL_LOCATION_TYPES.PROTOTYPE_SITE + )); + if (hasPrototypeSites) { + const visibleSites = manualLocationData.map((ml) => ml.siteCode); + initialRows = initialRows.filter((item) => visibleSites.includes(item)); + } const rows = initialRows.map((key) => locations[key]); if (selectionActive) { rows.forEach((row, idx) => { @@ -338,62 +474,60 @@ const SiteMapTable = () => { title: 'Site', sorting: true, defaultSort: 'desc', + searchable: true, lookup: Object.fromEntries( Array.from(sitesInMap).map((siteCode) => [siteCode, siteCode]), ), + customFilterAndSearch: (input, row) => { + if (typeof input === 'string') { + return searchOnAttribs(input, [row.siteCode, row.description]); + } + if (Array.isArray(input)) { + return input.includes(row.siteCode); + } + return false; + }, customSort: (rowA, rowB) => { const siteA = getSite(rowA); const siteB = getSite(rowB); - return siteA.description > siteB.description ? -1 : 1; + const aName = siteA.description; + const bName = siteB.description; + return aName > bName ? -1 : 1; }, + // eslint-disable-next-line arrow-body-style render: (row) => { const site = getSite(row); if (!site) { return null; } - const featureKey = `${site.terrain.toUpperCase()}_${site.type.toUpperCase()}_SITES`; + const isDecommissioned = site.manualLocationType === MANUAL_LOCATION_TYPES.PROTOTYPE_SITE; + const featureKey = isDecommissioned + ? FEATURES.DECOMMISSIONED_SITES.KEY + : `${site.terrain.toUpperCase()}_${site.type.toUpperCase()}_SITES`; const unselectable = selectionActive && !rowIsSelectable(row); return (
    -
    + jumpTo(row.siteCode)} + title={`Click to view ${row.siteCode} on the map`} + > {renderFeatureIcon(featureKey, unselectable)} - {`${site.description} (${site.siteCode})`} -
    -
    - - jumpTo(site.siteCode)} - data-selenium="sitemap-table-site-button-jumpTo" - aria-label="Jump to site on map" - > - - - - - - - - - - - - - + {`${site.description || 'Unnamed Site'} (${site.siteCode})`} + +
    + + Site Details + + | + + Explore Data +
    ); @@ -404,6 +538,7 @@ const SiteMapTable = () => { title: 'Domain', sorting: true, defaultSort: 'desc', + searchable: true, lookup: Object.fromEntries( Array.from(domainsInMap).map((domainCode) => [domainCode, domainCode]), ), @@ -416,49 +551,14 @@ const SiteMapTable = () => { const domain = getDomain(row); return !domain ? null : (
    -
    + jumpTo(domain.domainCode)} + title={`Click to view ${domain.domainCode} on the map`} + > {domain.domainCode} -
    -
    - - jumpTo(domain.domainCode)} - data-selenium="sitemap-table-domain-button-jumpTo" - aria-label="Jump to domain on map" - > - - - - {/* - - - - - - */} - - - - - -
    +
    ); }, @@ -468,6 +568,7 @@ const SiteMapTable = () => { title: 'State', sorting: true, defaultSort: 'desc', + searchable: true, lookup: Object.fromEntries( Array.from(statesInMap).map((stateCode) => [ stateCode, @@ -479,43 +580,46 @@ const SiteMapTable = () => { const stateB = getState(rowB); return stateA.name > stateB.name ? -1 : 1; }, + customFilterAndSearch: (input, row) => { + if (typeof input === 'string') { + const rowState = getState(row); + return searchOnAttribs(input, [row.stateCode, rowState.name]); + } + if (Array.isArray(input)) { + return input.includes(row.stateCode); + } + return false; + }, render: (row) => { const usstate = getState(row); return !usstate ? null : (
    -
    + jumpTo(row.stateCode)} + title={`Click to view ${row.stateCode} on the map`} + > {usstate.name} -
    -
    - - jumpTo(usstate.stateCode)} - data-selenium="sitemap-table-state-button-jumpTo" - aria-label="Jump to state on map" - > - - - - - - - - -
    +
    ); }, }, + coordinates: { + field: 'latitude', + title: 'Coordinates', + sorting: false, + filtering: false, + searchable: false, + render: (row) => ( + <> + {renderCaptionString(row.latitude.toFixed(5), 'Latitude')} +
    + {renderCaptionString(row.longitude.toFixed(5), 'Longitude')} + + ), + }, latitude: { field: 'latitude', title: 'Latitude', @@ -539,13 +643,12 @@ const SiteMapTable = () => { if (focus === FEATURE_TYPES.SITES.KEY) { columns = [ commonColumns.site, - commonColumns.latitude, - commonColumns.longitude, { // Site Type field: 'type', title: 'Type', sorting: true, defaultSort: 'desc', + searchable: true, lookup: Object.fromEntries( Array.from( new Set(rows.map((row) => row.type)), @@ -567,6 +670,11 @@ const SiteMapTable = () => { }, commonColumns.domain, commonColumns.state, + commonColumns.coordinates, + /* + commonColumns.latitude, + commonColumns.longitude, + */ ]; } if (focus === FEATURE_TYPES.LOCATIONS.KEY) { @@ -576,11 +684,13 @@ const SiteMapTable = () => { title: 'Name', sorting: true, defaultSort: 'desc', + searchable: true, render: (row) => ( jumpTo(row.name)} + title={`View ${row.name} on map`} > {row.name} @@ -591,6 +701,7 @@ const SiteMapTable = () => { title: 'Type', sorting: true, defaultSort: 'desc', + searchable: true, lookup: Object.fromEntries( Array.from(new Set(rows.map((row) => row.featureKey))) .sort((a, b) => (getFeatureName(a) > getFeatureName(b) ? 1 : -1)) @@ -613,8 +724,6 @@ const SiteMapTable = () => { ); }, }, - commonColumns.latitude, - commonColumns.longitude, { // Elevation field: 'elevation', title: 'Elevation', @@ -631,6 +740,7 @@ const SiteMapTable = () => { title: 'NLCD Class', sorting: true, deafultSort: 'asc', + searchable: true, lookup: Object.fromEntries( Object.keys(NLCD_CLASSES) .filter((classKey) => rows.some((row) => row.nlcdClass === classKey)) @@ -698,6 +808,7 @@ const SiteMapTable = () => { filtering: false, sorting: true, deafultSort: 'asc', + searchable: true, customSort: (rowA, rowB) => { const a = Array.isArray(rowA.samplingModules) ? rowA.samplingModules.length : null; const b = Array.isArray(rowB.samplingModules) ? rowB.samplingModules.length : null; @@ -740,14 +851,22 @@ const SiteMapTable = () => { commonColumns.site, commonColumns.domain, commonColumns.state, + commonColumns.coordinates, + /* + commonColumns.latitude, + commonColumns.longitude, + */ ]; } + const toolbarClassName = view === VIEWS.SPLIT + ? classes.toolbarContainer + : `${classes.toolbarContainer} ${classes.toolbarContainerNoSplit}`; const components = { Container: Box, Toolbar: (toolbarProps) => ( -
    - +
    +
    ), FilterRow: (filterRowProps) => ( @@ -778,7 +897,8 @@ const SiteMapTable = () => { top: 0, backgroundColor: Theme.palette.grey[50], }, - pageSizeOptions: [5, 10, 20, 50, 100], + pageSize: 100, + pageSizeOptions: [100, 200, 500], rowStyle: (row) => { if (selectionActive) { if (!rowIsSelectable(row)) { @@ -796,25 +916,31 @@ const SiteMapTable = () => { disabled: !rowIsSelectable(row), }), }; - if (fullHeight) { - tableOptions.pageSize = 50; - } else { + if (!fullHeight && view !== VIEWS.SPLIT) { tableOptions.maxBodyHeight = `${maxBodyHeight || MIN_TABLE_MAX_BODY_HEIGHT}px`; } + let containerClassName = `${classes.tableContainer} ${classes.tableContainerIntegrated}`; + let containerStyle = {}; + if (view === VIEWS.TABLE && fullHeight) { + containerStyle = { position: 'relative' }; + } + if (view === VIEWS.SPLIT) { + containerClassName = `${classes.tableContainer} ${classes.tableContainerStandalone}`; + } return (
    { const action = { type: 'updateSelectionSet', selection: new Set() }; @@ -831,3 +957,14 @@ const SiteMapTable = () => { }; export default SiteMapTable; + +// Additional items exported for unit testing +export const getTestableItems = () => ( + process.env.NODE_ENV !== 'test' ? {} : { + ucWord, + parseSearchTerms, + searchOnAttribs, + calculateMaxBodyHeight, + getFeatureName, + } +); diff --git a/src/lib_components/components/SiteMap/SiteMapUtils.js b/src/lib_components/components/SiteMap/SiteMapUtils.js index 72632c94..e212450a 100644 --- a/src/lib_components/components/SiteMap/SiteMapUtils.js +++ b/src/lib_components/components/SiteMap/SiteMapUtils.js @@ -30,6 +30,7 @@ import iconSiteRelocatableTerrestrialSVG from './svg/icon-site-relocatable-terre import iconSiteRelocatableTerrestrialSelectedSVG from './svg/icon-site-relocatable-terrestrial-selected.svg'; import iconSiteRelocatableAquaticSVG from './svg/icon-site-relocatable-aquatic.svg'; import iconSiteRelocatableAquaticSelectedSVG from './svg/icon-site-relocatable-aquatic-selected.svg'; +import iconSiteDecommissionedSVG from './svg/icon-site-decommissioned.svg'; import iconBenchmarkSVG from './svg/icon-benchmark.svg'; import iconBuoySVG from './svg/icon-buoy.svg'; @@ -60,6 +61,9 @@ import iconWetDepositionPointSVG from './svg/icon-wet-deposition-point.svg'; import statesShapesJSON from '../../staticJSON/statesShapes.json'; import domainsShapesJSON from '../../staticJSON/domainsShapes.json'; +const isCoord = (c) => Array.isArray(c) && c.length === 2 && c.every((x) => Number.isFinite(x)); +const round = (x) => Number.parseFloat(x.toFixed(4), 10); + export const MAP_ZOOM_RANGE = [1, 19]; export const OBSERVATORY_CENTER = [52.68, -110.75]; @@ -85,6 +89,9 @@ export const SORT_DIRECTIONS = { ASC: 'ASC', DESC: 'DESC' }; // For consistency in expressing site terrain export const SITE_TERRAINS = { AQUATIC: 'AQUATIC', TERRESTRIAL: 'TERRESTRIAL' }; +// For consistency in expressing the types of manual location data fed in through props +export const MANUAL_LOCATION_TYPES = { PROTOTYPE_SITE: 'PROTOTYPE_SITE' }; + // For consistency in differentiating discrete sets of data that can be tabulated together. // e.g. all LOCATIONS type feature data can coexist in a single table view with a // single column definition. But LOCATIONS and SITES shouldn't, as each set has @@ -140,6 +147,7 @@ export const FEATURE_DATA_SOURCES = { GRAPHQL_LOCATIONS_API: 'GRAPHQL_LOCATIONS_API', ARCGIS_ASSETS_API: 'ARCGIS_ASSETS_API', NEON_CONTEXT: 'NEON_CONTEXT', + MANUAL_LOCATIONS: 'MANUAL_LOCATIONS', // data injected by manualLocationData prop }; const SELECTED_ICON_OFFSET = 30; // Number of pixels bigger in one dimension for selected icons @@ -157,7 +165,7 @@ export const UNSELECTABLE_MARKER_FILTER = 'sepia(0.8) contrast(0.3) brightness(1 export const HIGHLIGHT_STATUS = { NONE: 'NONE', HIGHLIGHT: 'HIGHLIGHT', SELECT: 'SELECT' }; // For consistency in denoting which dinstinct user interfaces are available and which is visible -export const VIEWS = { MAP: 'MAP', TABLE: 'TABLE' }; +export const VIEWS = { MAP: 'MAP', TABLE: 'TABLE', SPLIT: 'SPLIT' }; // For consistency in denoting exclusive available mouse behaviors on the map export const MAP_MOUSE_MODES = { PAN: 'PAN', AREA_SELECT: 'AREA_SELECT' }; @@ -1013,6 +1021,7 @@ export const FEATURES = { iconSvg: iconSiteCoreTerrestrialSVG, iconSelectedSvg: iconSiteCoreTerrestrialSelectedSVG, iconShape: LOCATION_ICON_SVG_SHAPES.SQUARE.KEY, + maxZoom: 9, }, TERRESTRIAL_RELOCATABLE_SITES: { name: 'Terrestrial Relocatable Sites', @@ -1028,6 +1037,7 @@ export const FEATURES = { iconSvg: iconSiteRelocatableTerrestrialSVG, iconSelectedSvg: iconSiteRelocatableTerrestrialSelectedSVG, iconShape: LOCATION_ICON_SVG_SHAPES.CIRCLE.KEY, + maxZoom: 9, }, AQUATIC_CORE_SITES: { name: 'Aquatic Core Sites', @@ -1043,6 +1053,7 @@ export const FEATURES = { iconSvg: iconSiteCoreAquaticSVG, iconSelectedSvg: iconSiteCoreAquaticSelectedSVG, iconShape: LOCATION_ICON_SVG_SHAPES.SQUARE.KEY, + maxZoom: 9, }, AQUATIC_RELOCATABLE_SITES: { name: 'Aquatic Relocatable Sites', @@ -1058,6 +1069,23 @@ export const FEATURES = { iconSvg: iconSiteRelocatableAquaticSVG, iconSelectedSvg: iconSiteRelocatableAquaticSelectedSVG, iconShape: LOCATION_ICON_SVG_SHAPES.CIRCLE.KEY, + maxZoom: 9, + }, + DECOMMISSIONED_SITES: { + name: 'Decommissioned Sites', + nameSingular: 'Decommissioned Site', + type: FEATURE_TYPES.SITES.KEY, + description: 'No longer active in observatory', + parent: 'SITE_MARKERS', + attributes: { type: 'DECOMMISSIONED', terrain: 'DECOMMISSIONED' }, + dataSource: FEATURE_DATA_SOURCES.MANUAL_LOCATIONS, + primaryIdOnly: true, + featureShape: 'Marker', + iconScale: 1, + iconSvg: iconSiteDecommissionedSVG, + iconSelectedSvg: iconSiteDecommissionedSVG, + iconShape: LOCATION_ICON_SVG_SHAPES.CIRCLE.KEY, + maxZoom: 19, }, }; // Replicate keys as attributes to completely eliminate the need to write a feature key string @@ -1104,6 +1132,12 @@ export const BOUNDARY_COLORS = { export const calculateFeatureAvailability = (state) => { const featureIsAvailable = (feature) => { + // Special case: show SITES group at all zoom levels if DECOMMISSIONED_SITES are also shown + const hasDecomissionedSites = !!(state.manualLocationData || []).filter((manualLocation) => ( + manualLocation.manualLocationType === MANUAL_LOCATION_TYPES.PROTOTYPE_SITE + && !state.sites[manualLocation.siteCode] + )).length; + if (feature.KEY === 'SITE_MARKERS' && hasDecomissionedSites) { return true; } // Parent must be available (if the feature has a parent) if (typeof feature.parent === 'string' && !featureIsAvailable(FEATURES[feature.parent])) { return false; @@ -1371,9 +1405,10 @@ const featureIsHiddenByDefault = (key) => { export const DEFAULT_STATE = { view: { current: null, - initialized: Object.fromEntries( - Object.keys(VIEWS).map((view) => [view, false]), - ), + initialized: { + [VIEWS.MAP]: false, + [VIEWS.TABLE]: false, + }, }, neonContextHydrated: false, // Whether NeonContext data has been one-time hydrated into state overallFetch: { // Aggregation of all current fetch statuses for the SiteMap component @@ -1434,7 +1469,10 @@ export const DEFAULT_STATE = { featureDataFetchesHasAwaiting: false, // Boolean: track whether any data fetches are awaiting call featureDataFetches: Object.fromEntries( Object.keys(FEATURE_DATA_SOURCES) - .filter((dataSource) => dataSource !== FEATURE_DATA_SOURCES.NEON_CONTEXT) + .filter((dataSource) => ( + // eslint-disable-next-line max-len + ![FEATURE_DATA_SOURCES.MANUAL_LOCATIONS, FEATURE_DATA_SOURCES.NEON_CONTEXT].includes(dataSource) + )) .map((dataSource) => [dataSource, {}]), ), featureData: Object.fromEntries( @@ -1458,6 +1496,7 @@ export const DEFAULT_STATE = { }, }, fullscreen: false, + manualLocationData: null, }; // Initialize featureData and featureDataFetches objects for all features that have a dataSource @@ -1525,41 +1564,6 @@ if (domainsShapesJSON) { }); } -export const hydrateNeonContextData = (state, neonContextData) => { - const newState = { ...state, neonContextHydrated: true }; - // Sites - Object.keys(neonContextData.sites).forEach((siteCode) => { - newState.sites[siteCode] = { ...neonContextData.sites[siteCode] }; - const featureKey = Object.keys(FEATURES) - .filter((key) => FEATURES[key].type === FEATURE_TYPES.SITES.KEY) - .find((key) => ( - FEATURES[key].attributes.type === neonContextData.sites[siteCode].type - && FEATURES[key].attributes.terrain === neonContextData.sites[siteCode].terrain - )) || null; - if (featureKey !== null) { - // eslint-disable-next-line max-len - newState.featureData[FEATURE_TYPES.SITES.KEY][featureKey][siteCode] = newState.sites[siteCode]; - } - }); - // States - Object.keys(neonContextData.states).forEach((stateCode) => { - newState.featureData[FEATURE_TYPES.STATES.KEY][FEATURES.STATES.KEY][stateCode] = { - ...(newState.featureData[FEATURE_TYPES.STATES.KEY][FEATURES.STATES.KEY][stateCode] || {}), - ...neonContextData.states[stateCode], - sites: neonContextData.stateSites[stateCode], - }; - }); - // Domains - Object.keys(neonContextData.domains).forEach((domainCode) => { - newState.featureData[FEATURE_TYPES.DOMAINS.KEY][FEATURES.DOMAINS.KEY][domainCode] = { - ...(newState.featureData[FEATURE_TYPES.DOMAINS.KEY][FEATURES.DOMAINS.KEY][domainCode] || {}), - ...neonContextData.domains[domainCode], - sites: neonContextData.domainSites[domainCode], - }; - }); - return newState; -}; - /** PropTypes and defaultProps */ @@ -1614,6 +1618,10 @@ export const SITE_MAP_PROP_TYPES = { // Filter props search: PropTypes.string, features: PropTypes.arrayOf(PropTypes.oneOf(Object.keys(FEATURES))), + // Manual Location Data + manualLocationData: PropTypes.arrayOf(PropTypes.shape({ + manualLocationType: PropTypes.oneOf(Object.keys(MANUAL_LOCATION_TYPES)).isRequired, + })), }; export const SITE_MAP_DEFAULT_PROPS = { @@ -1639,6 +1647,8 @@ export const SITE_MAP_DEFAULT_PROPS = { // Filter props search: null, features: null, + // Manual Location Data + manualLocationData: null, }; /** @@ -1649,7 +1659,7 @@ export const SITE_MAP_DEFAULT_PROPS = { to the current zoom level and keep that in state. It is regenerated any time the zoom changes. */ // Get a single zoomed Leaflet icon instance -const getZoomedIcon = ( +export const getZoomedIcon = ( featureKey = null, zoom = 3, highlight = HIGHLIGHT_STATUS.NONE, @@ -1687,18 +1697,18 @@ const getZoomedIcon = ( const minScale = 0.2; const maxScale = Math.max((maxZoom - minZoom) / (MAP_ZOOM_RANGE[1] - MAP_ZOOM_RANGE[0]), 0.5); const baseScale = ((zoom || minZoom) - minZoom) / (maxZoom - minZoom); - const scale = (minScale + (baseScale * (maxScale - minScale))) * iconScale; + const scale = Math.max((minScale + (baseScale * (maxScale - minScale))) * iconScale, minScale); const iconProps = { iconUrl, iconRetinaUrl: iconUrl, - iconSize: iconSize.map((x) => x * scale), - iconAnchor: iconAnchor.map((x) => x * scale), - popupAnchor: popupAnchor.map((x) => x * scale), + iconSize: iconSize.map((x) => round(x * scale)), + iconAnchor: iconAnchor.map((x) => round(x * scale)), + popupAnchor: popupAnchor.map((x) => round(x * scale)), }; if (shadowUrl && shadowSize && shadowAnchor) { iconProps.shadowUrl = shadowUrl; - iconProps.shadowSize = shadowSize.map((x) => x * scale); - iconProps.shadowAnchor = shadowAnchor.map((x) => x * scale); + iconProps.shadowSize = shadowSize.map((x) => round(x * scale)); + iconProps.shadowAnchor = shadowAnchor.map((x) => round(x * scale)); } return new L.Icon(iconProps); }; @@ -1736,17 +1746,20 @@ export const getZoomedIcons = (zoom) => { // Creare a temporary non-rendering empty Leaflet map with dimensions, center, and zoom all // identical to a given state. This is necessary whenever needing to do pixel/latlon projections. -const getPhantomLeafletMap = (state) => { - const { aspectRatio: { currentValue: aspectRatio, widthReference } } = state; - L.Map.include({ - getSize: () => new L.Point(widthReference, widthReference * aspectRatio), +export const getPhantomLeafletMap = (state) => { + const { + aspectRatio: { currentValue: aspectRatio, widthReference }, + map: { center, zoom }, + } = state; + const x = widthReference || 400; + const y = (widthReference * aspectRatio) || 300; + const PhantomMapClass = L.Map.extend({ + includes: { + getSize: () => new L.Point(x, y), + }, }); const element = document.createElement('div'); - const map = new L.Map(element, { - center: state.map.center, - zoom: state.map.zoom, - }); - return map; + return new PhantomMapClass(element, { center, zoom }); }; export const mapIsAtFocusLocation = (state = {}) => (( @@ -1760,20 +1773,17 @@ export const mapIsAtFocusLocation = (state = {}) => (( export const getMapStateForFocusLocation = (state = {}) => { const { focusLocation } = state; - if (!focusLocation || !focusLocation.current) { return state; } + if (!focusLocation || !focusLocation.current) { return state.map; } const { current } = focusLocation; const { type = '', latitude, longitude } = focusLocation.data || {}; + // No latitude/longitude: previous map state + if (!Number.isFinite(latitude) || !Number.isFinite(longitude)) { return state.map; } + const newState = { ...state }; newState.map.bounds = null; newState.map.zoom = null; - // No latitude/longitude: return all defaults - if (!Number.isFinite(latitude) || !Number.isFinite(longitude)) { - newState.map.center = SITE_MAP_DEFAULT_PROPS.mapCenter; - return newState; - } - // Everything else (valid location with a center) newState.map.center = [latitude, longitude]; newState.map.bounds = null; @@ -1808,16 +1818,154 @@ export const getMapStateForFocusLocation = (state = {}) => { const newBounds = phantomMap.getBounds() || null; newState.map.bounds = !newBounds ? null : { /* eslint-disable no-underscore-dangle */ - lat: [newBounds._southWest.lat, newBounds._northEast.lat], - lng: [newBounds._southWest.lng, newBounds._northEast.lng], + lat: [round(newBounds._southWest.lat), round(newBounds._northEast.lat)], + lng: [round(newBounds._southWest.lng), round(newBounds._northEast.lng)], + /* eslint-enable no-underscore-dangle */ + }; + phantomMap.remove(); + } + + // Done + return newState.map; +}; + +export const findCentroid = (coords = []) => { + if (!Array.isArray(coords) || !coords.length || !coords.every(isCoord)) { return null; } + if (coords.length === 1) { return [...coords[0]]; } + const c = { x: 0, y: 0, z: 0 }; + coords.forEach((coord) => { + const rLat = coord[0] * (Math.PI / 180); + const rLng = coord[1] * (Math.PI / 180); + c.x += Math.cos(rLat) * Math.cos(rLng); + c.y += Math.cos(rLat) * Math.sin(rLng); + c.z += Math.sin(rLat); + }); + c.x /= coords.length; + c.y /= coords.length; + c.z /= coords.length; + const cLng = Math.atan2(c.y, c.x); + const cHyp = Math.sqrt((c.x ** 2) + (c.y ** 2)); + const cLat = Math.atan2(c.z, cHyp); + return [round(cLat * (180 / Math.PI)), round(cLng * (180 / Math.PI))]; +}; + +export const getMapStateForManualLocationData = (state) => { + const { map: previousMapState, manualLocationData } = state; + if (!Array.isArray(manualLocationData) || !manualLocationData.length) { + return previousMapState; + } + + const newState = { ...state }; + newState.map.zoom = null; + newState.map.bounds = null; + newState.map.center = SITE_MAP_DEFAULT_PROPS.mapCenter; + + const bounds = [[null, null], [null, null]]; + const locationCenters = manualLocationData.reduce((acc, cur) => { + if (Number.isFinite(cur.latitude) && Number.isFinite(cur.longitude)) { + acc.push([cur.latitude, cur.longitude]); + if (bounds[0][0] === null || cur.latitude < bounds[0][0]) { bounds[0][0] = cur.latitude; } + if (bounds[0][1] === null || cur.longitude < bounds[0][1]) { bounds[0][1] = cur.longitude; } + if (bounds[1][0] === null || cur.latitude > bounds[1][0]) { bounds[1][0] = cur.latitude; } + if (bounds[1][1] === null || cur.longitude > bounds[1][1]) { bounds[1][1] = cur.longitude; } + } + return acc; + }, []); + + if (!locationCenters.length) { return previousMapState; } + + if (locationCenters.length === 1) { + newState.map.center = [...locationCenters[0]]; + newState.map.zoom = 6; + } else { + newState.map.center = findCentroid(locationCenters); + const phantomZoomMap = getPhantomLeafletMap(newState); + phantomZoomMap.fitBounds(bounds, { animate: false, padding: [50, 50] }); + newState.map.zoom = phantomZoomMap.getZoom(); + phantomZoomMap.remove(); + } + + // Regenerate icons and bounds if we have a valid zoom + if (newState.map.zoom !== null) { + newState.map.zoomedIcons = getZoomedIcons(newState.map.zoom); + const phantomMap = getPhantomLeafletMap(newState); + const newBounds = phantomMap.getBounds() || null; + newState.map.bounds = !newBounds ? null : { + /* eslint-disable no-underscore-dangle */ + lat: [round(newBounds._southWest.lat), round(newBounds._northEast.lat)], + lng: [round(newBounds._southWest.lng), round(newBounds._northEast.lng)], /* eslint-enable no-underscore-dangle */ }; + phantomMap.remove(); } // Done return newState.map; }; +// Parse any manualLocationData into the mainfeatureData object so that its entries can be +// rendered consistently with other features +export const parseManualLocationFeatureData = (state) => { + if ( + !state.neonContextHydrated + || !Array.isArray(state.manualLocationData) || !state.manualLocationData.length + ) { return state; } + const newState = { ...state }; + state.manualLocationData.forEach((manualLocation) => { + const { siteCode } = manualLocation; + if ( + manualLocation.manualLocationType === MANUAL_LOCATION_TYPES.PROTOTYPE_SITE + && siteCode && !newState.sites[siteCode] + ) { + const featureType = FEATURE_TYPES.SITES.KEY; + const featureKey = FEATURES.DECOMMISSIONED_SITES.KEY; + newState.featureData[featureType][featureKey][siteCode] = manualLocation; + // Harmonize some values + newState.featureData[featureType][featureKey][siteCode].type = 'Decommissioned'; + newState.featureData[featureType][featureKey][siteCode].stateCode = manualLocation.state; + newState.featureData[featureType][featureKey][siteCode].domainCode = manualLocation.domain; + newState.featureData[featureType][featureKey][siteCode].description = manualLocation.siteName; + } + }); + newState.map = getMapStateForManualLocationData(newState); + return newState; +}; + +export const hydrateNeonContextData = (state, neonContextData) => { + const newState = { ...state, neonContextHydrated: true }; + // Sites + Object.keys(neonContextData.sites).forEach((siteCode) => { + newState.sites[siteCode] = { ...neonContextData.sites[siteCode] }; + const featureKey = Object.keys(FEATURES) + .filter((key) => FEATURES[key].type === FEATURE_TYPES.SITES.KEY) + .find((key) => ( + FEATURES[key].attributes.type === neonContextData.sites[siteCode].type + && FEATURES[key].attributes.terrain === neonContextData.sites[siteCode].terrain + )) || null; + if (featureKey !== null) { + // eslint-disable-next-line max-len + newState.featureData[FEATURE_TYPES.SITES.KEY][featureKey][siteCode] = newState.sites[siteCode]; + } + }); + // States + Object.keys(neonContextData.states).forEach((stateCode) => { + newState.featureData[FEATURE_TYPES.STATES.KEY][FEATURES.STATES.KEY][stateCode] = { + ...(newState.featureData[FEATURE_TYPES.STATES.KEY][FEATURES.STATES.KEY][stateCode] || {}), + ...neonContextData.states[stateCode], + sites: neonContextData.stateSites[stateCode], + }; + }); + // Domains + Object.keys(neonContextData.domains).forEach((domainCode) => { + newState.featureData[FEATURE_TYPES.DOMAINS.KEY][FEATURES.DOMAINS.KEY][domainCode] = { + ...(newState.featureData[FEATURE_TYPES.DOMAINS.KEY][FEATURES.DOMAINS.KEY][domainCode] || {}), + ...neonContextData.domains[domainCode], + sites: neonContextData.domainSites[domainCode], + }; + }); + return parseManualLocationFeatureData(newState); +}; + /** Aspect Ratio */ @@ -1866,7 +2014,6 @@ export const calculateLocationsInBounds = ( // This function flattens a geometry object to just coordinates so we can check if a boundary // is in the map. NOTE: extendPoints does not work with boundaries, only solitary points. const flatten = (items) => { - const isCoord = (c) => Array.isArray(c) && c.length === 2 && c.every((x) => Number.isFinite(x)); const flat = []; items.forEach((item) => { if (Array.isArray(item) && !isCoord(item)) { diff --git a/src/lib_components/components/SiteMap/StyleGuide.jsx b/src/lib_components/components/SiteMap/StyleGuide.jsx index a28ba710..99ae2bb7 100644 --- a/src/lib_components/components/SiteMap/StyleGuide.jsx +++ b/src/lib_components/components/SiteMap/StyleGuide.jsx @@ -1,10 +1,13 @@ /* eslint-disable react/jsx-one-expression-per-line, jsx-a11y/anchor-is-valid, react/no-unescaped-entities, max-len */ -import React from 'react'; +import React, { useState } from 'react'; import { makeStyles } from '@material-ui/core/styles'; +import AppBar from '@material-ui/core/AppBar'; import Divider from '@material-ui/core/Divider'; import Link from '@material-ui/core/Link'; +import Tab from '@material-ui/core/Tab'; +import Tabs from '@material-ui/core/Tabs'; import Typography from '@material-ui/core/Typography'; import DocBlock from '../../../components/DocBlock'; @@ -26,6 +29,10 @@ const useStyles = makeStyles((theme) => ({ divider: { margin: theme.spacing(3, 0), }, + appBar: { + marginTop: theme.spacing(3.5), + marginBottom: theme.spacing(4), + }, })); const propRows = [ @@ -88,6 +95,22 @@ const propRows = [ ), }, + { + name: 'manualLocationData', + type: ( +
    + Array of objects each containing a valid manualLocationType attribute +
    + ), + default: 'null', + description: ( +

    + When supplied this list of locations will show exclusively relative to other locations of + the same type (e.g. when providing prototype sites all sites not included will not appear). + The map will also initialize its zoom and bounds to show only the manual locations. +

    + ), + }, { name: 'mapBaseLayer', type: ( @@ -320,8 +343,39 @@ const propRows = [ }, ]; +const manualLocationData = [ + { + manualLocationType: 'PROTOTYPE_SITE', + domain: 'D04', + state: 'FL', + siteCode: 'MAME', + siteName: 'Mameyes', + latitude: 18.02192, + longitude: -66.61391, + }, + { + manualLocationType: 'PROTOTYPE_SITE', + domain: 'D08', + state: 'OK', + siteCode: 'BLUE', + siteName: 'Blue River', + latitude: 34.444218, + longitude: -96.624201, + }, + { + manualLocationType: 'PROTOTYPE_SITE', + domain: 'D03', + state: 'GA', + siteCode: 'ICHA', + siteName: 'Ichawaynochaway Creek', + latitude: 31.199, + longitude: -84.467, + }, +]; + export default function StyleGuide() { const classes = useStyles(Theme); + const [tabValue, setTabValue] = useState(0); return ( <> @@ -335,77 +389,203 @@ export default function StyleGuide() { import SiteMap from 'portal-core-components/lib/components/SiteMap'; `} - - Props - - + Note: Do not embed a SiteMap in a node that may be hidden. The Leaflet map relies on + having discrete non-zero width and height in order to initially render properly. If a + SiteMap is rendred in a component that may begin hidden the map itself will present + unrecoverable tiling errors. - Usage + Examples - Embedding a SiteMap requires no props to get the default observatory-scale view with - automatic sizing and aspect ratio based on the current viewport. + The SiteMap component has a broad variety of different modes for different use + cases. Use the tabs below to toggle between examples showing a few different combinations + of props. - - - - - {` - - `} - + + { setTabValue(newTabValue); }} + indicatorColor="primary" + textColor="primary" + variant="scrollable" + scrollButtons="auto" + > + + + + + + + + - - Focus Location + {/* 0: Basic */} + {tabValue !== 0 ? null : ( +
    + Basic + + Embedding a basic SiteMap requires no props to get the default observatory-scale view with + automatic sizing and aspect ratio based on the current viewport. + + + + + + {` + + `} + +
    + )} - - Use the location prop to initialize the Site Map on a particular location. Any - valid location name will work so long as it is represented in the locations API with a - geographical point (latitude and longitude). Additionally all Domain codes and US state - codes are also supported. - + {/* 1: Selection */} + {tabValue !== 1 ? null : ( +
    + Selection + + The SiteMap supports selection workflows for some feature types. See + the Map Selection Button documentation + for details and more varied examples. + + + + + + {` + + `} + +
    + )} - - - - - {` + {/* 2: Focus Location */} + {tabValue !== 2 ? null : ( +
    + Focus Location + + Use the location prop to initialize the Site Map on a particular location. Any + valid location name will work so long as it is represented in the locations API with a + geographical point (latitude and longitude). Additionally all Domain codes and US state + codes are also supported. + + + + + + {` `} - + +
    + )} - - Full Height Table View + {/* 3: Full Height Table */} + {tabValue !== 3 ? null : ( +
    + Full Height Table + + Use the tableFullHeight prop to allow for the table view to be unbounded by the + aspect ratio limitations imposed on the map. The result is, when switching into table view, + the height of the component may grow arbitrarily large with table content. Larger table page + sizes are also afforded. + + + + + + {` + + `} + +
    + )} - - Use the tableFullHeight prop to allow for the table view to be unbounded by the - aspect ratio limitations imposed on the map. The result is, when switching into table view, - the height of the component may grow arbitrarily large with table content. Larger table page - sizes are also afforded. - + {/* 4: Manual Locations */} + {tabValue !== 3 ? null : ( +
    + Manual Locations + + In some circumstances it is useful to pass the SiteMap a discrete list of locations for + exclusive display. This is done using the the manualLocationData prop. This should + be an array of objects that each has a manualLocationType attribute. The SiteMap + has an explicit list of supported values for this attribute that help it determine how to + process each manual location. Presently only "PROTOTYPE_SITE" is + supported. + + + + + + {` +const manualLocationData = [ + { + manualLocationType: 'PROTOTYPE_SITE', + domain: 'D04', + state: 'FL', + siteCode: 'MAME', + siteName: 'Mameyes', + latitude: 18.02192, + longitude: -66.61391, + }, + { + manualLocationType: 'PROTOTYPE_SITE', + domain: 'D08', + state: 'OK', + siteCode: 'BLUE', + siteName: 'Blue River', + latitude: 34.444218, + longitude: -96.624201, + }, + { + manualLocationType: 'PROTOTYPE_SITE', + domain: 'D03', + state: 'GA', + siteCode: 'ICHA', + siteName: 'Ichawaynochaway Creek', + latitude: 31.199, + longitude: -84.467, + }, +]; - - - - - {` - +return ( + +); `} - + +
    + )} + + {/* 5: Split View */} + {tabValue !== 5 ? null : ( +
    + Split View + + For some applications it may be preferable to see the map and table simultaneously. This + can be achieved by setting the view prop to "split". Both + map and table will still be linked such that only sites or locations visible in the + map will be visible in the table. + + + + + + {` + + `} + +
    + )} - Selection + Props - The SiteMap supports selection workflows for some feature types. - See the Map Selection Button documentation - for details and examples. + - ); } diff --git a/src/lib_components/components/SiteMap/__tests__/SiteMapContext.jsx b/src/lib_components/components/SiteMap/__tests__/SiteMapContext.jsx index e65eed58..7dbd55b2 100644 --- a/src/lib_components/components/SiteMap/__tests__/SiteMapContext.jsx +++ b/src/lib_components/components/SiteMap/__tests__/SiteMapContext.jsx @@ -15,6 +15,7 @@ import { FEATURES, FEATURE_TYPES, FEATURE_DATA_SOURCES, + MANUAL_LOCATION_TYPES, MAP_MOUSE_MODES, MAP_ZOOM_RANGE, MIN_TABLE_MAX_BODY_HEIGHT, @@ -453,6 +454,25 @@ describe('SiteMap - SiteMapContext', () => { const newState = calculateFeatureDataFetches(state); expect(newState).toStrictEqual(state); }); + test('only acts on subset of sites if manualLocationData contains prototype sites', () => { + calculateLocationsInBounds.mockReturnValue(['SB']); + state.manualLocationData = [ + { manualLocationType: MANUAL_LOCATION_TYPES.PROTOTYPE_SITE, siteCode: 'SB' }, + { manualLocationType: MANUAL_LOCATION_TYPES.PROTOTYPE_SITE, siteCode: 'FOO' }, + ]; + state.map.zoom = SITE_LOCATION_HIERARCHIES_MIN_ZOOM; + state.map.bounds = { lat: [-10, 10], lng: [-10, 10] }; + const newState = calculateFeatureDataFetches(state); + expect( + Object.keys(newState.featureDataFetches[REST_LOCATIONS_API][SITE_LOCATION_HIERARCHIES]), + ).toStrictEqual(['D01']); + expect( + newState.featureDataFetches[REST_LOCATIONS_API][SITE_LOCATION_HIERARCHIES].D01, + ).toBe(FETCH_STATUS.AWAITING_CALL); + expect(newState.overallFetch.expected).toBe(1); + expect(newState.overallFetch.pendingHierarchy).toBe(1); + expect(newState.featureDataFetchesHasAwaiting).toBe(true); + }); describe('SITE_LOCATION_HIERARCHIES', () => { test('applies no awaiting fetches if zoom is below minimum', () => { state.map.zoom = SITE_LOCATION_HIERARCHIES_MIN_ZOOM - 1; diff --git a/src/lib_components/components/SiteMap/__tests__/SiteMapTable.jsx b/src/lib_components/components/SiteMap/__tests__/SiteMapTable.jsx new file mode 100644 index 00000000..644f6c80 --- /dev/null +++ b/src/lib_components/components/SiteMap/__tests__/SiteMapTable.jsx @@ -0,0 +1,89 @@ +import { getTestableItems } from '../SiteMapTable'; + +import { MIN_TABLE_MAX_BODY_HEIGHT } from '../SiteMapUtils'; + +const { + ucWord, + parseSearchTerms, + searchOnAttribs, + getFeatureName, + calculateMaxBodyHeight, +} = getTestableItems(); + +describe('SiteMap - SiteMapTable', () => { + describe('ucWord()', () => { + test('capitalizes as expected', () => { + const cases = [ + ['FOO', 'Foo'], + ['bar', 'Bar'], + ['Qux', 'Qux'], + ]; + cases.forEach((c) => { + expect(ucWord(c[0])).toBe(c[1]); + }); + }); + }); + + describe('parseSearchTerms()', () => { + test('breaks up search strings into terms as expected', () => { + const cases = [ + ['"foo Bar" baz', ['foo bar', 'baz']], + ['lo-19 "IpsuM do"LOR', ['lo19', 'ipsum', 'lor']], + ['sit "Amet', ['sit', 'amet']], + ]; + cases.forEach((c) => { + expect(parseSearchTerms(c[0])).toStrictEqual(c[1]); + }); + }); + }); + + describe('searchOnAttribs()', () => { + test('applies search as expected', () => { + const cases = [ + ['foo', ['bar Foo qux', 'qux'], true], + ['foo', ['bar f oo qux', 'qux'], false], + ['', ['bar Foo qux', 'qux'], true], + ['qux', [null], false], + ]; + cases.forEach((c) => { + expect(searchOnAttribs(c[0], c[1])).toBe(c[2]); + }); + }); + }); + + describe('getFeatureName()', () => { + test('reflects back feature key if not valid', () => { + expect(getFeatureName('notValid')).toBe('notValid'); + }); + test('returns singular name if defined', () => { + expect(getFeatureName('STATES')).toBe('US State'); + }); + test('returns name if singular name is not defined', () => { + expect(getFeatureName('AQUATIC_WATERSHEDS')).toBe('Watersheds'); + }); + }); + + describe('calculateMaxBodyHeight()', () => { + test('returns mininum for an invalid tableRef', () => { + expect(calculateMaxBodyHeight()).toBe(MIN_TABLE_MAX_BODY_HEIGHT); + expect(calculateMaxBodyHeight({ current: null })).toBe(MIN_TABLE_MAX_BODY_HEIGHT); + }); + test('calculates value correctly based on children', () => { + const tableRef = { + current: { + clientHeight: 400, + children: [ + { + children: [ + { clientHeight: 80 }, // toolbar + { clientHeight: 30 }, + { clientHeight: 50 }, // pager + ], + }, + ], + }, + }; + expect(calculateMaxBodyHeight(tableRef)).toBe(270); + }); + }); +}); diff --git a/src/lib_components/components/SiteMap/__tests__/SiteMapUtils.js b/src/lib_components/components/SiteMap/__tests__/SiteMapUtils.js index c36cecbc..b9a6b49d 100644 --- a/src/lib_components/components/SiteMap/__tests__/SiteMapUtils.js +++ b/src/lib_components/components/SiteMap/__tests__/SiteMapUtils.js @@ -1,3 +1,6 @@ +import cloneDeep from 'lodash/cloneDeep'; + +import L from 'leaflet'; import { boundsAreValid, calculateLocationsInBounds, @@ -6,16 +9,67 @@ import { getHref, hydrateNeonContextData, mapIsAtFocusLocation, + getZoomedIcon, + getZoomedIcons, + findCentroid, + getPhantomLeafletMap, + getMapStateForFocusLocation, + getMapStateForManualLocationData, + parseManualLocationFeatureData, DEFAULT_STATE, FEATURES, + FEATURE_TYPES, + MANUAL_LOCATION_TYPES, SITE_MAP_PROP_TYPES, + HIGHLIGHT_STATUS, + SELECTION_STATUS, } from '../SiteMapUtils'; +jest.mock('leaflet', () => ({ + ...(jest.requireActual('leaflet')), + Icon: jest.fn(), +})); + +const neonContextData = { + sites: { + ABBY: { + type: 'RELOCATABLE', terrain: 'TERRESTRIAL', stateCode: 'WA', domainCode: 'D16', + }, + CLBJ: { + type: 'CORE', terrain: 'TERRESTRIAL', stateCode: 'TX', domainCode: 'D11', + }, + SUGG: { + type: 'CORE', terrain: 'AQUATIC', stateCode: 'FL', domainCode: 'D03', + }, + WLOU: { + type: 'RELOCATABLE', terrain: 'AQUATIC', stateCode: 'CO', domainCode: 'D13', + }, + }, + states: { + CO: { name: 'Colorado' }, + FL: { name: 'Florida' }, + TX: { name: 'Texas' }, + WA: { name: 'Washington' }, + }, + domains: { + D03: { name: 'Southeast' }, + D11: { name: 'Southern Plains' }, + D13: { name: 'Southern Rockies and Colorado Plateau' }, + D16: { name: 'Pacific Northwest' }, + }, + stateSites: { + CO: ['WLOU'], FL: ['SUGG'], TX: ['CLBJ'], WA: ['ABBY'], + }, + domainSites: { + D03: ['SUGG'], D11: ['CLBJ'], D13: ['WLOU'], D16: ['ABBY'], + }, +}; + describe('SiteMap - SiteMapUtils', () => { /** Functions */ - describe('boundsAreValid', () => { + describe('boundsAreValid()', () => { test('correctly identifies valid bounds', () => { [ { lat: [10, 20], lng: [10, 20] }, @@ -37,7 +91,7 @@ describe('SiteMap - SiteMapUtils', () => { }); }); - describe('calculateLocationsInBounds', () => { + describe('calculateLocationsInBounds()', () => { const locations = { A: { latitude: 15, longitude: 0 }, B: { latitude: 12, longitude: 0 }, @@ -128,7 +182,7 @@ describe('SiteMap - SiteMapUtils', () => { }); }); - describe('deriveFullObservatoryZoomLevel', () => { + describe('deriveFullObservatoryZoomLevel()', () => { test('returns FALLBACK_ZOOM if provided mapRef is not valid', () => { expect(deriveFullObservatoryZoomLevel()).toBe(2); expect(deriveFullObservatoryZoomLevel({ current: null })).toBe(2); @@ -156,7 +210,7 @@ describe('SiteMap - SiteMapUtils', () => { }); }); - describe('getDynamicAspectRatio', () => { + describe('getDynamicAspectRatio()', () => { let windowSpy; beforeEach(() => { windowSpy = jest.spyOn(global, 'window', 'get'); @@ -214,7 +268,7 @@ describe('SiteMap - SiteMapUtils', () => { }); }); - describe('getHref', () => { + describe('getHref()', () => { test('generates proper fallback href for missing or invalid key', () => { expect(getHref()).toEqual('#'); expect(getHref('INVALID_KEY', 'foo')).toEqual('#'); @@ -246,7 +300,7 @@ describe('SiteMap - SiteMapUtils', () => { }); }); - describe('hydrateNeonContextData', () => { + describe('hydrateNeonContextData()', () => { test('applies NeonContext data correctly', () => { const initialState = { neonContextHydrated: false, @@ -262,40 +316,6 @@ describe('SiteMap - SiteMapUtils', () => { DOMAINS: { DOMAINS: {} }, }, }; - const neonContextData = { - sites: { - ABBY: { - type: 'RELOCATABLE', terrain: 'TERRESTRIAL', stateCode: 'WA', domainCode: 'D16', - }, - CLBJ: { - type: 'CORE', terrain: 'TERRESTRIAL', stateCode: 'TX', domainCode: 'D11', - }, - SUGG: { - type: 'CORE', terrain: 'AQUATIC', stateCode: 'FL', domainCode: 'D03', - }, - WLOU: { - type: 'RELOCATABLE', terrain: 'AQUATIC', stateCode: 'CO', domainCode: 'D13', - }, - }, - states: { - CO: { name: 'Colorado' }, - FL: { name: 'Florida' }, - TX: { name: 'Texas' }, - WA: { name: 'Washington' }, - }, - domains: { - D03: { name: 'Southeast' }, - D11: { name: 'Southern Plains' }, - D13: { name: 'Southern Rockies and Colorado Plateau' }, - D16: { name: 'Pacific Northwest' }, - }, - stateSites: { - CO: ['WLOU'], FL: ['SUGG'], TX: ['CLBJ'], WA: ['ABBY'], - }, - domainSites: { - D03: ['SUGG'], D11: ['CLBJ'], D13: ['WLOU'], D16: ['ABBY'], - }, - }; expect(hydrateNeonContextData(initialState, neonContextData)).toStrictEqual({ neonContextHydrated: true, sites: { ...neonContextData.sites }, @@ -335,7 +355,371 @@ describe('SiteMap - SiteMapUtils', () => { }); }); - describe('mapIsAtFocusLocation', () => { + describe('getPhantomLeafletMap()', () => { + test('provides sane defaults for size when aspect ratio and width reference are null', () => { + const state = { + map: { center: [0, 0], zoom: null }, + aspectRatio: { currentValue: null, widthReference: null }, + }; + const phantomMap = getPhantomLeafletMap(state); + expect(phantomMap.getZoom()).toBe(0); + expect(phantomMap.getCenter()).toStrictEqual({ lat: 0, lng: 0 }); + expect(phantomMap.getSize()).toStrictEqual({ x: 400, y: 300 }); + }); + test('provides size based off of aspect ratio and width reference when defined', () => { + const state = { + map: { center: [70, -110], zoom: 4 }, + aspectRatio: { currentValue: 0.85, widthReference: 600 }, + }; + const phantomMap = getPhantomLeafletMap(state); + expect(phantomMap.getZoom()).toBe(4); + expect(phantomMap.getCenter()).toStrictEqual({ lat: 70, lng: -110 }); + expect(phantomMap.getSize()).toStrictEqual({ x: 600, y: 510 }); + }); + }); + + describe('getMapState functions', () => { + const newMapStateBoundsAreValid = (newMapState) => { + expect(typeof newMapState.bounds).toBe('object'); + expect(new Set(Object.keys(newMapState.bounds))).toStrictEqual(new Set(['lat', 'lng'])); + expect(newMapState.bounds.lat.every((c) => Number.isFinite(c))).toBe(true); + expect(newMapState.bounds.lng.every((c) => Number.isFinite(c))).toBe(true); + expect(newMapState.bounds.lat[0]).toBeLessThan(newMapState.center[0]); + expect(newMapState.bounds.lat[1]).toBeGreaterThan(newMapState.center[0]); + expect(newMapState.bounds.lng[0]).toBeLessThan(newMapState.center[1]); + expect(newMapState.bounds.lng[1]).toBeGreaterThan(newMapState.center[1]); + }; + const newMapStateZoomedIconsAreValid = (newMapState) => { + expect(typeof newMapState.zoomedIcons).toBe('object'); + expect(Object.keys(newMapState.zoomedIcons).length).toBeGreaterThan(0); + }; + + describe('getMapStateForManualLocationData()', () => { + test('returns previous map state if no manual locations are present', () => { + const state = { + map: { center: [60, -115], zoom: 10 }, + aspectRatio: { currentValue: 0.6, widthReference: 640 }, + }; + const newMapState = getMapStateForManualLocationData(state); + expect(newMapState).toStrictEqual(state.map); + }); + test('returns previous map state if manual locations are present but incomplete lat/lon', () => { + const state = { + map: { center: [60, -115], zoom: 10 }, + aspectRatio: { currentValue: 0.6, widthReference: 640 }, + manualLocationData: [{ latitude: 'bar', longitude: -35 }, { baz: 'qux', latitude: 24 }], + }; + const newMapState = getMapStateForManualLocationData(state); + expect(newMapState).toStrictEqual(state.map); + }); + test('builds out complete map state for a single manual location', () => { + const state = { + map: { center: [60, -115], zoom: 10 }, + aspectRatio: { currentValue: 0.6, widthReference: 640 }, + manualLocationData: [ + { latitude: 35, longitude: -85 }, + ], + }; + const newMapState = getMapStateForManualLocationData(state); + expect(typeof newMapState).toBe('object'); + expect(newMapState.zoom).toBe(6); + expect(newMapState.center).toStrictEqual([35, -85]); + newMapStateBoundsAreValid(newMapState); + newMapStateZoomedIconsAreValid(newMapState); + }); + test('builds out complete map state for multiple manual locations', () => { + const state = { + map: { center: [60, -115], zoom: 10 }, + aspectRatio: { currentValue: 0.6, widthReference: 640 }, + manualLocationData: [ + { latitude: 18.02192, longitude: -66.61391 }, + { latitude: 34.444218, longitude: -96.624201 }, + { latitude: 31.199, longitude: -84.467 }, + ], + }; + const newMapState = getMapStateForManualLocationData(state); + expect(typeof newMapState).toBe('object'); + expect(newMapState.zoom).toBe(4); + expect(newMapState.center).toStrictEqual([28.4642, -81.8378]); + newMapStateBoundsAreValid(newMapState); + newMapStateZoomedIconsAreValid(newMapState); + }); + }); + + describe('getMapStateForFocusLocation()', () => { + test('returns previous map state if no focus location is present', () => { + const state = { + map: { ...cloneDeep(DEFAULT_STATE.map) }, + aspectRatio: { currentValue: 0.6, widthReference: 640 }, + }; + const newMapState = getMapStateForFocusLocation(state); + expect(newMapState).toStrictEqual(state.map); + }); + test('returns previous map state if focus location is present but has invalid lat/lon', () => { + const state = { + map: { ...cloneDeep(DEFAULT_STATE.map) }, + aspectRatio: { currentValue: 0.6, widthReference: 640 }, + focusLocation: { + current: 'Foo', + data: { type: 'bar', latitude: 62, longitude: null }, + }, + }; + const newMapState = getMapStateForFocusLocation(state); + expect(newMapState).toStrictEqual(state.map); + }); + test('properly handles type SITE', () => { + const state = { + map: { ...cloneDeep(DEFAULT_STATE.map) }, + aspectRatio: { currentValue: 0.6, widthReference: 640 }, + focusLocation: { + current: 'FOO', + data: { type: 'SITE', latitude: 62, longitude: -12 }, + }, + sites: { + FOO: { zoom: 13 }, + }, + }; + const newMapState = getMapStateForFocusLocation(state); + expect(typeof newMapState).toBe('object'); + expect(newMapState.zoom).toBe(13); + expect(newMapState.center).toStrictEqual([62, -12]); + newMapStateBoundsAreValid(newMapState); + newMapStateZoomedIconsAreValid(newMapState); + }); + test('properly handles type DOMAIN', () => { + const state = { + map: { ...cloneDeep(DEFAULT_STATE.map) }, + aspectRatio: { currentValue: 0.6, widthReference: 640 }, + focusLocation: { + current: 'D18', + data: { type: 'DOMAIN', latitude: 42, longitude: -72 }, + }, + featureData: { + [FEATURE_TYPES.DOMAINS.KEY]: { + [FEATURES.DOMAINS.KEY]: { + D18: { zoom: 7 }, + }, + }, + }, + }; + const newMapState = getMapStateForFocusLocation(state); + expect(typeof newMapState).toBe('object'); + expect(newMapState.zoom).toBe(7); + expect(newMapState.center).toStrictEqual([42, -72]); + newMapStateBoundsAreValid(newMapState); + newMapStateZoomedIconsAreValid(newMapState); + }); + test('properly handles type STATE', () => { + const state = { + map: { ...cloneDeep(DEFAULT_STATE.map) }, + aspectRatio: { currentValue: 0.6, widthReference: 640 }, + focusLocation: { + current: 'CO', + data: { type: 'STATE', latitude: 56, longitude: -116 }, + }, + featureData: { + [FEATURE_TYPES.STATES.KEY]: { + [FEATURES.STATES.KEY]: { + CO: { zoom: 9 }, + }, + }, + }, + }; + const newMapState = getMapStateForFocusLocation(state); + expect(typeof newMapState).toBe('object'); + expect(newMapState.zoom).toBe(9); + expect(newMapState.center).toStrictEqual([56, -116]); + newMapStateBoundsAreValid(newMapState); + newMapStateZoomedIconsAreValid(newMapState); + }); + test('properly handles other feature type with set focusZoom', () => { + const state = { + map: { ...cloneDeep(DEFAULT_STATE.map) }, + aspectRatio: { currentValue: 0.6, widthReference: 640 }, + focusLocation: { + current: 'plotABC', + data: { type: 'OS Plot - mam', latitude: 61, longitude: -122 }, + }, + featureData: { + [FEATURE_TYPES.LOCATIONS.KEY]: { + [FEATURES.DISTRIBUTED_MAMMAL_GRIDS.KEY]: { + plotABC: { latitude: 61, longitude: -122 }, + }, + }, + }, + }; + const newMapState = getMapStateForFocusLocation(state); + expect(typeof newMapState).toBe('object'); + expect(newMapState.zoom).toBe(17); + expect(newMapState.center).toStrictEqual([61, -122]); + newMapStateBoundsAreValid(newMapState); + newMapStateZoomedIconsAreValid(newMapState); + }); + test('properly handles other feature type without set focusZoom', () => { + const state = { + map: { ...cloneDeep(DEFAULT_STATE.map) }, + aspectRatio: { currentValue: 0.6, widthReference: 640 }, + focusLocation: { + current: 'buoyABC', + data: { type: 'BUOY', latitude: 61, longitude: -122 }, + }, + featureData: { + [FEATURE_TYPES.LOCATIONS.KEY]: { + [FEATURES.AQUATIC_BUOYS.KEY]: { + plotABC: { latitude: 61, longitude: -122 }, + }, + }, + }, + }; + const newMapState = getMapStateForFocusLocation(state); + expect(typeof newMapState).toBe('object'); + expect(newMapState.zoom).toBe(18); + expect(newMapState.center).toStrictEqual([61, -122]); + newMapStateBoundsAreValid(newMapState); + newMapStateZoomedIconsAreValid(newMapState); + }); + }); + }); + + describe('parseManualLocationFeatureData()', () => { + test('does nothing when neonContextData is not yet hydrated', () => { + const initialState = { + neonContextHydrated: false, + sites: {}, + featureData: { SITES: { DECOMMISSIONED_SITES: {} } }, + map: { ...cloneDeep(DEFAULT_STATE.map) }, + manualLocationData: [ + { + manualLocationType: MANUAL_LOCATION_TYPES.PROTOTYPE_SITE, + siteCode: 'FOOO', + siteName: 'fooo site', + domain: 'D03', + state: 'FL', + }, + ], + }; + expect(parseManualLocationFeatureData(initialState)).toStrictEqual(initialState); + }); + test('does nothing when manualLocationData is missing or empty', () => { + const initialState = { + neonContextHydrated: true, + sites: { ...neonContextData.sites }, + featureData: { SITES: { DECOMMISSIONED_SITES: {} } }, + map: { ...cloneDeep(DEFAULT_STATE.map) }, + manualLocationData: [ + { + manualLocationType: MANUAL_LOCATION_TYPES.PROTOTYPE_SITE, + siteCode: 'FOOO', + siteName: 'fooo site', + domain: 'D03', + state: 'FL', + }, + ], + }; + expect(parseManualLocationFeatureData(initialState)).toStrictEqual(initialState); + }); + test('applies manualLocationData correctly when present', () => { + const initialState = { + neonContextHydrated: true, + sites: { ...neonContextData.sites }, + featureData: { + SITES: { + DECOMMISSIONED_SITES: {}, + }, + }, + map: { ...cloneDeep(DEFAULT_STATE.map) }, + manualLocationData: [ + { + manualLocationType: MANUAL_LOCATION_TYPES.PROTOTYPE_SITE, + siteCode: 'FOOO', + siteName: 'fooo site', + domain: 'D03', + state: 'FL', + }, + { + manualLocationType: MANUAL_LOCATION_TYPES.PROTOTYPE_SITE, + siteCode: 'ABBY', + siteName: 'abby site', // known incorrect, expect context data to override + domain: 'D11', + state: 'TX', + }, + { + manualLocationType: MANUAL_LOCATION_TYPES.PROTOTYPE_SITE, + siteCode: 'BRRR', + siteName: 'brrr site', + domain: 'D16', + state: 'WA', + }, + ], + }; + expect(parseManualLocationFeatureData(initialState)).toStrictEqual({ + neonContextHydrated: true, + sites: { ...neonContextData.sites }, + map: { ...DEFAULT_STATE.map, center: [52.68, -110.75] }, + manualLocationData: [ + { + manualLocationType: MANUAL_LOCATION_TYPES.PROTOTYPE_SITE, + type: 'Decommissioned', + siteCode: 'FOOO', + siteName: 'fooo site', + description: 'fooo site', + domain: 'D03', + domainCode: 'D03', + state: 'FL', + stateCode: 'FL', + }, + { + manualLocationType: MANUAL_LOCATION_TYPES.PROTOTYPE_SITE, + siteCode: 'ABBY', + siteName: 'abby site', // known incorrect, expect context data to override + domain: 'D11', + state: 'TX', + }, + { + manualLocationType: MANUAL_LOCATION_TYPES.PROTOTYPE_SITE, + type: 'Decommissioned', + siteCode: 'BRRR', + siteName: 'brrr site', + description: 'brrr site', + domain: 'D16', + domainCode: 'D16', + state: 'WA', + stateCode: 'WA', + }, + ], + featureData: { + SITES: { + DECOMMISSIONED_SITES: { + FOOO: { + manualLocationType: MANUAL_LOCATION_TYPES.PROTOTYPE_SITE, + type: 'Decommissioned', + siteCode: 'FOOO', + siteName: 'fooo site', + description: 'fooo site', + domain: 'D03', + domainCode: 'D03', + state: 'FL', + stateCode: 'FL', + }, + BRRR: { + manualLocationType: MANUAL_LOCATION_TYPES.PROTOTYPE_SITE, + type: 'Decommissioned', + siteCode: 'BRRR', + siteName: 'brrr site', + description: 'brrr site', + domain: 'D16', + domainCode: 'D16', + state: 'WA', + stateCode: 'WA', + }, + }, + }, + }, + }); + }); + }); + + describe('mapIsAtFocusLocation()', () => { test('correctly identifies when map center is at focus location', () => { expect(mapIsAtFocusLocation({ map: { zoom: 4, center: [-68.3, 15] }, @@ -384,6 +768,131 @@ describe('SiteMap - SiteMapUtils', () => { }); }); + describe('getZoomedIcon()', () => { + beforeEach(() => { L.Icon.mockReset(); }); + test('Uses placeholder when feature has no defined icon', () => { + getZoomedIcon(FEATURES.TOWER_AIRSHEDS.KEY); + expect(L.Icon.mock.calls.length).toBe(1); + expect(L.Icon.mock.calls[0].length).toBe(1); + expect(L.Icon.mock.calls[0][0]).toStrictEqual({ + iconUrl: 'icon-placeholder.svg', + iconRetinaUrl: 'icon-placeholder.svg', + iconSize: [15, 15], + iconAnchor: [7.5, 7.5], + popupAnchor: [0, -7.5], + shadowUrl: 'icon-shape-square-shadow.svg', + shadowSize: [18.8, 18.8], + shadowAnchor: [9.4, 9.4], + }); + }); + test('Scales appropriately with no defined zoom', () => { + getZoomedIcon(FEATURES.POUR_POINTS.KEY); + expect(L.Icon.mock.calls.length).toBe(1); + expect(L.Icon.mock.calls[0].length).toBe(1); + expect(L.Icon.mock.calls[0][0]).toStrictEqual({ + iconUrl: 'icon-pour-point.svg', + iconRetinaUrl: 'icon-pour-point.svg', + iconSize: [16, 18], + iconAnchor: [8, 9], + popupAnchor: [0, -9], + shadowUrl: 'icon-shape-homeplate-shadow.svg', + shadowSize: [20.2, 22.2], + shadowAnchor: [10.1, 11.1], + }); + }); + test('Scales appropriately with defined zoom', () => { + getZoomedIcon(FEATURES.TOWERS.KEY, 12); + expect(L.Icon.mock.calls.length).toBe(1); + expect(L.Icon.mock.calls[0].length).toBe(1); + expect(L.Icon.mock.calls[0][0]).toStrictEqual({ + iconUrl: 'icon-tower.svg', + iconRetinaUrl: 'icon-tower.svg', + iconSize: [48, 48], + iconAnchor: [24, 24], + popupAnchor: [0, -24], + shadowUrl: 'icon-shape-diamond-shadow.svg', + shadowSize: [59.52, 59.52], + shadowAnchor: [29.76, 29.76], + }); + }); + test('Handles explicit highlight status', () => { + getZoomedIcon(FEATURES.TERRESTRIAL_RELOCATABLE_SITES.KEY, 7, HIGHLIGHT_STATUS.HIGHLIGHT); + expect(L.Icon.mock.calls.length).toBe(1); + expect(L.Icon.mock.calls[0].length).toBe(1); + expect(L.Icon.mock.calls[0][0]).toStrictEqual({ + iconUrl: 'icon-site-relocatable-terrestrial.svg', + iconRetinaUrl: 'icon-site-relocatable-terrestrial.svg', + iconSize: [34, 34], + iconAnchor: [17, 17], + popupAnchor: [0, -17], + shadowUrl: 'icon-shape-circle-highlight.svg', + shadowSize: [51, 51], + shadowAnchor: [25.5, 25.5], + }); + }); + test('Handles explicit selection status', () => { + getZoomedIcon( + FEATURES.AQUATIC_RELOCATABLE_SITES.KEY, + 15, + HIGHLIGHT_STATUS.NONE, + SELECTION_STATUS.SELECTED, + ); + expect(L.Icon.mock.calls.length).toBe(1); + expect(L.Icon.mock.calls[0].length).toBe(1); + expect(L.Icon.mock.calls[0][0]).toStrictEqual({ + iconUrl: 'icon-site-relocatable-aquatic-selected.svg', + iconRetinaUrl: 'icon-site-relocatable-aquatic-selected.svg', + iconSize: [79.75, 79.75], + iconAnchor: [39.875, 39.875], + popupAnchor: [0, -39.875], + shadowUrl: 'icon-shape-circle-shadow.svg', + shadowSize: [94.25, 94.25], + shadowAnchor: [47.125, 47.125], + }); + }); + }); + + describe('getZoomedIcons()', () => { + test('generates an icon map with appropriate structure and feature representation', () => { + const zoomedIcons = getZoomedIcons(5); + expect(typeof zoomedIcons).toBe('object'); + Object.keys(zoomedIcons).forEach((f) => { + const feature = FEATURES[f]; + expect(!!feature.iconSvg && !!feature.iconShape).toBe(true); + expect(typeof zoomedIcons[f]).toBe('object'); + if (FEATURE_TYPES[feature.type].selectable) { + expect(Object.keys(zoomedIcons[f])).toStrictEqual(Object.keys(SELECTION_STATUS)); + } else { + expect(Object.keys(zoomedIcons[f])).toStrictEqual([SELECTION_STATUS.UNSELECTED]); + } + Object.keys(zoomedIcons[f]).forEach((s) => { + expect(Object.keys(zoomedIcons[f][s])).toStrictEqual(Object.keys(HIGHLIGHT_STATUS)); + }); + }); + }); + }); + + describe('findCentroid()', () => { + test('returns null if not provided a non-empty array of all valid points', () => { + expect(findCentroid()).toBe(null); + expect(findCentroid([])).toBe(null); + expect(findCentroid(['foo'])).toBe(null); + expect(findCentroid([[0, 0], [0, 'foo']])).toBe(null); + }); + test('reflects back only valid point if only one is present', () => { + expect(findCentroid([[0, 0]])).toStrictEqual([0, 0]); + expect(findCentroid([[30, 10]])).toStrictEqual([30, 10]); + }); + test('calculates centroids when provided more than one valid point', () => { + expect( + findCentroid([[-10, -10], [10, 10]]), + ).toStrictEqual([0, 0]); + expect( + findCentroid([[18.02192, -66.61391], [34.444218, -96.624201], [31.199, -84.467]]), + ).toStrictEqual([28.4642, -81.8378]); + }); + }); + describe('SelectionLimitPropType', () => { const { selectionLimit: SelectionLimitPropType } = SITE_MAP_PROP_TYPES; test('valid for null', () => { diff --git a/src/lib_components/components/SiteMap/svg/icon-site-decommissioned.svg b/src/lib_components/components/SiteMap/svg/icon-site-decommissioned.svg new file mode 100644 index 00000000..3abecdf2 --- /dev/null +++ b/src/lib_components/components/SiteMap/svg/icon-site-decommissioned.svg @@ -0,0 +1,94 @@ + + + + + + + + image/svg+xml + + + + + + + + + + + + + diff --git a/src/lib_components/components/Theme/Theme.jsx b/src/lib_components/components/Theme/Theme.jsx index 311981e8..0a3af24d 100644 --- a/src/lib_components/components/Theme/Theme.jsx +++ b/src/lib_components/components/Theme/Theme.jsx @@ -292,7 +292,7 @@ const baseTheme = createMuiTheme({ }, MuiBackdrop: { root: { - zIndex: 3, + zIndex: 50, }, }, MuiBreadcrumbs: { @@ -444,7 +444,7 @@ const baseTheme = createMuiTheme({ zIndex: 105, }, paper: { - zIndex: 3, + zIndex: 10, }, }, MuiFormControlLabel: { diff --git a/src/lib_components/components/TimeSeriesViewer/TimeSeriesViewerAxes.jsx b/src/lib_components/components/TimeSeriesViewer/TimeSeriesViewerAxes.jsx index 9b3db521..c050dd43 100644 --- a/src/lib_components/components/TimeSeriesViewer/TimeSeriesViewerAxes.jsx +++ b/src/lib_components/components/TimeSeriesViewer/TimeSeriesViewerAxes.jsx @@ -31,8 +31,8 @@ const useStyles = makeStyles((theme) => ({ flexWrap: 'wrap', }, smallButton: { - fontSize: '0.8rem', - padding: theme.spacing(0.125, 0.75), + fontSize: '0.55rem', + padding: theme.spacing(0.25, 0.75), whiteSpace: 'nowrap', }, smallButtonIcon: { diff --git a/src/lib_components/components/TimeSeriesViewer/TimeSeriesViewerContainer.jsx b/src/lib_components/components/TimeSeriesViewer/TimeSeriesViewerContainer.jsx index 3abaaa7d..6810415e 100644 --- a/src/lib_components/components/TimeSeriesViewer/TimeSeriesViewerContainer.jsx +++ b/src/lib_components/components/TimeSeriesViewer/TimeSeriesViewerContainer.jsx @@ -85,6 +85,10 @@ const useStyles = makeStyles((theme) => ({ margin: theme.spacing(-0.5, -0.5, 0, -0.5), padding: theme.spacing(20, 4, 4, 4), backgroundColor: 'rgba(255, 255, 255, 0.75)', + '& .MuiTypography-root': { + boxShadow: '0px 0px 20px 25px #fff', + backgroundColor: '#fff', + }, }, titleContainer: { marginBottom: theme.spacing(2), @@ -109,6 +113,12 @@ const useStyles = makeStyles((theme) => ({ fontSize: '85%', marginRight: Theme.spacing(1), }, + errorIcon: { + color: Theme.colors.RED[400], + }, + warningIcon: { + color: Theme.colors.GOLD[500], + }, })); const useTabsStyles = makeStyles((theme) => ({ @@ -489,14 +499,15 @@ export default function TimeSeriesViewerContainer() { const renderGraphOverlay = () => { const isError = state.status === TIME_SERIES_VIEWER_STATUS.ERROR; + const isWarning = state.status === TIME_SERIES_VIEWER_STATUS.WARNING; const isLoading = !isError && state.status !== TIME_SERIES_VIEWER_STATUS.READY; - if (isError) { + if (isError || isWarning) { return (
    {state.displayError || 'An unknown error occurred; unable to visualize data product'} - +
    ); } diff --git a/src/lib_components/components/TimeSeriesViewer/TimeSeriesViewerContext.jsx b/src/lib_components/components/TimeSeriesViewer/TimeSeriesViewerContext.jsx index b2935443..ff491eb2 100644 --- a/src/lib_components/components/TimeSeriesViewer/TimeSeriesViewerContext.jsx +++ b/src/lib_components/components/TimeSeriesViewer/TimeSeriesViewerContext.jsx @@ -48,7 +48,8 @@ export const TIME_SERIES_VIEWER_STATUS = { LOADING_META: 'LOADING_META', // Actively loading meta data (sites, variables, and positions) READY_FOR_DATA: 'READY_FOR_DATA', // Ready to trigger fetches for data LOADING_DATA: 'LOADING_DATA', // Actively loading plottable series data - ERROR: 'ERROR', // Stop everything because problem + ERROR: 'ERROR', // Stop everything because problem, do not trigger new fetches no matter what + WARNING: 'WARNING', // Current selection/data makes a graph not possible; show warning READY_FOR_SERIES: 'READY_FOR_SERIES', // Ready to re-calculate series data for the graph READY: 'READY', // Ready for user input }; @@ -103,8 +104,9 @@ const generateYAxisRange = (axis = {}) => { !Object.keys(Y_AXIS_RANGE_MODES).includes(rangeMode) || !Number.isFinite(standardDeviation) || !Number.isFinite(precision)) { return axisRange; } - let low = (dataRange[0] || 0) - standardDeviation; - let high = (dataRange[1] || 0) + standardDeviation; + const margin = standardDeviation !== 0 ? standardDeviation : (dataRange[0] / 2); + let low = (dataRange[0] || 0) - margin; + let high = (dataRange[1] || 0) + margin; low = parseFloat(low.toFixed(precision), 10); high = parseFloat(high.toFixed(precision), 10); if (rangeMode === Y_AXIS_RANGE_MODES.FROM_ZERO) { return [0, high]; } @@ -173,6 +175,8 @@ const DEFAULT_STATE = { y1: cloneDeep(DEFAULT_AXIS_STATE), y2: cloneDeep(DEFAULT_AXIS_STATE), }, + isDefault: true, + invalidDefaultVariables: new Set(), // canBeDefault vars found to have no series data after load }, availableQualityFlags: new Set(), availableTimeSteps: new Set(['auto']), @@ -516,6 +520,7 @@ const parseSitePositions = (site, csv) => { */ const applyDefaultsToSelection = (state) => { const { + status, product, variables, selection, @@ -546,7 +551,8 @@ const applyDefaultsToSelection = (state) => { if (Object.keys(variables).length) { // Ensure the selection has at least one variable if (!selection.variables.length) { - const defaultVar = Object.keys(variables).find((v) => variables[v].canBeDefault); + const defaultVar = Object.keys(variables) + .find((v) => (variables[v].canBeDefault && !selection.invalidDefaultVariables.has(v))); if (defaultVar) { selection.variables.push(defaultVar); selection.yAxes.y1.units = variables[defaultVar].units; @@ -627,6 +633,24 @@ const applyDefaultsToSelection = (state) => { selection.yAxes[yAxis].axisRange = generateYAxisRange(selection.yAxes[yAxis]); } }); + // Edge case: if the default site/month/position/variable produces a series with no data then + // it wasn't a good default, but we had no way of knowing until the data series was fully parsed. + // Here we check for this scenario and if we're in it then add the variable to the + // invalidDefaultVariables set, remove the variable from the selection, and run again. + // We'll recurse through the variables available for the site/month/position until we find one + // that works or show a meaningful error instructing the user to select a different site, + // month, or position. Note that as soon as the user makes an active selection of any kind + // isDefault will be false for the lifetime of the time series viewer instance, so this automated + // removal of a selected variable can only happen before any user selection happens. + if ( + status === TIME_SERIES_VIEWER_STATUS.READY_FOR_SERIES + && selection.isDefault && selection.variables.length + && selection.yAxes.y1.dataRange.every((x) => x === null) + ) { + selection.invalidDefaultVariables.add(selection.variables[0]); + selection.variables = []; + return applyDefaultsToSelection({ ...state, selection }); + } // Generate a new digest for effect comparison selection.digest = JSON.stringify({ sites: selection.sites, @@ -697,6 +721,11 @@ const reducer = (state, action) => { newState.status = TIME_SERIES_VIEWER_STATUS.READY_FOR_DATA; } }; + const softFail = (error) => { + newState.status = TIME_SERIES_VIEWER_STATUS.WARNING; + newState.displayError = error; + return newState; + }; const fail = (error) => { newState.status = TIME_SERIES_VIEWER_STATUS.ERROR; newState.displayError = error; @@ -827,6 +856,14 @@ const reducer = (state, action) => { // Regenerate Graph Data Actions case 'regenerateGraphData': + if ( + !action.graphData.series.length + || Object.keys(state.selection.yAxes).every((y) => ( + state.selection.yAxes[y].dataRange.every((x) => x === null) + )) + ) { + return softFail('Current selection of dates/sites/positions/variables does not have any valid numeric data.'); + } newState.graphData = action.graphData; newState.status = TIME_SERIES_VIEWER_STATUS.READY; return newState; @@ -871,6 +908,9 @@ const reducer = (state, action) => { delete newState.dataFetches[action.token]; newState.status = TIME_SERIES_VIEWER_STATUS.READY_FOR_SERIES; calcSelection(); + if (!newState.selection.variables.length) { + return softFail('None of the variables for this product\'s default site/month/position have data. Please select a different site, month, or position.'); + } return newState; case 'noDataFilesFetchNecessary': newState.status = TIME_SERIES_VIEWER_STATUS.READY_FOR_SERIES; @@ -904,12 +944,14 @@ const reducer = (state, action) => { // Core Selection Actions case 'selectDateRange': + newState.selection.isDefault = false; newState.selection.dateRange = action.dateRange; newState.selection.activelySelectingDateRange = action.dateRange; calcSelection(); calcStatus(); return newState; case 'selectVariables': + newState.selection.isDefault = false; parsedContent = limitVariablesToTwoUnits(state, action.variables); newState.selection.variables = parsedContent.variables; /* eslint-disable prefer-destructuring */ @@ -939,6 +981,7 @@ const reducer = (state, action) => { !state.selection.yAxes[action.axis] || !Object.keys(Y_AXIS_RANGE_MODES).includes(action.mode) ) { return state; } + newState.selection.isDefault = false; newState.selection.yAxes[action.axis].rangeMode = action.mode; if (action.mode !== Y_AXIS_RANGE_MODES.CUSTOM) { newState.selection.yAxes[action.axis].axisRange = generateYAxisRange( @@ -953,6 +996,7 @@ const reducer = (state, action) => { && action.range.every((v) => typeof v === 'number') && action.range[0] < action.range[1] )) { return state; } + newState.selection.isDefault = false; newState.selection.yAxes[action.axis].axisRange = action.range; return newState; /* @@ -965,9 +1009,11 @@ const reducer = (state, action) => { */ // Option Selection Actions case 'selectLogScale': + newState.selection.isDefault = false; newState.selection.logscale = !!action.logscale; return newState; case 'selectSwapYAxes': + newState.selection.isDefault = false; if (state.selection.yAxes.y2.units === null) { return state; } parsedContent = { y1: cloneDeep(state.selection.yAxes.y2), @@ -976,16 +1022,20 @@ const reducer = (state, action) => { newState.selection.yAxes = parsedContent; return newState; case 'setRollPeriod': + newState.selection.isDefault = false; newState.selection.rollPeriod = action.rollPeriod; return newState; case 'selectAllQualityFlags': + newState.selection.isDefault = false; newState.selection.qualityFlags = Array.from(state.availableQualityFlags); calcStatus(); return newState; case 'selectNoneQualityFlags': + newState.selection.isDefault = false; newState.selection.qualityFlags = []; return newState; case 'selectToggleQualityFlag': + newState.selection.isDefault = false; if (action.selected && !state.selection.qualityFlags.includes(action.qualityFlag)) { newState.selection.qualityFlags.push(action.qualityFlag); } else if (!action.selected) { @@ -995,11 +1045,13 @@ const reducer = (state, action) => { calcStatus(); return newState; case 'selectTimeStep': + newState.selection.isDefault = false; if (!state.availableTimeSteps.has(action.timeStep)) { return state; } newState.selection.timeStep = action.timeStep; calcStatus(); return newState; case 'selectAddSite': + newState.selection.isDefault = false; if (!state.product.sites[action.siteCode]) { return state; } if (state.selection.sites.some((site) => site.siteCode === action.siteCode)) { return state; } newState.selection.sites.push({ siteCode: action.siteCode, positions: [] }); @@ -1011,6 +1063,7 @@ const reducer = (state, action) => { !action.siteCodes || !action.siteCodes.constructor || action.siteCodes.constructor.name !== 'Set' || !action.siteCodes.size ) { return state; } + newState.selection.isDefault = false; // Remove any sites that are no longer in the selected set newState.selection.sites = newState.selection.sites .filter((site) => action.siteCodes.has(site.siteCode)); @@ -1028,6 +1081,7 @@ const reducer = (state, action) => { if (!state.selection.sites.some((site) => site.siteCode === action.siteCode)) { return state; } + newState.selection.isDefault = false; newState.selection.sites = newState.selection.sites .filter((site) => site.siteCode !== action.siteCode); calcSelection(); @@ -1042,6 +1096,7 @@ const reducer = (state, action) => { if (!action.positions.every((p) => ( Object.keys(state.product.sites[action.siteCode].positions).includes(p) ))) { return state; } + newState.selection.isDefault = false; newState.selection.sites[selectedSiteIdx].positions = [...action.positions]; calcSelection(); calcStatus(); diff --git a/src/lib_components/components/TimeSeriesViewer/__tests__/TimeSeriesViewerContext.jsx b/src/lib_components/components/TimeSeriesViewer/__tests__/TimeSeriesViewerContext.jsx index 7dea9849..b5376e7d 100644 --- a/src/lib_components/components/TimeSeriesViewer/__tests__/TimeSeriesViewerContext.jsx +++ b/src/lib_components/components/TimeSeriesViewer/__tests__/TimeSeriesViewerContext.jsx @@ -1256,11 +1256,31 @@ t1_2min,v3QM,v3QMdesc,real,percent,basic,* }); }); describe('regenerateGraphData', () => { - test('applies graphData and sets status', () => { - expect(reducer(state, { type: 'regenerateGraphData', graphData: { foo: 'bar' } })) + test('soft fails to warning if graphData has no series', () => { + expect(reducer(state, { type: 'regenerateGraphData', graphData: { series: [] } })) .toStrictEqual({ ...state, - graphData: { foo: 'bar' }, + status: TIME_SERIES_VIEWER_STATUS.WARNING, + displayError: 'Current selection of dates/sites/positions/variables does not have any valid numeric data.', + }); + }); + test('soft fails to warning if no yAxes have a non-null dataRange', () => { + state.selection.yAxes.y1.dataRange = [null, null]; + state.selection.yAxes.y2.dataRange = [null, null]; + expect(reducer(state, { type: 'regenerateGraphData', graphData: { series: ['foo'] } })) + .toStrictEqual({ + ...state, + status: TIME_SERIES_VIEWER_STATUS.WARNING, + displayError: 'Current selection of dates/sites/positions/variables does not have any valid numeric data.', + }); + }); + test('applies graphData and sets status if ', () => { + state.selection.yAxes.y1.dataRange = [0.1, 0.4]; + state.selection.yAxes.y2.dataRange = [null, null]; + expect(reducer(state, { type: 'regenerateGraphData', graphData: { series: ['foo'] } })) + .toStrictEqual({ + ...state, + graphData: { series: ['foo'] }, status: TIME_SERIES_VIEWER_STATUS.READY, }); }); @@ -1409,9 +1429,26 @@ t1_2min,v3QM,v3QMdesc,real,percent,basic,* reducer(modifiedState, { type: 'fetchDataFilesCompleted', token: 'bar' }), ).toStrictEqual(modifiedState); }); - test('clears the data fetches token and applies default selections', () => { + test('clears the data fetches token and applies default selections with warning', () => { + state.dataFetches = { foo: true }; + state.product.sites = { S1: { ...cloneDeep(expectedInitialSite) } }; + const newState = reducer(state, { type: 'fetchDataFilesCompleted', token: 'foo' }); + expect(newState.dataFetches).toStrictEqual({}); + expect(newState.status).toBe(TIME_SERIES_VIEWER_STATUS.WARNING); + expect(newState.selection.sites).toStrictEqual([{ siteCode: 'S1', positions: [] }]); + }); + test('ends on ready for series status if able', () => { state.dataFetches = { foo: true }; state.product.sites = { S1: { ...cloneDeep(expectedInitialSite) } }; + state.selection.isDefault = false; + state.variables = { + foo: { + canBeDefault: true, isDateTime: false, downloadPkg: 'basic', units: 'foos', + }, + startDate: { + canBeDefault: false, isDateTime: true, downloadPkg: 'basic', units: 'NA', + }, + }; const newState = reducer(state, { type: 'fetchDataFilesCompleted', token: 'foo' }); expect(newState.dataFetches).toStrictEqual({}); expect(newState.status).toBe(TIME_SERIES_VIEWER_STATUS.READY_FOR_SERIES); diff --git a/src/lib_components/remoteAssets/drupal-header.html.js b/src/lib_components/remoteAssets/drupal-header.html.js index ba88bda5..a7119efb 100644 --- a/src/lib_components/remoteAssets/drupal-header.html.js +++ b/src/lib_components/remoteAssets/drupal-header.html.js @@ -727,6 +727,10 @@ export default html = `