diff --git a/Gemfile.lock b/Gemfile.lock index 8bcf899582..7168d0aceb 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -239,7 +239,7 @@ GEM net-ssh (>= 2.6.5, < 7.0.0) net-ssh (6.1.0) nio4r (2.5.8) - nokogiri (1.13.8) + nokogiri (1.13.9) mini_portile2 (~> 2.8.0) racc (~> 1.4) oj (3.6.8) diff --git a/app/controllers/api/v1/ndcs_controller.rb b/app/controllers/api/v1/ndcs_controller.rb index 07be8a4105..9342e17478 100644 --- a/app/controllers/api/v1/ndcs_controller.rb +++ b/app/controllers/api/v1/ndcs_controller.rb @@ -140,7 +140,7 @@ def sectors_cache_key end def index_json - # params[:source] -> one of ["CAIT", "LTS", "WB", "NDC Explorer", "Pledges"] + # params[:source] -> one of ["CAIT", "LTS", "WB", "NDC Explorer", "Pledges", "UNICEF"] if params[:source].present? source = ::Indc::Source.where(name: params[:source]) end diff --git a/app/javascript/app/components/abbr-replace/abbr-replace-data.js b/app/javascript/app/components/abbr-replace/abbr-replace-data.js index f01bf76f83..d86a2d15cf 100644 --- a/app/javascript/app/components/abbr-replace/abbr-replace-data.js +++ b/app/javascript/app/components/abbr-replace/abbr-replace-data.js @@ -13,6 +13,7 @@ export const CONFLICTS = { // E.g. UNFCCC should go before UN. export const ABBREVIATIONS = { + ACE: 'Action for Climate Empowerment', AILAC: 'Asociación Independiente de Latinoamérica y el Caribe (Independent Association of Latin America and the Caribbean)', AR2: "IPCC's Second Assessment Report in 1995", diff --git a/app/javascript/app/components/ndcs/ndcs-country-accordion/ndcs-country-accordion-actions.js b/app/javascript/app/components/ndcs/ndcs-country-accordion/ndcs-country-accordion-actions.js index 77632f3a50..4c71de3df7 100644 --- a/app/javascript/app/components/ndcs/ndcs-country-accordion/ndcs-country-accordion-actions.js +++ b/app/javascript/app/components/ndcs/ndcs-country-accordion/ndcs-country-accordion-actions.js @@ -24,7 +24,7 @@ const fetchNdcsCountryAccordion = createThunkAction( }?location=${locations}&category=${category}${ lts ? '&source=LTS' - : '&source[]=CAIT&source[]=WB&source[]=NDC%20Explorer' + : '&source[]=CAIT&source[]=WB&source[]=NDC%20Explorer&source[]=UNICEF' }${documentParam}${!compare ? '&filter=overview' : ''}` ) .then(response => { diff --git a/app/javascript/app/components/ndcs/ndcs-explore-map/ndcs-explore-map-component.jsx b/app/javascript/app/components/ndcs/ndcs-explore-map/ndcs-explore-map-component.jsx index 27a99f9aa7..41e181eea5 100644 --- a/app/javascript/app/components/ndcs/ndcs-explore-map/ndcs-explore-map-component.jsx +++ b/app/javascript/app/components/ndcs/ndcs-explore-map/ndcs-explore-map-component.jsx @@ -102,6 +102,11 @@ const renderLegend = (legendData, emissionsCardData, isPNG) => ( emissionsCardData.data && getHoverIndex(emissionsCardData, l) } + infoText={ + emissionsCardData && + emissionsCardData.tooltip && + emissionsCardData.tooltip[l.name] + } name={l.name} number={l.partiesNumber} value={l.value} diff --git a/app/javascript/app/components/ndcs/ndcs-explore-map/ndcs-explore-map-selectors.js b/app/javascript/app/components/ndcs/ndcs-explore-map/ndcs-explore-map-selectors.js index 99b0e79f32..bfc62ea42a 100644 --- a/app/javascript/app/components/ndcs/ndcs-explore-map/ndcs-explore-map-selectors.js +++ b/app/javascript/app/components/ndcs/ndcs-explore-map/ndcs-explore-map-selectors.js @@ -27,6 +27,17 @@ import { getSubmitted2020Isos } from 'utils/indicatorCalculations'; const NOT_APPLICABLE_LABEL = 'Not Applicable'; +const getColorException = (indicator, label) => { + if (indicator.value !== 'child_sensitive_NDC') return null; + // Child sensitive NDC indicator label colors + return { + 'Category A': '#53AF5C', + 'Category B': '#8EC593', + 'Category C': '#C8DAC9', + 'No new or updated NDC submitted': '#757584' + }[label.name]; +}; + const getSearch = state => state.search || null; const getCountries = state => state.countries || null; const getSectors = state => state.sectors || null; @@ -256,8 +267,10 @@ export const getPathsWithStyles = createSelector( } }; if (countryData && countryData.label_id) { - const legendIndex = legendBuckets[countryData.label_id].index; - const color = getColorByIndex(legendBuckets, legendIndex); + const label = legendBuckets[countryData.label_id]; + const color = + getColorException(indicator, label) || + getColorByIndex(legendBuckets, label.index); style.default.fill = color; style.hover.fill = color; } @@ -312,7 +325,9 @@ export const getLegend = createSelector( ...label, value: percentage(partiesNumber, maximumCountries), partiesNumber, - color: getColorByIndex(indicator.legendBuckets, label.index) + color: + getColorException(indicator, label) || + getColorByIndex(indicator.legendBuckets, label.index) }); }); return legendItems.sort(sortByIndexAndNotInfo); @@ -352,6 +367,15 @@ export const getEmissionsCardData = createSelector( getIndicatorEmissionsData(emissionsIndicator, selectedIndicator, legend), 'value' ); + // Info tooltip only available for this indicator + const tooltip = selectedIndicator.value === 'child_sensitive_NDC' && { + 'Category A': + 'The NDC is Category A because it meets 4 of 4 criteria for child sensitivity', + 'Category B': + 'The NDC is Category B because it meets 3 of 4 criteria for child sensitivity', + 'Category C': + 'The NDC is Category C because it meets 0-2 of 4 criteria for child sensitivity' + }; const config = { animation: true, @@ -370,7 +394,8 @@ export const getEmissionsCardData = createSelector( return { config, - data + data, + tooltip }; } ); diff --git a/app/javascript/app/components/ndcs/ndcs-explore-map/ndcs-explore-map-styles.scss b/app/javascript/app/components/ndcs/ndcs-explore-map/ndcs-explore-map-styles.scss index 1ff5ae62b3..89dcc0d8f2 100644 --- a/app/javascript/app/components/ndcs/ndcs-explore-map/ndcs-explore-map-styles.scss +++ b/app/javascript/app/components/ndcs/ndcs-explore-map/ndcs-explore-map-styles.scss @@ -183,6 +183,7 @@ height: 10px; display: inline-block; margin-right: 5px; + min-width: 10px; width: 10px; border-radius: 50%; } diff --git a/app/javascript/app/components/ndcs/ndcs-explore-map/ndcs-explore-map.js b/app/javascript/app/components/ndcs/ndcs-explore-map/ndcs-explore-map.js index e74f4ad287..0c22bd67c2 100644 --- a/app/javascript/app/components/ndcs/ndcs-explore-map/ndcs-explore-map.js +++ b/app/javascript/app/components/ndcs/ndcs-explore-map/ndcs-explore-map.js @@ -175,7 +175,14 @@ class NDCSExploreMapContainer extends PureComponent { this.props.setModalMetadata({ customTitle: 'Explore NDCs', category: 'NDCS Explore Map', - slugs: ['ndc_cw', 'ndc_wb', 'ndc_die', 'ndc_adaptation'], + slugs: [ + 'ndc_cw', + 'ndc_wb', + 'ndc_die', + 'ndc_adaptation', + 'ndc_unicef', + 'ndc_finance' + ], open: true }); }; diff --git a/app/javascript/app/components/ndcs/shared/legend-item/legend-item-component.jsx b/app/javascript/app/components/ndcs/shared/legend-item/legend-item-component.jsx index b50a1c33be..0752805630 100644 --- a/app/javascript/app/components/ndcs/shared/legend-item/legend-item-component.jsx +++ b/app/javascript/app/components/ndcs/shared/legend-item/legend-item-component.jsx @@ -1,6 +1,9 @@ import React from 'react'; +import ReactTooltip from 'react-tooltip'; import Progress from 'components/progress'; import AbbrReplace from 'components/abbr-replace'; +import infoIcon from 'assets/icons/info.svg'; +import Icon from 'components/icon'; import PropTypes from 'prop-types'; import styles from './legend-item-styles.scss'; @@ -12,7 +15,8 @@ const LegendItem = ({ itemsName, hoverIndex, selectActiveDonutIndex, - disableAbbr + disableAbbr, + infoText }) => (
{disableAbbr ? name : {name}} + {infoText && ( +
+ + +
+ )}
@@ -39,7 +53,8 @@ LegendItem.propTypes = { hoverIndex: PropTypes.number.isRequired, color: PropTypes.string, selectActiveDonutIndex: PropTypes.func.isRequired, - disableAbbr: PropTypes.bool + disableAbbr: PropTypes.bool, + infoText: PropTypes.string }; LegendItem.defaultProps = { diff --git a/app/javascript/app/components/ndcs/shared/legend-item/legend-item-styles.scss b/app/javascript/app/components/ndcs/shared/legend-item/legend-item-styles.scss index 5c803ad42e..ecd52a4b2e 100644 --- a/app/javascript/app/components/ndcs/shared/legend-item/legend-item-styles.scss +++ b/app/javascript/app/components/ndcs/shared/legend-item/legend-item-styles.scss @@ -10,15 +10,22 @@ $progress-bar-width: 180px; } .legendName { + display: flex; + align-items: center; max-width: $progress-bar-width; margin-bottom: 2px; } + .infoContainer { + margin: 0 0 -3px 5px; + } + .legendDot { height: 10px; display: inline-block; margin-right: 5px; width: 10px; + min-width: 10px; border-radius: 50%; } diff --git a/app/javascript/app/data/constants.js b/app/javascript/app/data/constants.js index 6e1d751cf7..4235c8da2f 100644 --- a/app/javascript/app/data/constants.js +++ b/app/javascript/app/data/constants.js @@ -384,8 +384,8 @@ export const AGRICULTURE_INDICATORS_MAP_BUCKETS = { export const DEFAULT_NDC_EXPLORE_CATEGORY_SLUG = 'unfccc_process'; export const CATEGORY_SOURCES = { - NDC_EXPLORE: ['CAIT', 'WB', 'NDC Explorer'], - NDC_CONTENT: ['CAIT', 'WB', 'NDC Explorer'] + NDC_EXPLORE: ['CAIT', 'WB', 'NDC Explorer', 'UNICEF'], + NDC_CONTENT: ['CAIT', 'WB', 'NDC Explorer', 'UNICEF'] }; export const ENHANCEMENT_CATEGORIES = [ diff --git a/app/javascript/app/data/data-explorer-constants.js b/app/javascript/app/data/data-explorer-constants.js index 390dc2eeaf..0cced99868 100644 --- a/app/javascript/app/data/data-explorer-constants.js +++ b/app/javascript/app/data/data-explorer-constants.js @@ -154,7 +154,14 @@ export const DATA_EXPLORER_METHODOLOGY_SOURCE = { GCP: ['historical_emissions_gcp'] }, 'ndc-sdg-linkages': ['ndc_sdg_all indicators'], - 'ndc-content': ['ndc_cw', 'ndc_wb', 'ndc_die', 'ndc_adaptation'], + 'ndc-content': [ + 'ndc_cw', + 'ndc_wb', + 'ndc_die', + 'ndc_adaptation', + 'ndc_unicef', + 'ndc_finance' + ], 'lts-content': ['lts'], 'net-zero-content': ['net_zero'], 'emission-pathways': [null] // model, scenario and indicator related metadata diff --git a/app/javascript/app/providers/ndcs-explore-provider/ndcs-explore-provider-actions.js b/app/javascript/app/providers/ndcs-explore-provider/ndcs-explore-provider-actions.js index d8cb12c959..fb430174b8 100644 --- a/app/javascript/app/providers/ndcs-explore-provider/ndcs-explore-provider-actions.js +++ b/app/javascript/app/providers/ndcs-explore-provider/ndcs-explore-provider-actions.js @@ -22,7 +22,7 @@ const fetchNDCS = createThunkAction('fetchNDCS', props => (dispatch, state) => { if (ndcsExplore && !ndcsExplore.loading) { promises.push( apiWithCache.get( - `/api/v1/ndcs?filter=map&source[]=CAIT&source[]=WB&source[]=NDC%20Explorer'${ + `/api/v1/ndcs?filter=map&source[]=CAIT&source[]=WB&source[]=NDC%20Explorer&source[]=UNICEF${ params.length ? `&${params.join('&')}` : '' }` ) @@ -33,7 +33,7 @@ const fetchNDCS = createThunkAction('fetchNDCS', props => (dispatch, state) => { // and as it is not filtered by category also serves the whole list of categories promises.push( apiWithCache.get( - '/api/v1/ndcs?indicators=ndce_ghg, submission, submission_date&filter=map&source[]=CAIT&source[]=WB&source[]=NDC%20Explorer' + '/api/v1/ndcs?indicators=ndce_ghg, submission, submission_date&filter=map&source[]=CAIT&source[]=WB&source[]=NDC%20Explorer&source[]=UNICEF' ) ); diff --git a/app/javascript/app/providers/ndcs-provider/ndcs-provider-actions.js b/app/javascript/app/providers/ndcs-provider/ndcs-provider-actions.js index 96db10dff3..074181fd8d 100644 --- a/app/javascript/app/providers/ndcs-provider/ndcs-provider-actions.js +++ b/app/javascript/app/providers/ndcs-provider/ndcs-provider-actions.js @@ -23,7 +23,9 @@ const fetchNDCS = createThunkAction('fetchNDCS', props => (dispatch, state) => { params.push(`indicators=${indicatorSlugs.join(',')}`); } if (!overrideFilter) { - params.push('filter=map&source[]=CAIT&source[]=WB&source[]=NDC%20Explorer'); + params.push( + 'filter=map&source[]=CAIT&source[]=WB&source[]=NDC%20Explorer&source[]=UNICEF' + ); } if (subcategory) { params.push(`subcategory=${subcategory}`); @@ -54,7 +56,7 @@ const fetchNDCS = createThunkAction('fetchNDCS', props => (dispatch, state) => { `/api/v1/ndcs?indicators=${additionalIndicatorSlugs.join(',')}${ overrideFilter ? '' - : '&filter=map&source[]=CAIT&source[]=WB&source[]=NDC%20Explorer' + : '&filter=map&source[]=CAIT&source[]=WB&source[]=NDC%20Explorer&source[]=UNICEF' }` ) ); diff --git a/app/models/indc/source.rb b/app/models/indc/source.rb index a4463bd104..ffc2e9a8e7 100644 --- a/app/models/indc/source.rb +++ b/app/models/indc/source.rb @@ -9,8 +9,23 @@ # module Indc class Source < ApplicationRecord - validates :name, presence: true - validates :name, uniqueness: true + VALID_SOURCES = [ + 'CAIT', + 'LSE', + 'LTS', + 'NDC Explorer', + 'Net_Zero', + 'Pledges', + 'UNICEF', + 'WB' + ].freeze + + validates :name, presence: true, uniqueness: true, inclusion: { + in: VALID_SOURCES, + message: "%{value} is not supported. Supported values for source: #{VALID_SOURCES.join(', ')}", + allow_blank: true # to have only presence error if nil + } + scope :lts, -> { where("name ilike 'LTS'") } scope :net_zero, -> { where("name ilike 'Net_Zero'") } scope :ndc, -> { where.not(id: lts).where.not(id: net_zero) } diff --git a/app/services/import_indc.rb b/app/services/import_indc.rb index c1f3db8c0b..57da6838f7 100644 --- a/app/services/import_indc.rb +++ b/app/services/import_indc.rb @@ -442,7 +442,8 @@ def import_labels def import_values_ndc valid_sources = [@sources_index['CAIT'], @sources_index['NDC Explorer'], - @sources_index['WB'], @sources_index['Net_Zero']] + @sources_index['WB'], @sources_index['Net_Zero'], + @sources_index['UNICEF']] values = [] Indc::Indicator. where(source: valid_sources). diff --git a/spec/factories/indc_sources.rb b/spec/factories/indc_sources.rb index 181be0f520..8338069c9e 100644 --- a/spec/factories/indc_sources.rb +++ b/spec/factories/indc_sources.rb @@ -9,6 +9,8 @@ # FactoryBot.define do factory :indc_source, class: 'Indc::Source' do - sequence(:name) { |n| 'My Name ' + ('AA'..'ZZ').to_a[n] } + name { 'CAIT' } + + initialize_with { Indc::Source.find_or_create_by(name: name) } end end diff --git a/spec/models/indc/source_spec.rb b/spec/models/indc/source_spec.rb index 17c9dab006..ac49888fa3 100644 --- a/spec/models/indc/source_spec.rb +++ b/spec/models/indc/source_spec.rb @@ -10,9 +10,17 @@ require 'rails_helper' describe Indc::Source, type: :model do + subject { FactoryBot.build(:indc_source) } + + it { is_expected.to be_valid } + it 'should be invalid when name not present' do - expect( - FactoryBot.build(:indc_source, name: nil) - ).to have(1).errors_on(:name) + subject.name = nil + expect(subject).to have(1).errors_on(:name) + end + + it 'should be invalid with wrong name' do + subject.name = 'source' + expect(subject).to have(1).errors_on(:name) end end