-
Notifications
You must be signed in to change notification settings - Fork 2
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
1 parent
b6b4220
commit 80b88d3
Showing
7 changed files
with
962 additions
and
76 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
126 changes: 126 additions & 0 deletions
126
...uk/src/pages/FlowEditor/components/Sidebar/Search/SearchResultCard/DataDisplayMap.test.ts
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,126 @@ | ||
import { ComponentType } from "@opensystemslab/planx-core/types"; | ||
import { useStore } from "pages/FlowEditor/lib/store"; | ||
|
||
import { | ||
mockAnswerResult, | ||
mockCalculateFormulaResult, | ||
mockCalculateRootResult, | ||
mockFileUploadAndLabelResult, | ||
mockFlow, | ||
mockListAnswerResult, | ||
mockListDataResult, | ||
mockListRootResult, | ||
mockQuestionResult, | ||
} from "../mocks/DataDisplayMap"; | ||
import { getDisplayDetailsForResult } from "./DataDisplayMap"; | ||
|
||
type Output = ReturnType<typeof getDisplayDetailsForResult>; | ||
|
||
// Setup flow so that it can be referenced by SearchResults (e.g. getting parent nodes) | ||
beforeAll(() => useStore.setState({ flow: mockFlow })); | ||
|
||
describe("Question component", () => { | ||
it("returns the expected display values", () => { | ||
const output = getDisplayDetailsForResult(mockQuestionResult); | ||
|
||
expect(output).toStrictEqual<Output>({ | ||
key: "Data", | ||
iconKey: ComponentType.Question, | ||
componentType: "Question", | ||
title: "This is a question component", | ||
headline: "colour", | ||
}); | ||
}); | ||
}); | ||
|
||
describe("Answer component", () => { | ||
it("returns the expected display values", () => { | ||
const output = getDisplayDetailsForResult(mockAnswerResult); | ||
|
||
expect(output).toStrictEqual<Output>({ | ||
key: "Option (data)", | ||
iconKey: ComponentType.Question, | ||
componentType: "Question", | ||
title: "This is a question component", | ||
headline: "red", | ||
}); | ||
}); | ||
}); | ||
|
||
describe("List component", () => { | ||
it("handles the root data value", () => { | ||
const output = getDisplayDetailsForResult(mockListRootResult); | ||
|
||
expect(output).toStrictEqual<Output>({ | ||
componentType: "List", | ||
headline: "listRoot", | ||
iconKey: ComponentType.List, | ||
key: "Data", | ||
title: "This is a list component", | ||
}); | ||
}); | ||
|
||
it("handles nested data variables", () => { | ||
const output = getDisplayDetailsForResult(mockListDataResult); | ||
|
||
expect(output).toStrictEqual<Output>({ | ||
componentType: "List", | ||
headline: "tenure", | ||
iconKey: ComponentType.List, | ||
key: "Data", | ||
title: "This is a list component", | ||
}); | ||
}); | ||
|
||
it("handles nested data variables in Answers", () => { | ||
const output = getDisplayDetailsForResult(mockListAnswerResult); | ||
|
||
expect(output).toStrictEqual<Output>({ | ||
componentType: "List", | ||
headline: "selfCustomBuild", | ||
iconKey: ComponentType.List, | ||
key: "Option (data)", | ||
title: "This is a list component", | ||
}); | ||
}); | ||
}); | ||
|
||
describe("Calculate component", () => { | ||
it("handles the output data variables", () => { | ||
const output = getDisplayDetailsForResult(mockCalculateRootResult); | ||
|
||
expect(output).toStrictEqual<Output>({ | ||
componentType: "Calculate", | ||
headline: "calculateOutput", | ||
iconKey: ComponentType.Calculate, | ||
key: "Output (data)", | ||
title: "This is a calculate component", | ||
}); | ||
}); | ||
|
||
it("handles the formula data variables", () => { | ||
const output = getDisplayDetailsForResult(mockCalculateFormulaResult); | ||
|
||
expect(output).toStrictEqual<Output>({ | ||
componentType: "Calculate", | ||
headline: "formulaOne + formulaTwo", | ||
iconKey: ComponentType.Calculate, | ||
key: "Formula", | ||
title: "This is a calculate component", | ||
}); | ||
}); | ||
}); | ||
|
||
describe("FileUploadAndLabel component", () => { | ||
it("handles the data variables nested in FileTypes", () => { | ||
const output = getDisplayDetailsForResult(mockFileUploadAndLabelResult); | ||
|
||
expect(output).toStrictEqual<Output>({ | ||
componentType: "File upload and label", | ||
headline: "floorplan", | ||
iconKey: ComponentType.FileUploadAndLabel, | ||
key: "File type (data)", | ||
title: "This is a FileUploadAndLabel component", | ||
}); | ||
}); | ||
}); |
116 changes: 116 additions & 0 deletions
116
...anx.uk/src/pages/FlowEditor/components/Sidebar/Search/SearchResultCard/DataDisplayMap.tsx
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,116 @@ | ||
import { ComponentType, IndexedNode } from "@opensystemslab/planx-core/types"; | ||
import { Calculate } from "@planx/components/Calculate/model"; | ||
import { FileUploadAndLabel } from "@planx/components/FileUploadAndLabel/model"; | ||
import { List } from "@planx/components/List/model"; | ||
import { SearchResult } from "hooks/useSearch"; | ||
import { capitalize, get } from "lodash"; | ||
import { SLUGS } from "pages/FlowEditor/data/types"; | ||
import { useStore } from "pages/FlowEditor/lib/store"; | ||
|
||
interface DataDisplayValues { | ||
displayKey: string; | ||
getIconKey: (result: SearchResult<IndexedNode>) => ComponentType; | ||
getTitle: (result: SearchResult<IndexedNode>) => string; | ||
getHeadline: (result: SearchResult<IndexedNode>) => string; | ||
getComponentType: (result: SearchResult<IndexedNode>) => string; | ||
} | ||
|
||
/** | ||
* Map of data keys to their associated display values | ||
* Uses Partial<DataDisplayValues> as not all values are unique, we later apply defaults | ||
*/ | ||
type DataKeyMap = Record<string, Partial<DataDisplayValues>>; | ||
|
||
/** | ||
* Map of ComponentTypes to their associated data keys | ||
*/ | ||
type ComponentMap = Record<ComponentType, DataKeyMap>; | ||
|
||
/** | ||
* Map of ComponentTypes which need specific overrides in order to display their data values | ||
*/ | ||
const DISPLAY_DATA: Partial<ComponentMap> = { | ||
// Answers are mapped to their parent questions | ||
[ComponentType.Answer]: { | ||
default: { | ||
getIconKey: () => ComponentType.Question, | ||
displayKey: "Option (data)", | ||
getTitle: ({ item }) => { | ||
const parentNode = useStore.getState().flow[item.parentId]; | ||
return parentNode.data.text; | ||
}, | ||
getHeadline: ({ item, key }) => get(item, key)?.toString(), | ||
}, | ||
}, | ||
// FileUploadAndLabel has data values nested in FileTypes | ||
[ComponentType.FileUploadAndLabel]: { | ||
default: { | ||
displayKey: "File type (data)", | ||
getHeadline: ({ item, refIndex }) => | ||
(item["data"] as unknown as FileUploadAndLabel)["fileTypes"][refIndex][ | ||
"fn" | ||
], | ||
}, | ||
}, | ||
// Calculate contains both input and output data values | ||
[ComponentType.Calculate]: { | ||
formula: { | ||
displayKey: "Formula", | ||
getHeadline: ({ item }) => (item.data as unknown as Calculate).formula, | ||
}, | ||
"data.output": { | ||
displayKey: "Output (data)", | ||
getHeadline: ({ item }) => (item.data as unknown as Calculate).output, | ||
}, | ||
}, | ||
// List contains data variables nested within its schema | ||
[ComponentType.List]: { | ||
"data.schema.fields.data.fn": { | ||
getHeadline: ({ item, refIndex }) => | ||
(item.data as unknown as List).schema.fields[refIndex].data.fn, | ||
}, | ||
"data.schema.fields.data.options.data.val": { | ||
displayKey: "Option (data)", | ||
getHeadline: ({ item, refIndex }) => { | ||
// Fuse.js flattens deeply nested arrays when using refIndex | ||
const options = (item.data as unknown as List).schema.fields | ||
.filter((field) => field.type === "question") | ||
.flatMap((field) => field.data.options); | ||
return options[refIndex].data.val || ""; | ||
}, | ||
}, | ||
}, | ||
}; | ||
|
||
/** | ||
* Default values for all ComponentTypes not listed in DISPLAY_DATA | ||
*/ | ||
const DEFAULT_DISPLAY_DATA: DataDisplayValues = { | ||
displayKey: "Data", | ||
getIconKey: ({ item }) => item.type, | ||
getTitle: ({ item }) => | ||
(item.data?.title as string) || (item.data?.text as string) || "", | ||
getHeadline: ({ item, key }) => get(item, key)?.toString() || "", | ||
getComponentType: ({ item }) => | ||
capitalize(SLUGS[item.type].replaceAll("-", " ")), | ||
}; | ||
|
||
export const getDisplayDetailsForResult = ( | ||
result: SearchResult<IndexedNode>, | ||
) => { | ||
const componentMap = DISPLAY_DATA[result.item.type]; | ||
const keyMap = componentMap?.[result.key] || componentMap?.default || {}; | ||
|
||
const data: DataDisplayValues = { | ||
...DEFAULT_DISPLAY_DATA, | ||
...keyMap, | ||
}; | ||
|
||
return { | ||
iconKey: data.getIconKey(result), | ||
componentType: data.getComponentType(result), | ||
title: data.getTitle(result), | ||
key: data.displayKey, | ||
headline: data.getHeadline(result), | ||
}; | ||
}; |
Oops, something went wrong.