From 3e0149c058cd75807bdd9add96fef080e59a1e31 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C4=B1dvan=20Akca?= Date: Fri, 10 Mar 2023 16:27:32 +0300 Subject: [PATCH] feat: add searchable json viewer component --- .../src/components/ExecutionStep/index.tsx | 8 +- .../components/SearchableJSONViewer/index.tsx | 78 +++++++++++++++++++ 2 files changed, 82 insertions(+), 4 deletions(-) create mode 100644 packages/web/src/components/SearchableJSONViewer/index.tsx diff --git a/packages/web/src/components/ExecutionStep/index.tsx b/packages/web/src/components/ExecutionStep/index.tsx index 106d03db..3c011fa2 100644 --- a/packages/web/src/components/ExecutionStep/index.tsx +++ b/packages/web/src/components/ExecutionStep/index.tsx @@ -10,7 +10,7 @@ import Box from '@mui/material/Box'; import type { IApp, IExecutionStep, IStep } from '@automatisch/types'; import TabPanel from 'components/TabPanel'; -import JSONViewer from 'components/JSONViewer'; +import SearchableJSONViewer from 'components/SearchableJSONViewer'; import AppIcon from 'components/AppIcon'; import { GET_APPS } from 'graphql/queries/get-apps'; import useFormatMessage from 'hooks/useFormatMessage'; @@ -92,16 +92,16 @@ export default function ExecutionStep( - + - + {hasError && ( - + )} diff --git a/packages/web/src/components/SearchableJSONViewer/index.tsx b/packages/web/src/components/SearchableJSONViewer/index.tsx new file mode 100644 index 00000000..acfcdd79 --- /dev/null +++ b/packages/web/src/components/SearchableJSONViewer/index.tsx @@ -0,0 +1,78 @@ +import * as React from 'react'; +import get from 'lodash/get'; +import set from 'lodash/set'; +import throttle from 'lodash/throttle'; +import { Box } from '@mui/material'; + +import { IJSONObject, IJSONValue } from '@automatisch/types'; +import JSONViewer from 'components/JSONViewer'; +import SearchInput from 'components/SearchInput'; + +type JSONViewerProps = { + data: IJSONObject; +}; + +type Entry = [string, IJSONValue]; + +const SearchableJSONViewer = ({ data }: JSONViewerProps) => { + const [filteredData, setFilteredData] = React.useState(data); + + const allEntries = React.useMemo(() => { + const entries: Entry[] = []; + const collectEntries = (obj: IJSONObject, prefix?: string) => { + for (const key in obj) { + if (typeof obj[key] === 'object' && obj[key] !== null) { + entries.push([[prefix, key].filter(Boolean).join('.'), obj[key]]); + + collectEntries( + obj[key] as IJSONObject, + [prefix, key].filter(Boolean).join('.') + ); + } else { + entries.push([[prefix, key].filter(Boolean).join('.'), obj[key]]); + } + } + }; + + collectEntries(data); + return entries; + }, [data]); + + const onSearchChange = React.useMemo( + () => + throttle((event: React.ChangeEvent) => { + const search = (event.target as HTMLInputElement).value.toLowerCase(); + const newFilteredData: IJSONObject = {}; + + if (!search) { + setFilteredData(data); + return; + } + + allEntries.forEach(([key, value]) => { + if ( + key.toLowerCase().includes(search) || + (typeof value !== 'object' && + value.toString().toLowerCase().includes(search)) + ) { + const value = get(filteredData, key); + set(newFilteredData, key, value); + } + }); + + setFilteredData(newFilteredData); + }, 400), + [allEntries] + ); + + return ( + <> + + + + + + ); +}; + +export default SearchableJSONViewer;