diff --git a/__tests__/gui/common_cy_functions/general.js b/__tests__/gui/common_cy_functions/general.js index b02e09769..2d5f89752 100644 --- a/__tests__/gui/common_cy_functions/general.js +++ b/__tests__/gui/common_cy_functions/general.js @@ -454,7 +454,7 @@ export function confirmNoChoroplethFilterSelected() { cy.get(`${mapBottomItems} .map-options .map-options__filter-row:visible`).should('have.length', 1); cy.get(`${mapBottomItems} .map-options .map-options__filter-row:visible .mapping-options__filter`) .eq(0) - .find('div.MuiSelect-select em') + .find('div.MuiSelect-select') .should('have.text', 'Select an attribute'); cy.get(`${mapBottomItems} .map-options .map-options__filter-row:visible .mapping-options__filter`).eq(1).should('have.class', 'disabled'); } @@ -463,7 +463,7 @@ export function confirmNoChartFilterSelected() { cy.get('.rich-data-content .profile-indicator__filter-row:visible').should('have.length', 1); cy.get('.rich-data-content .profile-indicator__filter-row:visible .profile-indicator__filter') .eq(0) - .find('div.MuiSelect-select em') + .find('div.MuiSelect-select') .should('have.text', 'Select an attribute'); cy.get('.rich-data-content .profile-indicator__filter-row:visible .profile-indicator__filter').eq(1).should('have.class', 'disabled'); } diff --git a/__tests__/gui/sharing_2/sharing.js b/__tests__/gui/sharing_2/sharing.js index 958ee4091..4f2058a3c 100644 --- a/__tests__/gui/sharing_2/sharing.js +++ b/__tests__/gui/sharing_2/sharing.js @@ -106,7 +106,7 @@ Then(/^I confirm that the chart is not filtered$/, function () { // cy.get('.rich-data-content .profile-indicator__filter-row:visible:eq(0)').should('have.length', 1); cy.get('.rich-data-content .profile-indicator__filter-row:visible:eq(0) .profile-indicator__filter') .eq(0) - .find('div.MuiSelect-select em') + .find('div.MuiSelect-select') .should('have.text', 'Select an attribute'); cy.get('.rich-data-content .profile-indicator__filter-row:visible:eq(0) .profile-indicator__filter').eq(1).should('have.class', 'disabled'); }); diff --git a/cypress/downloads/map.png b/cypress/downloads/map.png new file mode 100644 index 000000000..2a7f26912 Binary files /dev/null and b/cypress/downloads/map.png differ diff --git a/src/custom-css/general.scss b/src/custom-css/general.scss index 6649fa75e..be2c3c0fa 100644 --- a/src/custom-css/general.scss +++ b/src/custom-css/general.scss @@ -164,6 +164,10 @@ } } +.warning-modal{ + position: fixed; +} + // CSS for new filters .filter-container { border-radius: 4px; diff --git a/src/js/configurations/geography_sa.js b/src/js/configurations/geography_sa.js index 429afe34d..f5240d4d9 100644 --- a/src/js/configurations/geography_sa.js +++ b/src/js/configurations/geography_sa.js @@ -67,6 +67,12 @@ export class Config { return false; } + get pointSearchEnabled(){ + if(this.config["point_search_enabled"] != undefined) + return this.config["point_search_enabled"]; + return true; + } + get siteWideFiltersEnabled() { if (this.config["site_wide_filters_enabled"] != undefined) return this.config["site_wide_filters_enabled"]; diff --git a/src/js/elements/header/search.js b/src/js/elements/header/search.js index 3a0c6a400..4bcd187b5 100644 --- a/src/js/elements/header/search.js +++ b/src/js/elements/header/search.js @@ -32,13 +32,14 @@ export class Search extends Component { * constructor of the class * sets the default values, calls init function(this.setSearchInput) * */ - constructor(parent, api, profileId, minChars) { + constructor(parent, api, profileId, minChars, config) { super(parent); minLength = minChars; this.api = api; this.profileId = profileId; this.plate = null; + this.config = config; this.prepareDomElements(); this.setSearchInput(); @@ -60,9 +61,11 @@ export class Search extends Component { this.updateGeoSearchHeader(titleRow); this.updateGeoSearch(); - this.appendSearchSeparator(); - this.appendPointsTitle(titleRow); - this.appendPointsNoData(); + if (this.config.pointSearchEnabled) { + this.appendSearchSeparator(); + this.appendPointsTitle(titleRow); + this.appendPointsNoData(); + } } updateGeoSearchHeader(titleRow) { @@ -282,13 +285,15 @@ export class Search extends Component { response(data); }) - const mapCenter = self.getMapCenter(); - self.api.searchPointsByDistance(self.profileId, searchTerm, mapCenter.lat, mapCenter.lng).then(data => { - self.removePointsNoData(); - self.appendPointsHeaderRow(); - self.appendPointsTable(searchTerm, data); - self.updatePointsHeaderSummary(data); - }) + if (self.config.pointSearchEnabled) { + const mapCenter = self.getMapCenter(); + self.api.searchPointsByDistance(self.profileId, searchTerm, mapCenter.lat, mapCenter.lng).then(data => { + self.removePointsNoData(); + self.appendPointsHeaderRow(); + self.appendPointsTable(searchTerm, data); + self.updatePointsHeaderSummary(data); + }) + } }, 0) } }) diff --git a/src/js/load.js b/src/js/load.js index ae69b52fa..532371218 100644 --- a/src/js/load.js +++ b/src/js/load.js @@ -77,8 +77,8 @@ class Application extends Component { const pointData = new PointData(this, api, mapcontrol.map, profileId, config); const pointDataTray = new PointDataTray(this, api, profileId, config.watermarkEnabled, config.ccLicenseEnabled); const mapchip = new MapChip(this, config.choropleth, config.siteWideFiltersEnabled, config.restrictValues, config.defaultFilters); - const search = new Search(this, api, profileId, 2); - const profileLoader = new ProfileLoader(this, formattingConfig, api, profileId, config.config, config.watermarkEnabled, config.siteWideFiltersEnabled, config.restrictValues, config.defaultFilters, config.chartColorRange); + const profileLoader = new ProfileLoader(this, formattingConfig, api, profileId, config.config, config.watermarkEnabled, config.siteWideFiltersEnabled, config.restrictValues, config.defaultFilters, config.chartColorRange, config.ccLicenseEnabled); + const search = new Search(this, api, profileId, 2, config); const locationInfoBox = new LocationInfoBox(this, formattingConfig); const zoomToggle = new ZoomToggle(this); const preferredChildToggle = new PreferredChildToggle(this); diff --git a/src/js/profile/blocks/indicator.js b/src/js/profile/blocks/indicator.js index 4d86d02df..3f32bc05d 100644 --- a/src/js/profile/blocks/indicator.js +++ b/src/js/profile/blocks/indicator.js @@ -15,14 +15,15 @@ export class Indicator extends ContentBlock { restrictValues = {}, defaultFilters = [], hiddenIndicators = [], - chartColorRange + chartColorRange, + ccLicenseEnabled = false ) { super(parent, container, indicator, title, isLast, geography, hiddenIndicators); this.chartAttribution = chartAttribution; this._chart = null; this.prepareDomElements(); - this.addIndicatorChart(addLockButton, restrictValues, defaultFilters, chartColorRange); + this.addIndicatorChart(addLockButton, restrictValues, defaultFilters, chartColorRange, ccLicenseEnabled); } get previouslySelectedFilters() { @@ -60,13 +61,13 @@ export class Indicator extends ContentBlock { super.prepareDomElements(); } - addIndicatorChart(addLockButton, restrictValues, defaultFilters, chartColorRange) { + addIndicatorChart(addLockButton, restrictValues, defaultFilters, chartColorRange, ccLicenseEnabled) { let groups = Object.keys(this.indicator.groups); const configuration = this.indicator.chartConfiguration; let chartData = this.orderChartData(); - let c = new Chart(this, configuration, chartData, groups, this.container, this.title, this.chartAttribution, addLockButton, restrictValues, defaultFilters, chartColorRange); + let c = new Chart(this, configuration, chartData, groups, this.container, this.title, this.chartAttribution, addLockButton, restrictValues, defaultFilters, chartColorRange, ccLicenseEnabled); this.bubbleEvents(c, [ 'profile.chart.saveAsPng', 'profile.chart.valueTypeChanged', 'profile.chart.download_csv', 'profile.chart.download_excel', 'profile.chart.download_json', 'profile.chart.download_kml', diff --git a/src/js/profile/category.js b/src/js/profile/category.js index cb8eb159b..f995870d9 100644 --- a/src/js/profile/category.js +++ b/src/js/profile/category.js @@ -30,7 +30,8 @@ export class Category extends Component { restrictValues = {}, defaultFilters = [], hiddenIndicators = [], - chartColorRange + chartColorRange, + ccLicenseEnabled = false ) { super(parent); @@ -49,7 +50,7 @@ export class Category extends Component { this.prepareDomElements(); this.prepareEvents(); - this.addCategory(category, detail, isFirst, addLockButton, restrictValues, defaultFilters, hiddenIndicators, chartColorRange); + this.addCategory(category, detail, isFirst, addLockButton, restrictValues, defaultFilters, hiddenIndicators, chartColorRange, ccLicenseEnabled); } get filteredIndicators() { @@ -107,7 +108,7 @@ export class Category extends Component { }); } - addCategory = (category, detail, isFirst, addLockButton, restrictValues, defaultFilters, hiddenIndicators, chartColorRange) => { + addCategory = (category, detail, isFirst, addLockButton, restrictValues, defaultFilters, hiddenIndicators, chartColorRange, ccLicenseEnabled) => { const newCategorySection = categoryTemplate.cloneNode(true); const sectionHeader = $('.category-header')[0].cloneNode(true); const indicatorHeader = $('.sub-category-header')[0].cloneNode(true); @@ -130,7 +131,7 @@ export class Category extends Component { $(newCategorySection).addClass('page-break-before'); } - this.loadSubcategories(newCategorySection, detail, addLockButton, restrictValues, defaultFilters, hiddenIndicators, chartColorRange); + this.loadSubcategories(newCategorySection, detail, addLockButton, restrictValues, defaultFilters, hiddenIndicators, chartColorRange, ccLicenseEnabled); this.uiElements.push(newCategorySection); @@ -144,7 +145,7 @@ export class Category extends Component { return sectionLink; } - loadSubcategories = (wrapper, detail, addLockButton, restrictValues, defaultFilters, hiddenIndicators, chartColorRange) => { + loadSubcategories = (wrapper, detail, addLockButton, restrictValues, defaultFilters, hiddenIndicators, chartColorRange, ccLicenseEnabled) => { let isFirst = true; for (const subcategoryDetail of sortBy(detail.subcategories, "order")) { @@ -162,7 +163,8 @@ export class Category extends Component { restrictValues, defaultFilters, hiddenIndicators, - chartColorRange + chartColorRange, + ccLicenseEnabled ); sc.isVisible = sc.indicators.filter( ind => ind.isVisible diff --git a/src/js/profile/chart.js b/src/js/profile/chart.js index 9dd9d143c..644493911 100644 --- a/src/js/profile/chart.js +++ b/src/js/profile/chart.js @@ -16,6 +16,7 @@ import {DataFilterModel} from "../models/data_filter_model"; import {SidePanels} from "../elements/side_panels"; import {configureGroupedBarchart, configureGroupedBarchartDownload} from "./charts/groupedBarChart"; import {isEmpty} from "vega-lite"; +import {ConfirmationModal} from "../ui_components/confirmation_modal"; const PERCENTAGE_TYPE = "percentage"; const VALUE_TYPE = "value"; @@ -44,7 +45,9 @@ export class Chart extends Component { addLockButton = true, restrictValues = {}, defaultFilters = [], - chartColorRange) { + chartColorRange, + ccLicenseEnabled = false + ) { //we need the subindicators and groups too even though we have detail parameter. they are used for the default chart data super(parent); @@ -77,6 +80,10 @@ export class Chart extends Component { addLockButton: addLockButton }); + this.ccLicenseEnabled = ccLicenseEnabled; + + this.confirmationModal = new ConfirmationModal(this, ConfirmationModal.COOKIE_NAMES.CC_LICENSE); + this.updateConfig(chartColorRange); this.addChart(restrictValues, defaultFilters, true, false, null); @@ -454,21 +461,34 @@ export class Chart extends Component { $(this.containerParent).find('.hover-menu__content_list--last a').each(function () { $(this).off('click'); $(this).on('click', () => { - let exportType = $(this).data('id'); - const downloadFn = { - 'csv': self.exportAsCsv, - 'excel': self.exportAsExcel, - 'json': self.exportAsJson, - 'kml': self.exportAsKml, - }; - self.triggerEvent(`profile.chart.download_${exportType}`, self); - - let fileName = self.getChartTitle('-'); - downloadFn[exportType](fileName); + if (self.ccLicenseEnabled) { + self.confirmationModal.askForConfirmation() + .then((payload) => { + if (payload.confirmed) { + self.initDownload(self, this); + } + }) + } else { + self.initDownload(self, this); + } }) }); }; + initDownload(self, chart) { + let exportType = $(chart).data('id'); + const downloadFn = { + 'csv': self.exportAsCsv, + 'excel': self.exportAsExcel, + 'json': self.exportAsJson, + 'kml': self.exportAsKml, + }; + self.triggerEvent(`profile.chart.download_${exportType}`, self); + + let fileName = self.getChartTitle('-'); + downloadFn[exportType](fileName); + } + selectedGraphValueTypeChanged = (containerParent, selectedDisplayType) => { this.graphValueType = selectedDisplayType; this.triggerEvent("profile.chart.valueTypeChanged", this); diff --git a/src/js/profile/profile_loader.js b/src/js/profile/profile_loader.js index 2ff0101d5..93b94175d 100644 --- a/src/js/profile/profile_loader.js +++ b/src/js/profile/profile_loader.js @@ -18,7 +18,7 @@ let profileWrapper = null; export default class ProfileLoader extends Component { - constructor(parent, formattingConfig, _api, _profileId, _config, watermarkEnabled, siteWideFiltersEnabled, restrictValues, defaultFilters, chartColorRange) { + constructor(parent, formattingConfig, _api, _profileId, _config, watermarkEnabled, siteWideFiltersEnabled, restrictValues, defaultFilters, chartColorRange, ccLicenseEnabled) { super(parent); this.api = _api; this.profileId = _profileId; @@ -34,6 +34,7 @@ export default class ProfileLoader extends Component { this._categories = []; this._hiddenIndicators = []; this.chartColorRange = chartColorRange; + this.ccLicenseEnabled = ccLicenseEnabled; new ResizeObserver(() => { this.setWatermarkVisibility(); @@ -131,7 +132,8 @@ export default class ProfileLoader extends Component { this.restrictValues, this.defaultFilters, this.hiddenIndicators, - this.chartColorRange + this.chartColorRange, + this.ccLicenseEnabled ); if (c.subCategories.length > 0) { let navItem = this.createNavItem(id, category); diff --git a/src/js/profile/subcategory.js b/src/js/profile/subcategory.js index 5da41dcd6..7e29522e8 100644 --- a/src/js/profile/subcategory.js +++ b/src/js/profile/subcategory.js @@ -32,7 +32,8 @@ export class Subcategory extends Component { restrictValues = {}, defaultFilters = [], hiddenIndicators = [], - chartColorRange + chartColorRange, + ccLicenseEnabled = false ) { super(parent); scHeaderClone = $(subcategoryHeaderClass)[0].cloneNode(true); @@ -46,7 +47,7 @@ export class Subcategory extends Component { this._hasKeyMetrics = false; this.addSubCategoryHeaders(wrapper, subcategory, detail, isFirst); - this.addIndicators(wrapper, detail, addLockButton, restrictValues, defaultFilters, hiddenIndicators, chartColorRange); + this.addIndicators(wrapper, detail, addLockButton, restrictValues, defaultFilters, hiddenIndicators, chartColorRange,ccLicenseEnabled); this.prepareEvents(); } @@ -144,7 +145,7 @@ export class Subcategory extends Component { this._scHeader = scHeader; } - addIndicatorBlock(container, indicator, title, isLast, addLockButton, restrictValues, defaultFilters, hiddenIndicators, chartColorRange) { + addIndicatorBlock(container, indicator, title, isLast, addLockButton, restrictValues, defaultFilters, hiddenIndicators, chartColorRange,ccLicenseEnabled) { let block = new Indicator( this, container, @@ -157,7 +158,8 @@ export class Subcategory extends Component { restrictValues, defaultFilters, hiddenIndicators, - chartColorRange + chartColorRange, + ccLicenseEnabled ); this.bubbleEvents(block, [ 'profile.chart.saveAsPng', 'profile.chart.valueTypeChanged', @@ -174,7 +176,7 @@ export class Subcategory extends Component { return block; } - addIndicators = (wrapper, detail, addLockButton, restrictValues, defaultFilters, hiddenIndicators, chartColorRange) => { + addIndicators = (wrapper, detail, addLockButton, restrictValues, defaultFilters, hiddenIndicators, chartColorRange,ccLicenseEnabled) => { let index = 0; let lastIndex = Object.entries(detail.indicators).length - 1; let isEmpty = JSON.stringify(detail.indicators) === JSON.stringify({}); @@ -191,7 +193,7 @@ export class Subcategory extends Component { $(wrapper).append(indicatorContainer); let metadata = indicator.metadata; if (indicator.content_type === ContentBlock.BLOCK_TYPES.Indicator) { - block = this.addIndicatorBlock(indicatorContainer, indicator, title, isLast, addLockButton, restrictValues, defaultFilters, hiddenIndicators, chartColorRange); + block = this.addIndicatorBlock(indicatorContainer, indicator, title, isLast, addLockButton, restrictValues, defaultFilters, hiddenIndicators, chartColorRange,ccLicenseEnabled); } else if (indicator.content_type === ContentBlock.BLOCK_TYPES.HTMLBlock) { block = this.addHTMLBlock(indicatorContainer, indicator, title, isLast, hiddenIndicators); } diff --git a/src/js/ui_components/confirmation_modal.js b/src/js/ui_components/confirmation_modal.js index 3658c3609..2daa8e20e 100644 --- a/src/js/ui_components/confirmation_modal.js +++ b/src/js/ui_components/confirmation_modal.js @@ -3,7 +3,8 @@ import {Component} from "../utils"; export class ConfirmationModal extends Component { static COOKIE_NAMES = { BOUNDARY_TYPE_SELECTION: 'boundary_selection', - DATA_MAPPER_VERSION_SELECTION: 'version_selection' + DATA_MAPPER_VERSION_SELECTION: 'version_selection', + CC_LICENSE: 'cc_license' }; constructor(parent, cookieName) { @@ -12,7 +13,6 @@ export class ConfirmationModal extends Component { this._isVisible = false; this._element = $('.warning-modal:not(.global-loading-modal)'); this._cookieName = cookieName; - this.prepareEvents(); } @@ -28,6 +28,7 @@ export class ConfirmationModal extends Component { this._isVisible = value; if (value) { + this.setContent(); this.element.removeClass('hidden'); } else { this.element.addClass('hidden'); @@ -38,6 +39,29 @@ export class ConfirmationModal extends Component { return this._cookieName; } + setContent() { + const licenseContent = $('.warning-modal .warning .license__content'); + if (this.cookieName === ConfirmationModal.COOKIE_NAMES.CC_LICENSE) { + if (licenseContent.length <= 0) { + const p = document.createElement('p'); + $(p).text('Dear User, We\'re thrilled you found value in Youth Explorer\'s data! If you intend to use or reference this data in your work, we kindly request that you include proper citations to acknowledge the source. This not only respects the efforts put into compiling this information but also helps others locate and benefit from it. Thank you for your cooperation and dedication to accurate attributions!'); + + const license__content = document.createElement('div'); + $(license__content).addClass('license__content'); + $(license__content).append(p); + $('.warning-modal .warning').prepend(license__content); + } + + $('.warning-modal .warning .warning__content').addClass('hidden'); + $('.warning-modal .warning .license__content').removeClass('hidden'); + $('.warning-modal .warning .warning__header').addClass('hidden'); + } else { + $('.warning-modal .warning .warning__content').removeClass('hidden'); + $('.warning-modal .warning .license__content').addClass('hidden'); + $('.warning-modal .warning .warning__header').removeClass('hidden'); + } + } + prepareEvents() { $('.warning-modal .button-wrapper a').on('click', () => { this.isVisible = false;