Skip to content
Merged
Show file tree
Hide file tree
Changes from 6 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions src/components/edit/earthEngine/PeriodSelect.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ import { SelectField } from '../../core/index.js'
import styles from './styles/PeriodSelect.module.css'

const isValidDate = (d) => {
return d instanceof Date && !isNaN(d)
return d instanceof Date && !Number.isNaN(d)
}
const normalizeToDayBefore2359 = (date) => {
const d = new Date(date)
Expand Down Expand Up @@ -80,7 +80,7 @@ const EarthEnginePeriodSelect = ({
let name = e.name
if (name.includes(AVAILABLE_UP_TO)) {
const regex = new RegExp(`\\s*\\(${AVAILABLE_UP_TO}.*\\)$`)
name = name.replace(regex, '')
name = name.replaceAll(regex, '')
}
onChange({
...e,
Expand Down
8 changes: 4 additions & 4 deletions src/components/edit/thematic/RadiusSelect.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -17,8 +17,8 @@ export const isValidRadius = (
radiusLow = THEMATIC_RADIUS_LOW,
radiusHigh = THEMATIC_RADIUS_HIGH
) =>
!isNaN(radiusLow) &&
!isNaN(radiusHigh) &&
!Number.isNaN(radiusLow) &&
!Number.isNaN(radiusHigh) &&
radiusLow <= radiusHigh &&
radiusLow >= THEMATIC_RADIUS_MIN &&
radiusHigh <= THEMATIC_RADIUS_MAX
Expand All @@ -33,15 +33,15 @@ const RadiusSelect = ({
<Fragment>
<NumberField
label={i18n.t('Low radius')}
value={isNaN(radiusLow) ? '' : radiusLow}
value={Number.isNaN(radiusLow) ? '' : radiusLow}
min={THEMATIC_RADIUS_MIN}
max={THEMATIC_RADIUS_MAX}
onChange={setRadiusLow}
className={className}
/>
<NumberField
label={i18n.t('High radius')}
value={isNaN(radiusHigh) ? '' : radiusHigh}
value={Number.isNaN(radiusHigh) ? '' : radiusHigh}
min={THEMATIC_RADIUS_MIN}
max={THEMATIC_RADIUS_MAX}
onChange={setRadiusHigh}
Expand Down
2 changes: 1 addition & 1 deletion src/components/layers/LayerCard.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ const LayerCard = ({
className={cx(styles.card, {
[styles.expanded]: isExpanded,
})}
data-test={`card-${title.replace(/ /g, '')}`}
data-test={`card-${title.replaceAll(' ', '')}`}
>
<Card dataTest={isOverlay ? 'layercard' : 'basemapcard'}>
<div className={styles.cardHeader}>
Expand Down
4 changes: 3 additions & 1 deletion src/components/layers/overlays/Layer.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,9 @@ import styles from './styles/Layer.module.css'
const Layer = ({ layer, onClick }) => {
const { img, type, name } = layer
const label = name || i18n.t(type)
const dataTest = `addlayeritem-${label.toLowerCase().replace(/\s/g, '_')}`
const dataTest = `addlayeritem-${label
.toLowerCase()
.replaceAll(/\s/g, '_')}`

return (
<div
Expand Down
29 changes: 18 additions & 11 deletions src/loaders/thematicLoader.js
Original file line number Diff line number Diff line change
Expand Up @@ -164,16 +164,21 @@ const thematicLoader = async ({
}

let legendItems = []
let valueFormat

if (!isSingleColor) {
legendItems = legendSet
? getPredefinedLegendItems(legendSet)
: getAutomaticLegendItems(
orderedValues,
method,
classes,
colorScale
)
if (legendSet) {
legendItems = getPredefinedLegendItems(legendSet)
} else {
const classification = getAutomaticLegendItems({
data: orderedValues,
method,
classes,
colorScale,
})
legendItems = classification.items
valueFormat = classification.valueFormat
}
}

const legend = {
Expand Down Expand Up @@ -222,13 +227,15 @@ const thematicLoader = async ({
const getLegendItem = (value) =>
getLegendItemForValue({
value,
valueFormat,
legendItems: legend.items.filter((item) => !item.noData),
clamp: !legendSet,
clamp: method !== CLASSIFICATION_PREDEFINED,
})

if (legendSet && Array.isArray(legend.items) && legend.items.length >= 2) {
minValue = legend.items[0].startValue
maxValue = legend.items[legend.items.length - 1].endValue
const regularItems = legend.items.filter((item) => !item.noData)
minValue = regularItems[0].startValue
maxValue = regularItems.at(-1).endValue
}

const getRadiusForValue = scaleSqrt()
Expand Down
36 changes: 30 additions & 6 deletions src/util/__tests__/classify.spec.js
Original file line number Diff line number Diff line change
Expand Up @@ -65,13 +65,28 @@ describe('getLegendItemForValue', () => {
getLegendItemForValue({ value: -1, legendItems })
).toBeUndefined()
})

it('applies valueFormat to value before lookup', () => {
// 9.999 formatted to 2 decimals → 10.00, which falls in the second bin [10, 20)
expect(
getLegendItemForValue({
value: 9.999,
valueFormat: (v) => Number(v.toFixed(2)),
legendItems,
})
).toEqual(legendItems[1])
})
})

describe('getLegendItems', () => {
it('returns equal intervals for CLASSIFICATION_EQUAL_INTERVALS', () => {
const values = [0, 100]
const result = getLegendItems(values, CLASSIFICATION_EQUAL_INTERVALS, 4)
expect(result).toEqual([
const { items } = getLegendItems(
values,
CLASSIFICATION_EQUAL_INTERVALS,
4
)
expect(items).toEqual([
{ startValue: 0.0, endValue: 25.0 },
{ startValue: 25.0, endValue: 50.0 },
{ startValue: 50.0, endValue: 75.0 },
Expand All @@ -81,16 +96,25 @@ describe('getLegendItems', () => {

it('returns quantiles for CLASSIFICATION_EQUAL_COUNTS', () => {
const values = [1, 2, 3, 4, 5, 6]
const result = getLegendItems(values, CLASSIFICATION_EQUAL_COUNTS, 3)
expect(result).toEqual([
const { items } = getLegendItems(values, CLASSIFICATION_EQUAL_COUNTS, 3)
expect(items).toEqual([
{ startValue: 1.0, endValue: 3.0 },
{ startValue: 3.0, endValue: 5.0 },
{ startValue: 5.0, endValue: 6.0 },
])
})

it('returns undefined if method is unknown', () => {
const result = getLegendItems([0, 100], 'UNKNOWN', 3)
expect(result).toBeUndefined()
const { items } = getLegendItems([0, 100], 'UNKNOWN', 3)
expect(items).toBeUndefined()
})

it('returns a valueFormat function for known methods', () => {
const { valueFormat } = getLegendItems(
[0, 100],
CLASSIFICATION_EQUAL_INTERVALS,
4
)
expect(typeof valueFormat).toBe('function')
})
})
89 changes: 78 additions & 11 deletions src/util/__tests__/legend.spec.js
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import {
import { defaultClasses, defaultColorScale } from '../colors.js'
import {
loadDataItemLegendSet,
sortLegendItems,
formatLegendItems,
getBinsFromLegendItems,
getColorScaleFromLegendItems,
Expand All @@ -15,6 +16,61 @@ import {
getRenderingLabel,
} from '../legend.js'

describe('sortLegendItems', () => {
it('sorts items by startValue descending', () => {
const items = [
{ startValue: 20, endValue: 30 },
{ startValue: 0, endValue: 10 },
{ startValue: 10, endValue: 20 },
]
expect(sortLegendItems(items).map((i) => i.startValue)).toEqual([
20, 10, 0,
])
})

it('sorts items with from/to keys descending', () => {
const items = [
{ from: 5, to: 10 },
{ from: 0, to: 5 },
{ from: 10, to: 15 },
]
expect(sortLegendItems(items).map((i) => i.from)).toEqual([10, 5, 0])
})

it('places items without range keys at the end', () => {
const items = [
{ startValue: 10, endValue: 20 },
{ name: 'Other', color: 'grey' },
{ startValue: 0, endValue: 10 },
]
const sorted = sortLegendItems(items)
expect(sorted[0].startValue).toBe(10)
expect(sorted[1].startValue).toBe(0)
expect(sorted[2].name).toBe('Other')
})

it('does not mutate the original array', () => {
const items = [
{ startValue: 10, endValue: 20 },
{ startValue: 0, endValue: 10 },
]
const copy = [...items]
sortLegendItems(items)
expect(items).toEqual(copy)
})

it('sorts by endValue descending when startValues are equal', () => {
const items = [
{ startValue: 0, endValue: 10 },
{ startValue: 0, endValue: 20 },
{ startValue: 0, endValue: 15 },
]
expect(sortLegendItems(items).map((i) => i.endValue)).toEqual([
20, 15, 10,
])
})
})

describe('legend utils', () => {
describe('loadDataItemLegendSet', () => {
it('returns null when no dataItem provided', async () => {
Expand Down Expand Up @@ -96,12 +152,12 @@ describe('legend utils', () => {
describe('getAutomaticLegendItems', () => {
it('returns items with colors from default color scale', () => {
const data = [1, 2, 3, 4, 5]
const items = getAutomaticLegendItems(
const { items } = getAutomaticLegendItems({
data,
CLASSIFICATION_EQUAL_INTERVALS,
defaultClasses,
defaultColorScale
)
method: CLASSIFICATION_EQUAL_INTERVALS,
classes: defaultClasses,
colorScale: defaultColorScale,
})
expect(items.length).toBeGreaterThan(0)
// each item should have a color from the provided colorScale
items.forEach((item, idx) => {
Expand All @@ -110,14 +166,25 @@ describe('legend utils', () => {
})

it('returns empty array when no data', () => {
const items = getAutomaticLegendItems(
[],
CLASSIFICATION_EQUAL_INTERVALS,
defaultClasses,
defaultColorScale
)
const { items } = getAutomaticLegendItems({
data: [],
method: CLASSIFICATION_EQUAL_INTERVALS,
classes: defaultClasses,
colorScale: defaultColorScale,
})
expect(items).toEqual([])
})

it('returns a valueFormat function alongside items', () => {
const { items, valueFormat } = getAutomaticLegendItems({
data: [0, 50, 100],
method: CLASSIFICATION_EQUAL_INTERVALS,
classes: 3,
colorScale: defaultColorScale,
})
expect(items.length).toBe(3)
expect(typeof valueFormat).toBe('function')
})
})

describe('getRenderingLabel', () => {
Expand Down
Loading
Loading