+const parseMDNLinks = content =>
content.replace(new RegExp(`href="/`, 'g'), `href="https://developer.mozilla.org/`)
const FeatureExperienceBlock = ({
@@ -19,7 +20,7 @@ const FeatureExperienceBlock = ({
keys,
data,
i18nNamespace = 'features',
- units: defaultUnits = 'percentage_question',
+ units: defaultUnits = 'percentage_question'
}) => {
const [units, setUnits] = useState(defaultUnits)
@@ -36,36 +37,17 @@ const FeatureExperienceBlock = ({
// only show descriptions for english version
const description = locale.id === 'en-US' && mdn && parseMDNLinks(mdn.summary)
- const isLastYear = (year) =>
- allYears.findIndex((y) => y.year === year.year) === allYears.length - 1
-
- let headings = [{ id: 'label', label: translate('table.year') }]
- headings = headings.concat(bucketKeys)
-
- const generateRows = (data) => {
- const rows = []
- data.forEach((row) => {
- const newRow = []
- newRow.push({ id: 'label', label: row.year })
- row?.buckets?.forEach((bucket) =>
- newRow.push({ id: bucket.id, label: `${bucket.percentage}% (${bucket.count})` })
- )
- rows.push(newRow)
- })
- return rows
- }
-
- const tables = [
- {
- headings: headings,
- rows: generateRows(allYears),
- },
- ]
+ const isLastYear = year => allYears.findIndex(y => y.year === year.year) === allYears.length - 1
return (
y.year)
+ })
+ ]}
legends={bucketKeys}
title={name}
units={units}
@@ -76,27 +58,29 @@ const FeatureExperienceBlock = ({
title: name,
titleLink: mdnLink,
description,
- enableDescriptionMarkdown: false,
+ enableDescriptionMarkdown: false
}}
>
- {allYears.map((year) => (
+ {allYears.map(year => (
{year.year}
-
-
-
+
+
+
+
+
))}
@@ -111,14 +95,18 @@ const Row = styled.div`
margin-bottom: ${spacing()};
`
-const RowYear = styled.h4`
+const RowYear = styled.dt`
+ font-weight: bold;
+ margin: 0;
+`
+const RowChart = styled.dd`
margin: 0;
`
FeatureExperienceBlock.propTypes = {
block: PropTypes.shape({
id: PropTypes.string.isRequired,
- path: PropTypes.string.isRequired,
+ path: PropTypes.string.isRequired
}).isRequired,
data: PropTypes.shape({
id: PropTypes.string.isRequired,
@@ -129,7 +117,7 @@ FeatureExperienceBlock.propTypes = {
year: PropTypes.number.isRequired,
completion: PropTypes.shape({
count: PropTypes.number.isRequired,
- percentage: PropTypes.number.isRequired,
+ percentage: PropTypes.number.isRequired
}).isRequired,
buckets: PropTypes.arrayOf(
PropTypes.shape({
@@ -140,16 +128,16 @@ FeatureExperienceBlock.propTypes = {
PropTypes.shape({
id: PropTypes.string.isRequired,
count: PropTypes.number.isRequired,
- percentage: PropTypes.number.isRequired,
+ percentage: PropTypes.number.isRequired
})
- ).isRequired,
- }),
+ ).isRequired
+ })
})
- ).isRequired,
+ ).isRequired
})
- ).isRequired,
- }).isRequired,
- }).isRequired,
+ ).isRequired
+ }).isRequired
+ }).isRequired
}
export default FeatureExperienceBlock
diff --git a/results/src/core/blocks/features/FeaturesOverviewBlock.js b/results/src/core/blocks/features/FeaturesOverviewBlock.js
index bbfabba13..89c60305a 100644
--- a/results/src/core/blocks/features/FeaturesOverviewBlock.js
+++ b/results/src/core/blocks/features/FeaturesOverviewBlock.js
@@ -7,68 +7,99 @@ import { useI18n } from 'core/i18n/i18nContext'
import { useEntities } from 'core/entities/entitiesContext'
import ChartContainer from 'core/charts/ChartContainer'
import variables from 'Config/variables.yml'
+import round from 'lodash/round'
+import sortBy from 'lodash/sortBy'
+import { getTableData } from 'core/helpers/datatables'
+import BlockUnitsSelector from 'core/blocks/block/BlockUnitsSelector'
+import { useLegends } from 'core/helpers/useBucketKeys'
+
+const modes = ['grouped', 'awareness_rank', 'usage_rank' /*'usage_ratio_rank'*/]
+
+const getNodeData = (feature, index) => {
+ const buckets = get(feature, 'experience.year.facets.0.buckets')
+
+ if (!buckets) {
+ throw new Error(`Feature “${feature.id}” does not have any data associated.`)
+ }
+
+ let usageBucket = buckets.find(b => b.id === 'used')
+ if (!usageBucket) {
+ usageBucket = { count: 0 }
+ }
+
+ let knowNotUsedBucket = buckets.find(b => b.id === 'heard')
+ if (!knowNotUsedBucket) {
+ knowNotUsedBucket = { count: 0 }
+ }
+
+ const usage = usageBucket.count
+ const awareness = usage + knowNotUsedBucket.count
+
+ return {
+ index,
+ id: feature.id,
+ awareness,
+ usage,
+ unused_count: knowNotUsedBucket.count,
+ usage_ratio: round((usage / awareness) * 100, 1),
+ name: feature.name
+ }
+}
+
+const addRanks = features => {
+ const rankedByUsage = sortBy(features, 'usage').reverse()
+ const rankedByAwareness = sortBy(features, 'awareness').reverse()
+ const rankedByUsageRatio = sortBy(features, 'usage_ratio').reverse()
+ const featuresWithRanks = features.map(f => ({
+ ...f,
+ usage_rank: rankedByUsage.findIndex(ff => ff.id === f.id),
+ awareness_rank: rankedByAwareness.findIndex(ff => ff.id === f.id),
+ usage_ratio_rank: rankedByUsageRatio.findIndex(ff => ff.id === f.id)
+ }))
+ return featuresWithRanks
+}
const getChartData = (data, getName, translate) => {
- const categories = variables.featuresCategories
+ const categories = variables.featuresCategories
const sectionIds = Object.keys(categories)
- const sections = sectionIds.map((sectionId) => {
+ const allNodes = data.map((feature, index) => getNodeData(feature, index))
+ const allNodesWithRanks = addRanks(allNodes)
+ const sections = sectionIds.map(sectionId => {
const sectionFeatures = categories[sectionId]
- let features = data.filter((f) => sectionFeatures.includes(f.id))
- features = features.map((feature, index) => {
-
- const buckets = get(feature, 'experience.year.facets.0.buckets')
-
- if (!buckets) {
- throw new Error(`Feature “${feature.id}” does not have any data associated.`)
- }
-
- let usageBucket = buckets.find((b) => b.id === 'used')
- if (!usageBucket) {
- usageBucket = { count: 0 }
- }
-
- let knowNotUsedBucket = buckets.find((b) => b.id === 'heard')
- if (!knowNotUsedBucket) {
- knowNotUsedBucket = { count: 0 }
- }
-
- return {
- index,
- id: feature.id,
- awareness: usageBucket.count + knowNotUsedBucket.count,
- usage: usageBucket.count,
- unusedCount: knowNotUsedBucket.count,
- name: feature.name,
- sectionId,
- }
- })
+ let features = allNodesWithRanks.filter(f => sectionFeatures.includes(f.id))
return features.length
? {
id: sectionId,
isSection: true,
- children: features,
- name: translate(`sections.${sectionId}.title`),
+ children: features.map(f => ({ ...f, sectionId })),
+ name: translate(`sections.${sectionId}.title`)
}
: null
})
return {
id: 'root',
- children: compact(sections),
+ children: compact(sections)
}
}
const FeaturesOverviewBlock = ({ block, data, triggerId }) => {
const { getName } = useEntities()
const { translate } = useI18n()
-
- const chartData = useMemo(() => getChartData(data, getName, translate), [
- data,
- getName,
- translate,
- ])
+ const chartData = useMemo(
+ () => getChartData(data, getName, translate),
+ [data, getName, translate]
+ )
+ const categories = chartData.children
+ const legends = useLegends(
+ block,
+ categories.map(c => c.id),
+ 'features_categories'
+ )
+
+ const [mode, setMode] = useState(modes[0])
const controlledCurrent = triggerId
@@ -77,56 +108,32 @@ const FeaturesOverviewBlock = ({ block, data, triggerId }) => {
const chartClassName = controlledCurrent
? `FeaturesOverviewChart--${controlledCurrent.join('_')}`
: ''
-
- const tables = [];
-
- const generateRows = (data) => {
- const rows = [];
- data.forEach((row) => {
- rows.push([{
- id: 'tech',
- label: row.name,
- }, {
- id: 'awareness',
- label: row.awareness,
- }, {
- id: 'usage',
- label: row.usage,
- }, {
- id: 'ratio',
- label: `${parseInt((row.usage / row.awareness) * 10000) / 100}%`,
- }])
- })
- return rows;
- };
-
- chartData.children.forEach((feature) => {
- tables.push({
- id: feature.id,
- title: feature.name,
- headings: [
- {id:'tech', label: translate('tools.technology')},
- {id:'awareness', label: translate('options.experience_ranking.awareness')},
- {id:'usage', label: translate('options.experience_ranking.usage')},
- {id:'ratio', label: translate('options.features_simplified.usage_ratio')}
- ],
- rows: generateRows(feature.children),
- })
- });
return (
+ getTableData({
+ title: category.name,
+ data: sortBy(category.children, 'usage_ratio')
+ .reverse()
+ .map(f => ({ ...f, id: f.name })),
+ valueKeys: ['awareness', 'usage', 'usage_ratio']
+ })
+ )}
+ modeProps={{
+ units: mode,
+ options: modes,
+ onChange: setMode,
+ i18nNamespace: 'options.features_mode'
+ }}
>
{
data={chartData}
variant="allFeatures"
current={controlledCurrent}
+ mode={mode}
/>
diff --git a/results/src/core/blocks/features/KnowledgeScoreBlock.js b/results/src/core/blocks/features/KnowledgeScoreBlock.js
index 29c329c6f..31c3e1593 100644
--- a/results/src/core/blocks/features/KnowledgeScoreBlock.js
+++ b/results/src/core/blocks/features/KnowledgeScoreBlock.js
@@ -8,9 +8,26 @@ import { usePageContext } from 'core/helpers/pageContext'
import range from 'lodash/range'
import sumBy from 'lodash/sumBy'
import T from 'core/i18n/T'
+import { getTableData } from 'core/helpers/datatables'
const groupBy = 10
+const getLabel = (n) => `${n * groupBy}-${(n + 1) * groupBy}%`
+
+const getChartData = ({ data }) => {
+ return range(0, 100 / groupBy).map((n) => {
+ const selectedBuckets = data.buckets.filter(
+ (b) => b.id >= n * groupBy && b.id < (n + 1) * groupBy
+ )
+ return {
+ id: getLabel(n),
+ count: sumBy(selectedBuckets, 'count'),
+ percentage_survey: Math.round(100 * sumBy(selectedBuckets, 'percentage_survey')) / 100,
+ percentage_question: Math.round(100 * sumBy(selectedBuckets, 'percentage_question')) / 100,
+ }
+ })
+}
+
const KnowledgeScoreBlock = ({ block, data }) => {
if (!data) {
throw new Error(
@@ -30,56 +47,21 @@ const KnowledgeScoreBlock = ({ block, data }) => {
const [units, setUnits] = useState(defaultUnits)
- const { buckets, total, completion } = data
-
- const getLabel = (n) => `${n * groupBy}-${(n + 1) * groupBy}%`
+ const { total, completion } = data
const bucketKeys = range(0, 100 / groupBy).map((n) => ({
id: getLabel(n),
shortLabel: getLabel(n),
}))
- const groupedBuckets = range(0, 100 / groupBy).map((n) => {
- const selectedBuckets = buckets.filter(
- (b) => b.id >= n * groupBy && b.id < (n + 1) * groupBy
- )
- return {
- id: getLabel(n),
- count: sumBy(selectedBuckets, 'count'),
- percentage_survey: Math.round(100 * sumBy(selectedBuckets, 'percentage_survey')) / 100,
- percentage_question: Math.round(100 * sumBy(selectedBuckets, 'percentage_question')) / 100,
- }
- })
-
- const tables = [
- {
- headings: [
- { id: 'label', label:
},
- { id: 'percentage', label:
},
- { id: 'count', label:
},
- ],
- rows: groupedBuckets.map((bucket) => [
- {
- id: 'label',
- label: bucket.id,
- },
- {
- id: 'percentage',
- label: `${bucket.percentage}%`,
- },
- {
- id: 'count',
- label: bucket.count,
- },
- ]),
- },
- ]
+ const groupedBuckets = getChartData({ data })
return (
{
}),
]}
block={block}
+ completion={completion}
>
{
throw new Error(`VerticalBarBlock: Missing data for block ${block.id}.`)
}
const {
- id,
+ // id,
mode = 'relative',
defaultUnits = 'percentage_survey',
translateData,
- i18nNamespace,
- colorVariant,
+ i18nNamespace = block.id,
+ colorVariant
} = block
const context = usePageContext()
@@ -38,12 +38,14 @@ const VerticalBarBlock = ({ block, data, keys }: VerticalBarBlockProps) => {
return (
{
bucketKeys={bucketKeys}
total={total}
buckets={buckets}
- i18nNamespace={i18nNamespace || id}
+ i18nNamespace={i18nNamespace}
translateData={translateData}
mode={mode}
units={units}
@@ -77,15 +79,15 @@ VerticalBarBlock.propTypes = {
mode: PropTypes.oneOf(['absolute', 'relative']),
units: PropTypes.oneOf(['percentage', 'count']),
view: PropTypes.oneOf(['data', 'viz']),
- colorVariant: PropTypes.oneOf(['primary', 'secondary']),
+ colorVariant: PropTypes.oneOf(['primary', 'secondary'])
}).isRequired,
data: PropTypes.shape({
buckets: PropTypes.arrayOf(
PropTypes.shape({
- id: PropTypes.oneOfType([PropTypes.string, PropTypes.number]).isRequired,
+ id: PropTypes.oneOfType([PropTypes.string, PropTypes.number]).isRequired
})
- ).isRequired,
- }).isRequired,
+ ).isRequired
+ }).isRequired
}
export default memo(VerticalBarBlock)
diff --git a/results/src/core/blocks/opinions/OpinionBlock.tsx b/results/src/core/blocks/opinions/OpinionBlock.tsx
index 5bb501fb9..3c5a5dfff 100644
--- a/results/src/core/blocks/opinions/OpinionBlock.tsx
+++ b/results/src/core/blocks/opinions/OpinionBlock.tsx
@@ -11,7 +11,7 @@ import StreamChart from 'core/charts/generic/StreamChart'
import { useBucketKeys, useLegends } from 'core/helpers/useBucketKeys'
// @ts-ignore
import { useI18n } from 'core/i18n/i18nContext'
-import { TableBucketItem, getTableData } from 'core/helpers/datatables'
+import { TableBucketItem, getTableData, groupDataByYears } from 'core/helpers/datatables'
import { BlockUnits, ResultsByYear } from 'core/types'
import { isPercentage } from 'core/helpers/units'
@@ -24,48 +24,7 @@ interface OpinionBlockProps {
keys: string[]
}
-export const getBucketValue = ({
- data,
- year,
- key,
- valueKey,
-}: {
- data: ResultsByYear[]
- year: number
- key: number | string
- valueKey: BlockUnits
-}) => {
- const yearData = data.find((d) => d.year === year)
- const yearBuckets = yearData.facets[0].buckets
- const bucket = yearBuckets.find((b) => b.id === key)
- return bucket[valueKey]
-}
-export const groupDataByYears = ({
- keys,
- data,
- valueKeys,
- years,
-}: {
- years: number[]
- data: ResultsByYear
- valueKeys: BlockUnits[]
- keys: string[]
-}) => {
- return keys.map((key) => {
- const bucket: TableBucketItem = {
- id: key,
- }
- valueKeys.forEach((valueKey) => {
- bucket[valueKey] = years.map((year) => ({
- year,
- value: getBucketValue({ data, year, key, valueKey }),
- isPercentage: isPercentage(valueKey),
- }))
- })
- return bucket
- })
-}
export const OpinionBlock = ({
block,
@@ -106,14 +65,8 @@ export const OpinionBlock = ({
[data, bucketKeys]
)
- // console.log(data)
- // console.log(normalizedData)
-
const years = data.map((y) => y.year)
-
- const valueKeys: BlockUnits[] = ['percentage_survey', 'percentage_question', 'count']
-
- const tableData = groupDataByYears({ keys, data, valueKeys, years })
+ const tableData = groupDataByYears({ keys, data })
return (
{
return (
-
{sectionResources.map((resource) => {
const url = resource.url.includes('utm_source')
@@ -100,6 +95,7 @@ RecommendedResourcesBlock.propTypes = {
}
const List = styled.div`
+ margin-top: ${spacing(1)};
@media ${mq.large} {
display: grid;
grid-template-columns: auto auto;
diff --git a/results/src/core/blocks/tools/AllSectionsToolsCardinalityByUserBlock/AllSectionsToolsCardinalityByUserBlock.tsx b/results/src/core/blocks/tools/AllSectionsToolsCardinalityByUserBlock/AllSectionsToolsCardinalityByUserBlock.tsx
index ac0e15e2b..a15560547 100644
--- a/results/src/core/blocks/tools/AllSectionsToolsCardinalityByUserBlock/AllSectionsToolsCardinalityByUserBlock.tsx
+++ b/results/src/core/blocks/tools/AllSectionsToolsCardinalityByUserBlock/AllSectionsToolsCardinalityByUserBlock.tsx
@@ -7,6 +7,8 @@ import { ToolsCardinalityByUserBucket } from 'core/survey_api/tools'
// @ts-ignore
import variables from 'Config/variables.yml'
import { AllSectionsToolsCardinalityByUserChart } from './AllSectionsToolsCardinalityByUserChart'
+import { getTableData } from 'core/helpers/datatables'
+import { useI18n } from 'core/i18n/i18nContext'
const { toolsCategories } = variables
@@ -16,7 +18,7 @@ interface AllSectionsToolsCardinalityByUserBlockProps {
'SectionToolsCardinalityByUserBlock'
>
data: Record
- units?: 'percentage' | 'count'
+ units?: 'percentage_survey' | 'count'
}
const getChartData = (data: AllSectionsToolsCardinalityByUserBlockProps['data']) => {
@@ -27,9 +29,9 @@ const getChartData = (data: AllSectionsToolsCardinalityByUserBlockProps['data'])
if (sectionId in data) {
const sectionBuckets = data[sectionId]
- const normalizedBuckets = range(toolIds.length, 0).map((cardinality) => {
+ const normalizedBuckets = range(toolIds.length, 0).map(cardinality => {
const matchingBucket = sectionBuckets.find(
- (bucket) => bucket.cardinality === cardinality
+ bucket => bucket.cardinality === cardinality
)
if (matchingBucket) {
return matchingBucket
@@ -38,18 +40,18 @@ const getChartData = (data: AllSectionsToolsCardinalityByUserBlockProps['data'])
return {
cardinality,
count: 0,
- percentage: 0,
+ percentage_survey: 0
}
})
return {
sectionId,
- data: normalizedBuckets,
+ data: normalizedBuckets
}
} else {
return {
sectionId,
- data: [],
+ data: []
}
}
}) as {
@@ -61,29 +63,39 @@ const getChartData = (data: AllSectionsToolsCardinalityByUserBlockProps['data'])
export const AllSectionsToolsCardinalityByUserBlock = ({
block,
data,
- units: defaultUnits = 'percentage',
+ defaultUnits = 'percentage_survey'
}: AllSectionsToolsCardinalityByUserBlockProps) => {
- const [units, setUnits] = useState(defaultUnits)
+ // const [units, setUnits] = useState(defaultUnits)
+ const units = defaultUnits
+ const { getString } = useI18n()
- const charData = useMemo(() => getChartData(data), [data])
+ const chartData = useMemo(() => getChartData(data), [data])
const maxNumberOfTools = maxBy(Object.values(toolsCategories), 'length')!.length
+ console.log(chartData)
return (
+ getTableData({
+ title: getString(`sections.${sectionId}.title`)?.t,
+ data: data.map(item => ({ ...item, label: item.cardinality })).reverse(),
+ valueKeys: ['count', 'percentage_survey']
+ })
+ )}
data={data}
>
diff --git a/results/src/core/blocks/tools/AllSectionsToolsCardinalityByUserBlock/AllSectionsToolsCardinalityByUserChart.tsx b/results/src/core/blocks/tools/AllSectionsToolsCardinalityByUserBlock/AllSectionsToolsCardinalityByUserChart.tsx
index 59f76ada3..bf1e1d1d8 100644
--- a/results/src/core/blocks/tools/AllSectionsToolsCardinalityByUserBlock/AllSectionsToolsCardinalityByUserChart.tsx
+++ b/results/src/core/blocks/tools/AllSectionsToolsCardinalityByUserBlock/AllSectionsToolsCardinalityByUserChart.tsx
@@ -14,7 +14,7 @@ export const AllSectionsToolsCardinalityByUserChart = ({
sectionId: string
data: ToolsCardinalityByUserBucket[]
}[]
- units: 'percentage' | 'count'
+ units: 'percentage_survey' | 'count'
maxNumberOfTools: number
}) => (
@@ -45,7 +45,7 @@ const GridContainer = styled.div`
}
@media ${mq.large} {
- grid-template-columns: repeat(4, minmax(120px, 1fr));
+ grid-template-columns: repeat(3, minmax(120px, 1fr));
row-gap: ${spacing(1.5)};
column-gap: ${spacing(1.5)};
}
diff --git a/results/src/core/blocks/tools/AllSectionsToolsCardinalityByUserBlock/SectionItem.tsx b/results/src/core/blocks/tools/AllSectionsToolsCardinalityByUserBlock/SectionItem.tsx
index bb8c19f67..4a8f2bd67 100644
--- a/results/src/core/blocks/tools/AllSectionsToolsCardinalityByUserBlock/SectionItem.tsx
+++ b/results/src/core/blocks/tools/AllSectionsToolsCardinalityByUserBlock/SectionItem.tsx
@@ -19,7 +19,7 @@ export const SectionItem = memo(
}: {
sectionId: string
data: ToolsCardinalityByUserBucket[]
- units: 'percentage' | 'count'
+ units: 'percentage_survey' | 'count'
maxNumberOfTools: number
}) => {
const { translate } = useI18n()
@@ -29,7 +29,7 @@ export const SectionItem = memo(
return (bucket: ToolsCardinalityByUserBucket) => {
return units === 'count'
? formatter(bucket.count)
- : `${formatter(bucket.percentage)}%`
+ : `${formatter(bucket.percentage_survey)}%`
}
}, [units])
@@ -55,7 +55,7 @@ export const SectionItem = memo(
{isMax && {translate('blocks.cardinality.max')}}
diff --git a/results/src/core/blocks/tools/ToolExperienceBlock.tsx b/results/src/core/blocks/tools/ToolExperienceBlock.tsx
index 2e104e971..543fa2cbb 100644
--- a/results/src/core/blocks/tools/ToolExperienceBlock.tsx
+++ b/results/src/core/blocks/tools/ToolExperienceBlock.tsx
@@ -13,6 +13,7 @@ import { usePageContext } from 'core/helpers/pageContext'
import { ExperienceByYearBarChart } from 'core/charts/generic/ExperienceByYearBarChart'
// @ts-ignore
import { useI18n } from 'core/i18n/i18nContext'
+import { groupDataByYears, getTableData } from 'core/helpers/datatables'
const BAR_THICKNESS = 28
const BAR_SPACING = 16
@@ -20,45 +21,25 @@ const BAR_SPACING = 16
interface ToolExperienceBlockProps {
block: BlockContext<'toolExperienceTemplate', 'ToolExperienceBlock', { toolIds: string }>
data: ToolAllYearsExperience
+ keys: string[]
units?: 'percentage_survey' | 'percentage_question' | 'count'
closeComponent: any
}
-export const ToolExperienceBlock = ({
- block,
- data,
- units: defaultUnits = 'percentage_question',
- closeComponent,
-}: ToolExperienceBlockProps) => {
-
- const context = usePageContext()
- const { locale } = context
- const { translate } = useI18n()
-
- const [units, setUnits] = useState(defaultUnits)
-
-
- const title = data.entity.name
- const titleLink = data.entity.homepage
-
- // as descriptions are extracted from best of js/github...
- // we only have english available.
- const description = locale.id === 'en-US' && data.entity.description!
-
- const bucketKeys = useBucketKeys('tools')
+const getChartData = ({ data, bucketKeys }) => {
const allYears = data.experience.all_years
- const normalizedData = useMemo(
+ return useMemo(
() =>
allYears.map((yearExperience, index) => {
const yearData: ToolExperienceBucket[] = bucketKeys.map((key: { id: string }) => {
const matchingBucket = yearExperience.facets[0].buckets.find(
- (bucket) => bucket.id === key.id
+ bucket => bucket.id === key.id
)
return (
matchingBucket || {
id: key.id,
count: 0,
- percentage: 0,
+ percentage: 0
}
)
})
@@ -68,52 +49,62 @@ export const ToolExperienceBlock = ({
return {
year: yearExperience.year,
...keyBy(yearData, 'id'),
- thickness: isLastYear ? 2 : 1,
+ thickness: isLastYear ? 2 : 1
}
}),
[data, bucketKeys]
)
+}
- if (allYears.length === 0) {
- return no data
- }
+export const ToolExperienceBlock = ({
+ block,
+ keys,
+ data,
+ units: defaultUnits = 'percentage_question',
+ closeComponent
+}: ToolExperienceBlockProps) => {
+ const context = usePageContext()
+ const { locale } = context
+ const { translate } = useI18n()
- const chartHeight = (allYears.length - 1) * (BAR_THICKNESS + BAR_SPACING) + BAR_THICKNESS * 2
+ const [units, setUnits] = useState(defaultUnits)
+
+ const title = data.entity.name
+ const titleLink = data.entity.homepage
- let headings = [{ id: 'label', label: translate('table.year') }]
- headings = headings.concat(bucketKeys)
+ // as descriptions are extracted from best of js/github...
+ // we only have english available.
+ const description = locale.id === 'en-US' && data.entity.description
- const generateRows = (data) => {
- const rows = []
- data.forEach((row) => {
- const newRow = []
- newRow.push({ id: 'label', label: row.year })
- row.facets[0].buckets.forEach((bucket) =>
- newRow.push({ id: bucket.id, label: `${bucket.percentage}% (${bucket.count})` })
- )
- rows.push(newRow)
- })
+ const bucketKeys = useBucketKeys('tools')
+ const allYears = data.experience.all_years
+ const completion = allYears[allYears.length - 1]?.completion
- return rows
+ const normalizedData = getChartData({ data, bucketKeys })
+
+ if (allYears.length === 0) {
+ return no data
}
- const tables = [
- {
- headings: headings,
- rows: generateRows(allYears),
- },
- ]
+ const chartHeight = (allYears.length - 1) * (BAR_THICKNESS + BAR_SPACING) + BAR_THICKNESS * 2
return (
y.year),
+ translateData: true,
+ i18nNamespace: 'tools'
+ })
+ ]}
+ completion={completion}
>
- useMemo(() => {
- let data: ToolsExperienceMarimekkoToolData[] = rawData.map((tool) => {
- const keyedBuckets = keyBy(get(tool, 'experience.year.facets.0.buckets'), 'id')
+const getChartData = ({
+ data,
+ makeAbsolute = false
+}: {
+ data: ToolsExperienceToolData[]
+ makeAbsolute?: boolean
+}): ToolsExperienceMarimekkoToolData[] => {
+ let chartData: ToolsExperienceMarimekkoToolData[] = data.map(tool => {
+ const keyedBuckets = keyBy(get(tool, 'experience.year.facets.0.buckets'), 'id')
- const total = get(tool, 'experience.year.completion.total')
- const aware = total - keyedBuckets.never_heard.count
+ const total = get(tool, 'experience.year.completion.total')
+ const aware = total - keyedBuckets.never_heard.count
- return {
- id: tool.id,
- tool: { ...tool.entity, id: tool.id },
- awareness: aware,
- would_not_use: (keyedBuckets.would_not_use.count / aware) * 100 * -1,
- not_interested: (keyedBuckets.not_interested.count / aware) * 100 * -1,
- interested: (keyedBuckets.interested.count / aware) * 100,
- would_use: (keyedBuckets.would_use.count / aware) * 100,
- }
- })
+ const coeff = makeAbsolute ? 1 : -1
- // tools with the most positive experience come first,
- // interested users and users willing to use it again
- data = sortBy(data, (datum) => datum.interested + datum.would_use)
- data.reverse()
+ return {
+ id: tool.id,
+ entity: tool.entity,
+ tool: { ...tool.entity, id: tool.id },
+ awareness: aware,
+ would_not_use_percentage: round(
+ (keyedBuckets.would_not_use.count / aware) * 100 * coeff,
+ 1
+ ),
+ not_interested_percentage: round(
+ (keyedBuckets.not_interested.count / aware) * 100 * coeff,
+ 1
+ ),
+ interested_percentage: round((keyedBuckets.interested.count / aware) * 100, 1),
+ would_use_percentage: round((keyedBuckets.would_use.count / aware) * 100, 1)
+ }
+ })
+
+ // tools with the most positive experience come first,
+ // interested users and users willing to use it again
+ chartData = sortBy(chartData, datum => datum.interested_percentage + datum.would_use_percentage)
+ chartData.reverse()
- return data
- }, [rawData])
+ return chartData
+}
interface ToolsExperienceMarimekkoBlockProps {
index: number
@@ -64,9 +86,10 @@ interface ToolsExperienceMarimekkoBlockProps {
export const ToolsExperienceMarimekkoBlock = ({
block,
data,
- triggerId = null,
+ keys,
+ triggerId = null
}: ToolsExperienceMarimekkoBlockProps) => {
- const normalizedData = useNormalizedData(data)
+ const normalizedData = getChartData({ data })
// make the height relative to the number of tools
// in order to try to get consistent sizes across
@@ -74,47 +97,21 @@ export const ToolsExperienceMarimekkoBlock = ({
// fewer tools would appear to have a better awareness
// than those with more.
const height = MARGIN.top + ROW_HEIGHT * data.length + MARGIN.bottom
-
const controlledCurrent = triggerId
-
- const { translate } = useI18n()
-
- const headings = [{id: 'label', label: translate('tools.technology')}];
- data[0].experience.year.facets[0].buckets.forEach((bucket) => {
- headings.push({id: bucket.id, label: translate(`options.tools.${bucket.id}.short`)});
- })
-
- const getRows = (data) => {
- const rows = [];
- data.forEach((row) => {
- const newRow = [{id: 'label', label: row.entity.name}];
- row.experience.year.facets[0].buckets.forEach((bucket) => {
- newRow.push({
- id: bucket.id,
- label: `${bucket.percentage}% (${bucket.count})`,
- })
- })
- rows.push(newRow);
- });
- return rows;
- }
-
- const tables = [{
- headings: headings,
- rows: getRows(data),
- }];
-
return (
diff --git a/results/src/core/blocks/tools/ToolsExperienceMarimekkoBlock/ToolsExperienceMarimekkoChart.tsx b/results/src/core/blocks/tools/ToolsExperienceMarimekkoBlock/ToolsExperienceMarimekkoChart.tsx
index f1e3247d7..2979edb34 100644
--- a/results/src/core/blocks/tools/ToolsExperienceMarimekkoBlock/ToolsExperienceMarimekkoChart.tsx
+++ b/results/src/core/blocks/tools/ToolsExperienceMarimekkoBlock/ToolsExperienceMarimekkoChart.tsx
@@ -16,15 +16,10 @@ export const MARGIN = {
top: 30,
right: 10,
bottom: 120,
- left: 160,
+ left: 160
}
export const ROW_HEIGHT = 40
-/**
- * Create a map of tool experience keys for easier access.
- */
-const experienceKeys = keyBy(keys.tools.keys, 'id')
-
/**
* In order to create a diverging chart (<– negative | positive –>),
* we have to use negative and positive values, that's why we're using
@@ -38,7 +33,7 @@ const valueFormatter = ((value: number) => `${Math.abs(Math.round(value))}%`) as
const ShadowsLayer = ({ data }: CustomLayerProps) => {
return (
- {data.map((datum) => (
+ {data.map(datum => (
) => {
return (
- {data.map((datum) => (
+ {data.map(datum => (
@@ -78,38 +73,21 @@ export const ToolsExperienceMarimekkoChart = (props: ToolsExperienceMarimekkoCha
const { translate } = useI18n()
const { current } = props
-
+
// `id` is the label while `value` is the accessor
// for a given dimension.
- const dimensions = useMemo(
- () => [
- {
- id: translate(experienceKeys.not_interested.label),
- value: experienceKeys.not_interested.id,
- },
- {
- id: translate(experienceKeys.would_not_use.label),
- value: experienceKeys.would_not_use.id,
- },
- {
- id: translate(experienceKeys.would_use.label),
- value: experienceKeys.would_use.id,
- },
- {
- id: translate(experienceKeys.interested.label),
- value: experienceKeys.interested.id,
- },
- ],
- [translate]
- )
+ const dimensions = keys.tools.keys.map(k => ({
+ id: translate(k.label),
+ originalId: k.id,
+ value: `${k.id}_percentage`
+ }))
const theme = useTheme()
-
const getLayerColor = (props: any) => {
- const dimension = dimensions.find((d) => d.id === props.id)
+ const dimension = dimensions.find(d => d.id === props.id)
if (dimension) {
- const color = theme.colors.ranges.tools[dimension.value]
+ const color = theme.colors.ranges.tools[dimension.originalId]
if (current !== null && current !== props.datum.id) {
return `${color}33`
}
@@ -123,10 +101,10 @@ export const ToolsExperienceMarimekkoChart = (props: ToolsExperienceMarimekkoCha
margin={MARGIN}
axisTop={{
- format: valueFormatter,
+ format: valueFormatter
}}
axisBottom={{
- format: valueFormatter,
+ format: valueFormatter
}}
id="id"
value="awareness"
@@ -157,7 +135,7 @@ export const ToolsExperienceMarimekkoChart = (props: ToolsExperienceMarimekkoCha
'axes',
// ShadowsLayer,
ToolsLabels,
- 'bars',
+ 'bars'
]}
/>
)
diff --git a/results/src/core/blocks/tools/ToolsExperienceRankingBlock.tsx b/results/src/core/blocks/tools/ToolsExperienceRankingBlock.tsx
index b711e097e..5eb60ede0 100644
--- a/results/src/core/blocks/tools/ToolsExperienceRankingBlock.tsx
+++ b/results/src/core/blocks/tools/ToolsExperienceRankingBlock.tsx
@@ -14,6 +14,7 @@ import { Entity } from 'core/types'
import T from 'core/i18n/T'
// @ts-ignore
import { useI18n } from 'core/i18n/i18nContext'
+import { getTableData } from 'core/helpers/datatables'
type MetricId = 'satisfaction' | 'interest' | 'usage' | 'awareness'
type ViewId = 'viz' | 'data'
@@ -28,7 +29,7 @@ interface SwitcherProps {
const Switcher = ({ setMetric, metric }: SwitcherProps) => {
return (
- {ALL_METRICS.map((key) => (
+ {ALL_METRICS.map(key => (
diff --git a/results/src/core/blocks/tools/ToolsScatterplotBlock.js b/results/src/core/blocks/tools/ToolsScatterplotBlock.js
index 8436525e7..0f2170c5f 100644
--- a/results/src/core/blocks/tools/ToolsScatterplotBlock.js
+++ b/results/src/core/blocks/tools/ToolsScatterplotBlock.js
@@ -4,7 +4,6 @@ import Block from 'core/blocks/block/BlockVariant'
import compact from 'lodash/compact'
import round from 'lodash/round'
import get from 'lodash/get'
-import { keys } from 'core/bucket_keys'
import ToolsScatterplotChart from 'core/charts/tools/ToolsScatterplotChart'
import { useI18n } from 'core/i18n/i18nContext'
import ChartContainer from 'core/charts/ChartContainer'
@@ -12,68 +11,77 @@ import ButtonGroup from 'core/components/ButtonGroup'
import Button from 'core/components/Button'
import variables from 'Config/variables.yml'
import T from 'core/i18n/T'
+import { getTableData } from 'core/helpers/datatables'
const { toolsCategories } = variables
+const getNodeData = (tool, metric = 'satisfaction') => {
+ const { id, entity, experience } = tool
+ const name = entity.name
+ const buckets = get(experience, 'year.facets[0].buckets')
+ const total = get(experience, 'year.completion.total')
+
+ // if tool doesn't have experience data, abort
+ if (!buckets) {
+ return null
+ }
+
+ // get count for a given bucket
+ const getCount = id => {
+ return buckets && buckets.find(b => b.id === id).count
+ }
+
+ const totals = {
+ // calculate satisfaction ratio against usage
+ satisfaction: getCount('would_use') + getCount('would_not_use'),
+ // calculate interest ratio against awareness
+ interest: getCount('interested') + getCount('not_interested'),
+ // calculate interest ratio against total
+ awareness: total
+ }
+
+ const getPercentage = id => {
+ return round((getCount(id) / totals[metric]) * 100, 2)
+ }
+
+ const percentages = {
+ satisfaction: getPercentage('would_use'),
+ interest: getPercentage('interested'),
+ awareness: 100 - getPercentage('never_heard')
+ }
+
+ // note: we use the same x for all metrics to stay consistent
+ const node = {
+ id,
+ originalId: id,
+ usage_count: totals['satisfaction'],
+ satisfaction_percentage: percentages['satisfaction'],
+ interest_percentage: percentages['interest'],
+ x: totals['satisfaction'],
+ y: percentages[metric],
+ name,
+ label: name
+ }
+
+ return node
+}
+
/*
Parse data and convert it into a format compatible with the Scatterplot chart
*/
-const useChartData = (data, translate, metric = 'satisfaction') => {
+const getChartData = (data, translate, metric = 'satisfaction') => {
const theme = useTheme()
const allTools = Object.keys(toolsCategories)
- .filter((c) => !c.includes('abridged'))
- .map((categoryId) => {
+ .filter(c => !c.includes('abridged'))
+ .map(categoryId => {
const toolsIds = toolsCategories[categoryId]
- const categoryTools = data.filter((tool) => toolsIds.includes(tool.id))
+ const categoryTools = data.filter(tool => toolsIds.includes(tool.id))
const categoryData =
- categoryTools &&
- categoryTools.map((tool) => {
- const { id, entity, experience } = tool
- const name = entity.name
- const buckets = get(experience, 'year.buckets')
- const total = get(experience, 'year.total')
-
- // if tool doesn't have experience data, abort
- if (!buckets) {
- return null
- }
-
- // get count for a given bucket
- const getCount = (id) => {
- return buckets && buckets.find((b) => b.id === id).count
- }
-
- const totals = {
- satisfaction: getCount('would_use') + getCount('would_not_use'),
- interest: getCount('interested') + getCount('not_interested'),
- awareness: total,
- }
-
- const getPercentage = (id) => {
- return round((getCount(id) / totals[metric]) * 100, 2)
- }
-
- const percentages = {
- satisfaction: getPercentage('would_use'),
- interest: getPercentage('interested'),
- awareness: 100 - getPercentage('never_heard'),
- }
-
- // note: we use the same x for all metrics to stay consistent
- const node = {
- id,
- originalId: id,
- x: totals['satisfaction'],
- y: percentages[metric],
- name,
- }
-
- return node
- })
+ categoryTools && categoryTools.map(tool => getNodeData(tool, metric))
const color = theme.colors.ranges.toolSections[categoryId]
@@ -82,7 +90,7 @@ const useChartData = (data, translate, metric = 'satisfaction') => {
id: categoryId,
name: translate(`page.${categoryId}`),
color,
- data: compact(categoryData),
+ data: compact(categoryData)
}
: null
})
@@ -92,7 +100,7 @@ const useChartData = (data, translate, metric = 'satisfaction') => {
const Switcher = ({ setMetric, metric }) => {
return (
- {['satisfaction', 'interest'].map((key) => (
+ {['satisfaction', 'interest'].map(key => (