@defra/interactive-map 0.0.47-alpha → 0.0.48-fmp-1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/package.json +1 -1
- package/plugins/datasets/dist/esm/im-datasets-plugin.js +1 -1
- package/plugins/datasets/dist/umd/im-datasets-plugin.js +1 -1
- package/plugins/datasets/src/registry/datasetRegistry.js +6 -6
- package/plugins/draw/dist/esm/im-draw-ol-adapter.js +1 -1
- package/plugins/draw/dist/umd/im-draw-ol-adapter.js +1 -1
- package/plugins/draw/src/adapters/openlayers/snap/snapEngine.js +8 -1
- package/plugins/draw/src/adapters/openlayers/snap/snapEngine.test.js +17 -0
- package/plugins/map-key/dist/esm/im-map-key-plugin.js +1 -1
- package/plugins/map-key/dist/umd/im-map-key-plugin.js +1 -1
- package/plugins/map-key/dist/umd/index.js +1 -1
- package/plugins/map-key/src/api/addKeyItem.js +3 -0
- package/plugins/map-key/src/api/addKeyItem.test.js +14 -0
- package/plugins/map-key/src/api/index.js +7 -0
- package/plugins/map-key/src/api/index.test.js +13 -0
- package/plugins/map-key/src/api/removeKeyItem.js +3 -0
- package/plugins/map-key/src/api/removeKeyItem.test.js +14 -0
- package/plugins/map-key/src/components/Key/MapKey.jsx +11 -3
- package/plugins/map-key/src/initialise/MapKeyInit.jsx +17 -3
- package/plugins/map-key/src/initialise/MapKeyInit.test.jsx +43 -7
- package/plugins/map-key/src/manifest.js +5 -1
- package/plugins/map-key/src/reducers/mergeKeyGroupItems.js +68 -0
- package/plugins/map-key/src/reducers/mergeKeyGroupItems.test.js +91 -0
- package/plugins/map-key/src/reducers/pluginState.js +68 -0
- package/plugins/map-key/src/reducers/pluginState.test.js +82 -0
- package/plugins/map-key/src/utils/mergeKeyGroupItems.js +0 -12
- package/plugins/map-key/src/utils/mergeKeyGroupItems.test.js +0 -53
|
@@ -1,8 +1,11 @@
|
|
|
1
1
|
import { MapKey } from './components/Key/MapKey.jsx'
|
|
2
2
|
import { MapKeyInit } from './initialise/MapKeyInit.jsx'
|
|
3
|
+
import { initialState, actions } from './reducers/pluginState.js'
|
|
4
|
+
import { api } from './api/index.js'
|
|
3
5
|
|
|
4
6
|
export const manifest = {
|
|
5
7
|
InitComponent: MapKeyInit,
|
|
8
|
+
reducer: { initialState, actions },
|
|
6
9
|
panels: [
|
|
7
10
|
{
|
|
8
11
|
id: 'mapKey',
|
|
@@ -26,5 +29,6 @@ export const manifest = {
|
|
|
26
29
|
icons: [{
|
|
27
30
|
id: 'key',
|
|
28
31
|
svgContent: '<path d="M3 5h.01"/><path d="M3 12h.01"/><path d="M3 19h.01"/><path d="M8 5h13"/><path d="M8 12h13"/><path d="M8 19h13"/>'
|
|
29
|
-
}]
|
|
32
|
+
}],
|
|
33
|
+
api
|
|
30
34
|
}
|
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
let _pluginStateRef = {}
|
|
2
|
+
export const attachPluginStateRef = (pluginStateRef) => { _pluginStateRef = pluginStateRef }
|
|
3
|
+
|
|
4
|
+
let previousRef = null
|
|
5
|
+
let addedKeyGroups = []
|
|
6
|
+
|
|
7
|
+
const getStateKeyGroups = () => {
|
|
8
|
+
if (!_pluginStateRef?.current) {
|
|
9
|
+
return []
|
|
10
|
+
}
|
|
11
|
+
if (previousRef === _pluginStateRef.current) {
|
|
12
|
+
return addedKeyGroups
|
|
13
|
+
}
|
|
14
|
+
previousRef = _pluginStateRef.current
|
|
15
|
+
const { groups = [], keyDefinitions = [] } = _pluginStateRef.current
|
|
16
|
+
|
|
17
|
+
addedKeyGroups = groups.map(group => {
|
|
18
|
+
if (group.type === 'flat') {
|
|
19
|
+
return {
|
|
20
|
+
...group,
|
|
21
|
+
keyDefinition: keyDefinitions.find(item => item.id === group.id)
|
|
22
|
+
}
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
return {
|
|
26
|
+
...group,
|
|
27
|
+
keyDefinitions: keyDefinitions.filter(item => item.groupId === group.id)
|
|
28
|
+
}
|
|
29
|
+
})
|
|
30
|
+
return addedKeyGroups
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
// Merges the state key group items with items provided by another plugin,
|
|
34
|
+
// specifically the datasets plugin
|
|
35
|
+
export const mergeKeyGroupItems = (keyGroupItemsToMerge = []) => {
|
|
36
|
+
// Clone the current state key groups from the plugin state reference
|
|
37
|
+
// to avoid mutating the original array
|
|
38
|
+
const stateKeyGroups = [...getStateKeyGroups()]
|
|
39
|
+
if (stateKeyGroups.length === 0) {
|
|
40
|
+
// if there is nothing in the state key groups, just return the items that were passed in
|
|
41
|
+
return keyGroupItemsToMerge
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
const mergedItems = keyGroupItemsToMerge.map((keyGroupItem) => {
|
|
45
|
+
const groupIndex = stateKeyGroups.findIndex((_group) => _group.id === keyGroupItem.id)
|
|
46
|
+
if (groupIndex === -1) {
|
|
47
|
+
return keyGroupItem
|
|
48
|
+
}
|
|
49
|
+
// Splice the matched group from the stateKeyGroups and merge it with the item
|
|
50
|
+
const group = stateKeyGroups.splice(groupIndex, 1)[0]
|
|
51
|
+
return {
|
|
52
|
+
...keyGroupItem,
|
|
53
|
+
...group,
|
|
54
|
+
keyDefinitions: [
|
|
55
|
+
...(group.keyDefinitions || []),
|
|
56
|
+
...(keyGroupItem.keyDefinitions || [])
|
|
57
|
+
]
|
|
58
|
+
}
|
|
59
|
+
})
|
|
60
|
+
|
|
61
|
+
// Append any remaining pluginConfigGroups that were not matched with datasetItems
|
|
62
|
+
// and filter out any groups that have no key definitions
|
|
63
|
+
const finalMergedItems = [...mergedItems, ...stateKeyGroups]
|
|
64
|
+
.filter(item =>
|
|
65
|
+
((item.type === 'flat' && item.keyDefinition) ||
|
|
66
|
+
(item.type === 'group' && item.keyDefinitions?.length)))
|
|
67
|
+
return finalMergedItems
|
|
68
|
+
}
|
|
@@ -0,0 +1,91 @@
|
|
|
1
|
+
import { attachPluginStateRef, mergeKeyGroupItems } from './mergeKeyGroupItems.js'
|
|
2
|
+
|
|
3
|
+
describe('mergeKeyGroupItems', () => {
|
|
4
|
+
afterEach(() => {
|
|
5
|
+
attachPluginStateRef({ current: null })
|
|
6
|
+
})
|
|
7
|
+
|
|
8
|
+
it('returns the incoming items unchanged when no plugin state is attached', () => {
|
|
9
|
+
attachPluginStateRef({ current: null })
|
|
10
|
+
const items = [{ id: 'dataset-1', keyDefinitions: [{ id: 'k1' }] }]
|
|
11
|
+
|
|
12
|
+
expect(mergeKeyGroupItems(items)).toBe(items)
|
|
13
|
+
})
|
|
14
|
+
|
|
15
|
+
it('returns dataset items unchanged when plugin state groups are empty', () => {
|
|
16
|
+
attachPluginStateRef({ current: { groups: [], keyDefinitions: [] } })
|
|
17
|
+
const items = [{ id: 'dataset-1', keyDefinitions: [{ id: 'k1' }] }]
|
|
18
|
+
|
|
19
|
+
expect(mergeKeyGroupItems(items)).toEqual(items)
|
|
20
|
+
})
|
|
21
|
+
|
|
22
|
+
it('uses default empty state values and empty arguments when no groups or items are supplied', () => {
|
|
23
|
+
attachPluginStateRef({ current: {} })
|
|
24
|
+
|
|
25
|
+
expect(mergeKeyGroupItems()).toEqual([])
|
|
26
|
+
})
|
|
27
|
+
|
|
28
|
+
it('merges a matched group even when the state group has no keyDefinitions array', () => {
|
|
29
|
+
attachPluginStateRef({
|
|
30
|
+
current: {
|
|
31
|
+
groups: [{ id: 'dataset-1', type: 'group', groupLabel: 'Dataset 1' }],
|
|
32
|
+
keyDefinitions: []
|
|
33
|
+
}
|
|
34
|
+
})
|
|
35
|
+
|
|
36
|
+
const items = [{ id: 'dataset-1', keyDefinitions: [{ id: 'd1' }] }]
|
|
37
|
+
|
|
38
|
+
expect(mergeKeyGroupItems(items)).toEqual([
|
|
39
|
+
{ id: 'dataset-1', type: 'group', groupLabel: 'Dataset 1', keyDefinitions: [{ id: 'd1' }] }
|
|
40
|
+
])
|
|
41
|
+
})
|
|
42
|
+
|
|
43
|
+
it('reuses the cached plugin-state groups when the ref object is unchanged', () => {
|
|
44
|
+
const ref = { current: { groups: [{ id: 'group-1', type: 'group', groupLabel: 'Group 1' }], keyDefinitions: [{ id: 'k1', groupId: 'group-1' }] } }
|
|
45
|
+
attachPluginStateRef(ref)
|
|
46
|
+
|
|
47
|
+
const first = mergeKeyGroupItems([{ id: 'dataset-1', keyDefinitions: [{ id: 'd1' }] }])
|
|
48
|
+
const second = mergeKeyGroupItems([{ id: 'dataset-1', keyDefinitions: [{ id: 'd2' }] }])
|
|
49
|
+
|
|
50
|
+
expect(first).toEqual([{ id: 'group-1', type: 'group', groupLabel: 'Group 1', keyDefinitions: [{ id: 'k1', groupId: 'group-1' }] }])
|
|
51
|
+
expect(second).toEqual(first)
|
|
52
|
+
})
|
|
53
|
+
|
|
54
|
+
it('merges a matching flat group into the dataset item when the key definition matches the same id', () => {
|
|
55
|
+
attachPluginStateRef({
|
|
56
|
+
current: {
|
|
57
|
+
groups: [{ id: 'dataset-1', type: 'flat' }],
|
|
58
|
+
keyDefinitions: [{ id: 'dataset-1' }]
|
|
59
|
+
}
|
|
60
|
+
})
|
|
61
|
+
|
|
62
|
+
const items = [{ id: 'dataset-1', keyDefinitions: [{ id: 'dataset-1' }] }]
|
|
63
|
+
|
|
64
|
+
expect(mergeKeyGroupItems(items)).toEqual([
|
|
65
|
+
{
|
|
66
|
+
id: 'dataset-1',
|
|
67
|
+
type: 'flat',
|
|
68
|
+
keyDefinition: { id: 'dataset-1' },
|
|
69
|
+
keyDefinitions: [{ id: 'dataset-1' }]
|
|
70
|
+
}
|
|
71
|
+
])
|
|
72
|
+
})
|
|
73
|
+
|
|
74
|
+
it('keeps only unmatched state groups that still have key definitions', () => {
|
|
75
|
+
attachPluginStateRef({
|
|
76
|
+
current: {
|
|
77
|
+
groups: [
|
|
78
|
+
{ id: 'group-1', type: 'group', groupLabel: 'Group 1' },
|
|
79
|
+
{ id: 'group-2', type: 'group', groupLabel: 'Group 2' }
|
|
80
|
+
],
|
|
81
|
+
keyDefinitions: [{ id: 'k1', groupId: 'group-1' }]
|
|
82
|
+
}
|
|
83
|
+
})
|
|
84
|
+
|
|
85
|
+
const items = [{ id: 'dataset-1', keyDefinitions: [{ id: 'd1' }] }]
|
|
86
|
+
|
|
87
|
+
expect(mergeKeyGroupItems(items)).toEqual([
|
|
88
|
+
{ id: 'group-1', type: 'group', groupLabel: 'Group 1', keyDefinitions: [{ id: 'k1', groupId: 'group-1' }] }
|
|
89
|
+
])
|
|
90
|
+
})
|
|
91
|
+
})
|
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
const initialState = {
|
|
2
|
+
keyDefinitions: [],
|
|
3
|
+
groups: []
|
|
4
|
+
}
|
|
5
|
+
|
|
6
|
+
const labelToId = (label) => label ? label.toLowerCase().replace(/\s+/g, '-') : null
|
|
7
|
+
const createNewGroup = (id, groupId, groupLabel) => {
|
|
8
|
+
return groupId
|
|
9
|
+
? { id: groupId, type: 'group', groupLabel }
|
|
10
|
+
: { id, type: 'flat' }
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
const findGroup = (state, id, groupId) => {
|
|
14
|
+
return state.groups.find(group => {
|
|
15
|
+
return (group.type === 'group' && group.id === groupId) ||
|
|
16
|
+
(group.type === 'flat' && group.id === id)
|
|
17
|
+
})
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
const addKeyItem = (state, keyDefinition) => {
|
|
21
|
+
const { id, groupLabel } = keyDefinition
|
|
22
|
+
const groupId = keyDefinition.groupId || labelToId(groupLabel)
|
|
23
|
+
// Check for an existingGroup
|
|
24
|
+
const existingGroup = findGroup(state, id, groupId)
|
|
25
|
+
// If no existing group is found, create a new one
|
|
26
|
+
const newGroup = existingGroup ? null : createNewGroup(id, groupId, groupLabel)
|
|
27
|
+
// Add the new group to the state if it was created
|
|
28
|
+
const groups = newGroup ? [...state.groups, newGroup] : state.groups
|
|
29
|
+
// Add the key definition to the state
|
|
30
|
+
const keyDefinitions = [...state.keyDefinitions, {
|
|
31
|
+
...keyDefinition,
|
|
32
|
+
type: 'manual',
|
|
33
|
+
...(groupId ? { groupId } : {})
|
|
34
|
+
}]
|
|
35
|
+
|
|
36
|
+
// Return the updated state with the new key definitions and groups
|
|
37
|
+
return {
|
|
38
|
+
...state,
|
|
39
|
+
keyDefinitions,
|
|
40
|
+
groups
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
const removeKeyItem = (state, keyDefinition) => {
|
|
45
|
+
const { id } = keyDefinition
|
|
46
|
+
return {
|
|
47
|
+
...state,
|
|
48
|
+
keyDefinitions: state.keyDefinitions.filter(key => key.id !== id)
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
const addKeyGroups = (state, groups) => {
|
|
53
|
+
return {
|
|
54
|
+
...state,
|
|
55
|
+
groups: [...state.groups, ...groups]
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
const actions = {
|
|
60
|
+
ADD_KEY_ITEM: addKeyItem,
|
|
61
|
+
REMOVE_KEY_ITEM: removeKeyItem,
|
|
62
|
+
ADD_KEY_GROUPS: addKeyGroups
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
export {
|
|
66
|
+
initialState,
|
|
67
|
+
actions
|
|
68
|
+
}
|
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
import { actions, initialState } from './pluginState.js'
|
|
2
|
+
|
|
3
|
+
describe('pluginState reducer helpers', () => {
|
|
4
|
+
it('returns the initial state', () => {
|
|
5
|
+
expect(initialState).toEqual({ keyDefinitions: [], groups: [] })
|
|
6
|
+
})
|
|
7
|
+
|
|
8
|
+
it('adds a flat key item and creates a flat group when needed', () => {
|
|
9
|
+
const nextState = actions.ADD_KEY_ITEM(initialState, { id: 'flat-key', label: 'Flat key' })
|
|
10
|
+
|
|
11
|
+
expect(nextState).toEqual({
|
|
12
|
+
keyDefinitions: [{ id: 'flat-key', label: 'Flat key', type: 'manual' }],
|
|
13
|
+
groups: [{ id: 'flat-key', type: 'flat' }]
|
|
14
|
+
})
|
|
15
|
+
})
|
|
16
|
+
|
|
17
|
+
it('adds a grouped key item when the label can be converted to a group id', () => {
|
|
18
|
+
const nextState = actions.ADD_KEY_ITEM(initialState, { id: 'group-key', groupLabel: 'Group Label', label: 'Group key' })
|
|
19
|
+
|
|
20
|
+
expect(nextState).toEqual({
|
|
21
|
+
keyDefinitions: [{
|
|
22
|
+
id: 'group-key',
|
|
23
|
+
groupId: 'group-label',
|
|
24
|
+
groupLabel: 'Group Label',
|
|
25
|
+
label: 'Group key',
|
|
26
|
+
type: 'manual'
|
|
27
|
+
}],
|
|
28
|
+
groups: [{ id: 'group-label', type: 'group', groupLabel: 'Group Label' }]
|
|
29
|
+
})
|
|
30
|
+
})
|
|
31
|
+
|
|
32
|
+
it('reuses an existing group when the key item matches a flat group already in state', () => {
|
|
33
|
+
const state = {
|
|
34
|
+
keyDefinitions: [{ id: 'existing-item' }],
|
|
35
|
+
groups: [{ id: 'existing-item', type: 'flat' }]
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
expect(actions.ADD_KEY_ITEM(state, { id: 'existing-item', label: 'Existing item' })).toEqual({
|
|
39
|
+
keyDefinitions: [
|
|
40
|
+
{ id: 'existing-item' },
|
|
41
|
+
{ id: 'existing-item', label: 'Existing item', type: 'manual' }
|
|
42
|
+
],
|
|
43
|
+
groups: [{ id: 'existing-item', type: 'flat' }]
|
|
44
|
+
})
|
|
45
|
+
})
|
|
46
|
+
|
|
47
|
+
it('reuses an existing grouped definition when the same group id is already present', () => {
|
|
48
|
+
const state = {
|
|
49
|
+
keyDefinitions: [{ id: 'existing-group-item', groupId: 'group-1' }],
|
|
50
|
+
groups: [{ id: 'group-1', type: 'group', groupLabel: 'Group 1' }]
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
expect(actions.ADD_KEY_ITEM(state, { id: 'new-item', groupId: 'group-1', groupLabel: 'Group 1', label: 'New item' })).toEqual({
|
|
54
|
+
keyDefinitions: [
|
|
55
|
+
{ id: 'existing-group-item', groupId: 'group-1' },
|
|
56
|
+
{ id: 'new-item', groupId: 'group-1', groupLabel: 'Group 1', label: 'New item', type: 'manual' }
|
|
57
|
+
],
|
|
58
|
+
groups: [{ id: 'group-1', type: 'group', groupLabel: 'Group 1' }]
|
|
59
|
+
})
|
|
60
|
+
})
|
|
61
|
+
|
|
62
|
+
it('removes a key definition by id', () => {
|
|
63
|
+
const state = {
|
|
64
|
+
keyDefinitions: [{ id: 'a' }, { id: 'b' }],
|
|
65
|
+
groups: [{ id: 'a', type: 'flat' }]
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
expect(actions.REMOVE_KEY_ITEM(state, { id: 'a' })).toEqual({
|
|
69
|
+
keyDefinitions: [{ id: 'b' }],
|
|
70
|
+
groups: [{ id: 'a', type: 'flat' }]
|
|
71
|
+
})
|
|
72
|
+
})
|
|
73
|
+
|
|
74
|
+
it('adds configured groups to state without mutating the source array', () => {
|
|
75
|
+
const groups = [{ id: 'g1', type: 'group', groupLabel: 'G1' }]
|
|
76
|
+
|
|
77
|
+
expect(actions.ADD_KEY_GROUPS(initialState, groups)).toEqual({
|
|
78
|
+
keyDefinitions: [],
|
|
79
|
+
groups: [{ id: 'g1', type: 'group', groupLabel: 'G1' }]
|
|
80
|
+
})
|
|
81
|
+
})
|
|
82
|
+
})
|
|
@@ -1,53 +0,0 @@
|
|
|
1
|
-
import { mergeKeyGroupItems } from './mergeKeyGroupItems'
|
|
2
|
-
|
|
3
|
-
describe('mergeKeyGroupItems', () => {
|
|
4
|
-
it('returns items unchanged when groups is not provided', () => {
|
|
5
|
-
const items = [{ id: 'a', label: 'A' }, { id: 'b', label: 'B' }]
|
|
6
|
-
|
|
7
|
-
expect(mergeKeyGroupItems(undefined, items)).toBe(items)
|
|
8
|
-
})
|
|
9
|
-
|
|
10
|
-
it('returns items unchanged when groups is null', () => {
|
|
11
|
-
const items = [{ id: 'a', label: 'A' }]
|
|
12
|
-
|
|
13
|
-
expect(mergeKeyGroupItems(null, items)).toBe(items)
|
|
14
|
-
})
|
|
15
|
-
|
|
16
|
-
it('merges matching group data into an item', () => {
|
|
17
|
-
const items = [{ id: 'a', label: 'A' }]
|
|
18
|
-
const groups = { a: { group: 'Group 1' } }
|
|
19
|
-
|
|
20
|
-
expect(mergeKeyGroupItems(groups, items)).toEqual([
|
|
21
|
-
{ id: 'a', label: 'A', group: 'Group 1' }
|
|
22
|
-
])
|
|
23
|
-
})
|
|
24
|
-
|
|
25
|
-
it('overrides item properties with matching group properties', () => {
|
|
26
|
-
const items = [{ id: 'a', label: 'A' }]
|
|
27
|
-
const groups = { a: { label: 'Overridden' } }
|
|
28
|
-
|
|
29
|
-
expect(mergeKeyGroupItems(groups, items)).toEqual([
|
|
30
|
-
{ id: 'a', label: 'Overridden' }
|
|
31
|
-
])
|
|
32
|
-
})
|
|
33
|
-
|
|
34
|
-
it('leaves items without a matching group unchanged', () => {
|
|
35
|
-
const items = [{ id: 'a', label: 'A' }, { id: 'b', label: 'B' }]
|
|
36
|
-
const groups = { a: { group: 'Group 1' } }
|
|
37
|
-
|
|
38
|
-
expect(mergeKeyGroupItems(groups, items)).toEqual([
|
|
39
|
-
{ id: 'a', label: 'A', group: 'Group 1' },
|
|
40
|
-
{ id: 'b', label: 'B' }
|
|
41
|
-
])
|
|
42
|
-
})
|
|
43
|
-
|
|
44
|
-
it('returns an empty array when items is empty', () => {
|
|
45
|
-
expect(mergeKeyGroupItems({ a: { group: 'Group 1' } }, [])).toEqual([])
|
|
46
|
-
})
|
|
47
|
-
|
|
48
|
-
it('handles an empty groups object as no matches', () => {
|
|
49
|
-
const items = [{ id: 'a', label: 'A' }]
|
|
50
|
-
|
|
51
|
-
expect(mergeKeyGroupItems({}, items)).toEqual(items)
|
|
52
|
-
})
|
|
53
|
-
})
|