From 2e94cd660ca863743ee17146187c716e4774196f Mon Sep 17 00:00:00 2001 From: Tymoteusz Czech <2625371+Tymek@users.noreply.github.com> Date: Fri, 8 Jul 2022 09:35:32 +0200 Subject: [PATCH] Playground form (#1129) * playground form * playground context fields * playground interactive json form * add playground route * remove memo from select options generation * add toast when playground context parsing fails * add error handling when adding a fiedl to playground context * remove playground context options memo --- frontend/src/component/menu/Header/Header.tsx | 6 + .../__snapshots__/routes.test.tsx.snap | 10 ++ frontend/src/component/menu/routes.ts | 11 ++ .../playground/Playground/Playground.tsx | 92 ++++++++++ .../PlaygroundCodeFieldset.tsx | 162 ++++++++++++++++++ .../PlaygroundConnectionFieldset.tsx | 104 +++++++++++ 6 files changed, 385 insertions(+) create mode 100644 frontend/src/component/playground/Playground/Playground.tsx create mode 100644 frontend/src/component/playground/Playground/PlaygroundCodeFieldset/PlaygroundCodeFieldset.tsx create mode 100644 frontend/src/component/playground/Playground/PlaygroundConnectionFieldset/PlaygroundConnectionFieldset.tsx diff --git a/frontend/src/component/menu/Header/Header.tsx b/frontend/src/component/menu/Header/Header.tsx index facdb236ce..d79afd3e7a 100644 --- a/frontend/src/component/menu/Header/Header.tsx +++ b/frontend/src/component/menu/Header/Header.tsx @@ -127,6 +127,12 @@ const Header: VFC = () => { Feature toggles + + Playground + + + + {Boolean(contextObject) && ( + + TODO: Request +
{contextObject}
+
+ )} + + ); +}; diff --git a/frontend/src/component/playground/Playground/PlaygroundCodeFieldset/PlaygroundCodeFieldset.tsx b/frontend/src/component/playground/Playground/PlaygroundCodeFieldset/PlaygroundCodeFieldset.tsx new file mode 100644 index 0000000000..f2d1f17164 --- /dev/null +++ b/frontend/src/component/playground/Playground/PlaygroundCodeFieldset/PlaygroundCodeFieldset.tsx @@ -0,0 +1,162 @@ +import { + Dispatch, + SetStateAction, + useEffect, + useMemo, + useState, + VFC, +} from 'react'; +import { + Box, + Button, + FormControl, + InputLabel, + MenuItem, + Select, + TextField, + Typography, + useTheme, +} from '@mui/material'; +import { debounce } from 'debounce'; +import useUnleashContext from 'hooks/api/getters/useUnleashContext/useUnleashContext'; +import { formatUnknownError } from 'utils/formatUnknownError'; +import useToast from 'hooks/useToast'; + +interface IPlaygroundCodeFieldsetProps { + value: string | undefined; + setValue: Dispatch>; +} + +export const PlaygroundCodeFieldset: VFC = ({ + value, + setValue, +}) => { + const theme = useTheme(); + const { setToastData } = useToast(); + const { context } = useUnleashContext(); + const contextOptions = context + .sort((a, b) => a.sortOrder - b.sortOrder) + .map(({ name }) => name); + const [error, setError] = useState(); + const debounceSetError = useMemo( + () => + debounce((input?: string) => { + if (!input) { + return setError(undefined); + } + + try { + JSON.parse(input); + } catch (error: unknown) { + return setError(formatUnknownError(error)); + } + + return setError(undefined); + }, 250), + [setError] + ); + + useEffect(() => { + debounceSetError(value); + }, [debounceSetError, value]); + + const [contextField, setContextField] = useState(''); + const [contextValue, setContextValue] = useState(''); + const onAddField = () => { + try { + const currentValue = JSON.parse(value || '{}'); + setValue( + JSON.stringify( + { + ...currentValue, + [contextField]: contextValue, + }, + null, + 2 + ) + ); + } catch (error) { + setToastData({ + type: 'error', + title: `Error parsing context: ${formatUnknownError(error)}`, + }); + } + }; + + return ( + + + Unleash context + + setValue(event.target.value)} + /> + + + + Context field + + + + + setContextValue(event.target.value || '') + } + /> + + + + ); +}; diff --git a/frontend/src/component/playground/Playground/PlaygroundConnectionFieldset/PlaygroundConnectionFieldset.tsx b/frontend/src/component/playground/Playground/PlaygroundConnectionFieldset/PlaygroundConnectionFieldset.tsx new file mode 100644 index 0000000000..43ed6b3ba0 --- /dev/null +++ b/frontend/src/component/playground/Playground/PlaygroundConnectionFieldset/PlaygroundConnectionFieldset.tsx @@ -0,0 +1,104 @@ +import { ComponentProps, useMemo, useState, VFC } from 'react'; +import { + Autocomplete, + Box, + TextField, + Typography, + useTheme, +} from '@mui/material'; +import { useEnvironments } from 'hooks/api/getters/useEnvironments/useEnvironments'; +import useProjects from 'hooks/api/getters/useProjects/useProjects'; + +interface IPlaygroundConnectionFieldsetProps {} + +interface IOption { + label: string; + id: string; +} + +const allOption: IOption = { label: 'ALL', id: '*' }; + +export const PlaygroundConnectionFieldset: VFC< + IPlaygroundConnectionFieldsetProps +> = () => { + const theme = useTheme(); + const { environments } = useEnvironments(); + const environmentOptions = environments + .filter(({ enabled }) => Boolean(enabled)) + .sort((a, b) => a.sortOrder - b.sortOrder) + .map(({ name }) => name); + + const { projects: availableProjects = [] } = useProjects(); + const projectsOptions = [ + allOption, + ...availableProjects.map(({ name: label, id }) => ({ + label, + id, + })), + ]; + const [projects, setProjects] = useState(allOption); + + const onProjectsChange: ComponentProps['onChange'] = ( + event, + value, + reason + ) => { + const newProjects = value as IOption | IOption[]; + if (reason === 'clear' || newProjects === null) { + return setProjects(allOption); + } + if (Array.isArray(newProjects)) { + if (newProjects.length === 0) { + return setProjects(allOption); + } + if ( + newProjects.find(({ id }) => id === allOption.id) !== undefined + ) { + return setProjects(allOption); + } + return setProjects(newProjects); + } + if (newProjects.id === allOption.id) { + return setProjects(allOption); + } + + return setProjects([newProjects]); + }; + + return ( + + + Access configuration + + + ( + + )} + size="small" + /> + ( + + )} + size="small" + value={projects} + onChange={onProjectsChange} + /> + + + ); +};