Skip to content
Open
Show file tree
Hide file tree
Changes from all 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