Skip to content

Commit d9bc608

Browse files
nicoaleejdkent
andauthored
fix: added add metadata row feature in study annotations (#1197)
* fix: added add metadata row feature in study annotations * feat: add edit study annotation numeric and column deletion support * add annotation table re-ordering logic and tests * syncronize package.json --------- Co-authored-by: James Kent <[email protected]>
1 parent 61445c5 commit d9bc608

26 files changed

+459
-349
lines changed

compose/neurosynth-frontend/cypress/fixtures/ImportSleuth/neurosynthResponses/annotationsSingleSleuthStudyResponse.json

Lines changed: 9 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -25,10 +25,16 @@
2525
"source_id": null,
2626
"source_updated_at": null,
2727
"note_keys": {
28-
"included": "boolean",
29-
"test1_new_txt": "boolean"
28+
"included": {
29+
"type": "boolean",
30+
"order": 0
31+
},
32+
"test1_new_txt": {
33+
"type": "boolean",
34+
"order": 1
35+
}
3036
},
3137
"metadata": null,
3238
"name": "Annotation for Untitled sleuth project",
3339
"description": ""
34-
}
40+
}

compose/neurosynth-frontend/cypress/fixtures/IngestionFixtures/annotationsFixture.json

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,10 @@
1010
"source_id": null,
1111
"source_updated_at": null,
1212
"note_keys": {
13-
"included": "boolean"
13+
"included": {
14+
"type": "boolean",
15+
"order": 0
16+
}
1417
},
1518
"metadata": null,
1619
"name": "Annotation for studyset hQFjozWL9v8Q",

compose/neurosynth-frontend/cypress/fixtures/IngestionFixtures/annotationsPutFixture.json

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,10 @@
1010
"source_id": null,
1111
"source_updated_at": null,
1212
"note_keys": {
13-
"included": "boolean"
13+
"included": {
14+
"type": "boolean",
15+
"order": 0
16+
}
1417
},
1518
"metadata": null,
1619
"name": "Annotation for studyset hQFjozWL9v8Q",

compose/neurosynth-frontend/cypress/fixtures/annotation.json

Lines changed: 9 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -5,8 +5,14 @@
55
"metadata": null,
66
"name": "Annotation for studyset 73HRs8HaJbR8",
77
"note_keys": {
8-
"included": "boolean",
9-
"string_key": "string"
8+
"included": {
9+
"type": "boolean",
10+
"order": 0
11+
},
12+
"string_key": {
13+
"type": "string",
14+
"order": 1
15+
}
1016
},
1117
"notes": [
1218
{
@@ -44,4 +50,4 @@
4450
"user": "github|26612023",
4551
"username": "Nicholas Lee"
4652
}
47-
53+

compose/neurosynth-frontend/package-lock.json

Lines changed: 9 additions & 51 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

compose/neurosynth-frontend/package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -102,7 +102,7 @@
102102
"prettier": "^3.3.3",
103103
"react-error-overlay": "^6.0.9",
104104
"ts-node": "^10.9.2",
105-
"typescript": "^4.9.5",
105+
"typescript": "^5.9.3",
106106
"typescript-eslint": "^8.13.0",
107107
"vite-tsconfig-paths": "^5.1.4",
108108
"vitest": "^3.0.7"

compose/neurosynth-frontend/src/components/HotTables/HotTables.types.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ import { EPropertyType } from 'components/EditMetadata/EditMetadata.types';
33
export interface NoteKeyType {
44
key: string;
55
type: EPropertyType;
6+
order: number;
67
}
78

89
export type AnnotationNoteValue = string | number | boolean | null;
Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,45 @@
1+
import { describe, expect, it } from 'vitest';
2+
import { EPropertyType } from 'components/EditMetadata/EditMetadata.types';
3+
import { noteKeyArrToObj, noteKeyObjToArr } from './HotTables.utils';
4+
5+
describe('HotTables utils - note key conversions', () => {
6+
it('converts note_keys object descriptors to a sorted array and reindexes order', () => {
7+
const input = {
8+
beta: { type: EPropertyType.STRING, order: 5 },
9+
alpha: { type: EPropertyType.NUMBER, order: 1 },
10+
gamma: { type: EPropertyType.BOOLEAN, order: 1 },
11+
};
12+
13+
const result = noteKeyObjToArr(input);
14+
15+
expect(result).toEqual([
16+
{ key: 'alpha', type: EPropertyType.NUMBER, order: 0 },
17+
{ key: 'gamma', type: EPropertyType.BOOLEAN, order: 1 },
18+
{ key: 'beta', type: EPropertyType.STRING, order: 2 },
19+
]);
20+
});
21+
22+
it('throws if a note_key descriptor is missing a type', () => {
23+
const invalid = {
24+
alpha: { order: 0 },
25+
} as any;
26+
27+
expect(() => noteKeyObjToArr(invalid)).toThrow(/missing type/i);
28+
});
29+
30+
it('converts a note key array back to descriptor object preserving order', () => {
31+
const arr = [
32+
{ key: 'first', type: EPropertyType.STRING, order: 2 },
33+
{ key: 'second', type: EPropertyType.BOOLEAN, order: 0 },
34+
{ key: 'third', type: EPropertyType.NUMBER, order: undefined as unknown as number },
35+
];
36+
37+
const result = noteKeyArrToObj(arr);
38+
39+
expect(result).toEqual({
40+
first: { type: EPropertyType.STRING, order: 2 },
41+
second: { type: EPropertyType.BOOLEAN, order: 0 },
42+
third: { type: EPropertyType.NUMBER, order: 2 },
43+
});
44+
});
45+
});

compose/neurosynth-frontend/src/components/HotTables/HotTables.utils.tsx

Lines changed: 22 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -4,19 +4,32 @@ import { CellValue } from 'handsontable/common';
44

55
export const noteKeyObjToArr = (noteKeys?: object | null): NoteKeyType[] => {
66
if (!noteKeys) return [];
7-
const noteKeyTypes = noteKeys as { [key: string]: EPropertyType };
8-
const arr = Object.entries(noteKeyTypes).map(([key, type]) => ({
9-
key,
10-
type,
11-
}));
7+
const noteKeyTypes = noteKeys as { [key: string]: { type: EPropertyType; order?: number } };
8+
const arr = Object.entries(noteKeyTypes)
9+
.map(([key, descriptor]) => {
10+
if (!descriptor?.type) throw new Error('Invalid note_keys descriptor: missing type');
11+
return {
12+
// rely on new descriptor shape (type + order)
13+
type: descriptor.type,
14+
key,
15+
order: descriptor.order ?? 0,
16+
};
17+
})
18+
.sort((a, b) => a.order - b.order || a.key.localeCompare(b.key))
19+
.map((noteKey, index) => ({ ...noteKey, order: index }));
1220
return arr;
1321
};
1422

15-
export const noteKeyArrToObj = (noteKeyArr: NoteKeyType[]): { [key: string]: EPropertyType } => {
16-
const noteKeyObj: { [key: string]: EPropertyType } = noteKeyArr.reduce((acc, curr) => {
17-
acc[curr.key] = curr.type;
23+
export const noteKeyArrToObj = (
24+
noteKeyArr: NoteKeyType[]
25+
): { [key: string]: { type: EPropertyType; order: number } } => {
26+
const noteKeyObj = noteKeyArr.reduce((acc, curr, index) => {
27+
acc[curr.key] = {
28+
type: curr.type,
29+
order: curr.order ?? index,
30+
};
1831
return acc;
19-
}, {} as { [key: string]: EPropertyType });
32+
}, {} as { [key: string]: { type: EPropertyType; order: number } });
2033

2134
return noteKeyObj;
2235
};

compose/neurosynth-frontend/src/pages/Annotations/components/EditAnnotationsHotTable.helpers.tsx

Lines changed: 5 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -119,11 +119,7 @@ export const annotationNotesToHotData = (
119119
};
120120
};
121121

122-
export const createColumnHeader = (
123-
colKey: string,
124-
colType: EPropertyType,
125-
allowRemoveColumn: boolean
126-
) => {
122+
export const createColumnHeader = (colKey: string, colType: EPropertyType, allowRemoveColumn: boolean) => {
127123
const allowRemove = allowRemoveColumn
128124
? `<div style="width: 50px; display: flex; align-items: center; justify-content: center">
129125
${renderToString(
@@ -141,7 +137,7 @@ export const createColumnHeader = (
141137

142138
return (
143139
`<div title="${colKey}" style="display: flex; align-items: center; justify-content: center;">` +
144-
`<div class="${styles[colType]} ${styles.truncate}" style="width: 150px">${colKey}</div>` +
140+
`<div class="${styles[colType]} ${styles.truncate}" style="width: 100px">${colKey}</div>` +
145141
allowRemove +
146142
`</div>`
147143
);
@@ -168,17 +164,15 @@ export const createColumns = (noteKeys: NoteKeyType[], disable?: boolean) =>
168164
x.type === EPropertyType.NUMBER
169165
? numericValidator
170166
: x.type === EPropertyType.BOOLEAN
171-
? booleanValidator
172-
: undefined,
167+
? booleanValidator
168+
: undefined,
173169
} as ColumnSettings;
174170
}),
175171
] as ColumnSettings[];
176172

177173
// we can assume that the hashmap maintains order and is sorted by key
178174
// this function gets all merge cells and only merge cells. If a cell does not need to be merged, a mergeCellObj is not creatd
179-
export const getMergeCells = (
180-
hotDataToStudyMapping: Map<number, { studyId: string; analysisId: string }>
181-
) => {
175+
export const getMergeCells = (hotDataToStudyMapping: Map<number, { studyId: string; analysisId: string }>) => {
182176
const mergeCells: MergeCellsSettings[] = [];
183177

184178
let studyId: string;

0 commit comments

Comments
 (0)