@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.
Files changed (27) hide show
  1. package/package.json +1 -1
  2. package/plugins/datasets/dist/esm/im-datasets-plugin.js +1 -1
  3. package/plugins/datasets/dist/umd/im-datasets-plugin.js +1 -1
  4. package/plugins/datasets/src/registry/datasetRegistry.js +6 -6
  5. package/plugins/draw/dist/esm/im-draw-ol-adapter.js +1 -1
  6. package/plugins/draw/dist/umd/im-draw-ol-adapter.js +1 -1
  7. package/plugins/draw/src/adapters/openlayers/snap/snapEngine.js +8 -1
  8. package/plugins/draw/src/adapters/openlayers/snap/snapEngine.test.js +17 -0
  9. package/plugins/map-key/dist/esm/im-map-key-plugin.js +1 -1
  10. package/plugins/map-key/dist/umd/im-map-key-plugin.js +1 -1
  11. package/plugins/map-key/dist/umd/index.js +1 -1
  12. package/plugins/map-key/src/api/addKeyItem.js +3 -0
  13. package/plugins/map-key/src/api/addKeyItem.test.js +14 -0
  14. package/plugins/map-key/src/api/index.js +7 -0
  15. package/plugins/map-key/src/api/index.test.js +13 -0
  16. package/plugins/map-key/src/api/removeKeyItem.js +3 -0
  17. package/plugins/map-key/src/api/removeKeyItem.test.js +14 -0
  18. package/plugins/map-key/src/components/Key/MapKey.jsx +11 -3
  19. package/plugins/map-key/src/initialise/MapKeyInit.jsx +17 -3
  20. package/plugins/map-key/src/initialise/MapKeyInit.test.jsx +43 -7
  21. package/plugins/map-key/src/manifest.js +5 -1
  22. package/plugins/map-key/src/reducers/mergeKeyGroupItems.js +68 -0
  23. package/plugins/map-key/src/reducers/mergeKeyGroupItems.test.js +91 -0
  24. package/plugins/map-key/src/reducers/pluginState.js +68 -0
  25. package/plugins/map-key/src/reducers/pluginState.test.js +82 -0
  26. package/plugins/map-key/src/utils/mergeKeyGroupItems.js +0 -12
  27. 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,12 +0,0 @@
1
- export const mergeKeyGroupItems = (groups, items) => {
2
- if (!groups) {
3
- return items
4
- }
5
- return items.map((item) => {
6
- const { id } = item
7
- if (groups[id]) {
8
- return { ...item, ...groups[id] }
9
- }
10
- return item
11
- })
12
- }
@@ -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
- })