Skip to content

Commit

Permalink
FI-3261: Add markdown support to input descriptions (#546)
Browse files Browse the repository at this point in the history
* add markdown descriptions to text input

* replace requirement with input

* update deprecated prop

* replace react markdown with markdown

* add example

* fix broken test

* Add markdown formatting to input descriptions. (#564)

* fix lint errors

* resolve preset errors

---------

Co-authored-by: Rob Scanlon <robscanlon@gmail.com>
  • Loading branch information
AlyssaWang and arscan authored Dec 17, 2024
1 parent 1565a94 commit 6e669c1
Show file tree
Hide file tree
Showing 25 changed files with 278 additions and 271 deletions.
4 changes: 2 additions & 2 deletions client/src/components/InputsModal/AuthSettings.ts
Original file line number Diff line number Diff line change
Expand Up @@ -177,7 +177,7 @@ export const getAuthFields = (
},
] as TestInput[];

// If the requirement contains custom fields, replace default fields
// If the input contains custom fields, replace default fields
const fieldsToUpdate = components.map((component) => component.name);
fields.forEach((field, i) => {
if (fieldsToUpdate.includes(field.name)) {
Expand Down Expand Up @@ -322,7 +322,7 @@ export const getAccessFields = (
},
] as TestInput[];

// If the requirement contains custom fields, replace default fields
// If the input contains custom fields, replace default fields
const fieldsToUpdate = components.map((component) => component.name);
fields.forEach((field, i) => {
if (fieldsToUpdate.includes(field.name)) {
Expand Down
2 changes: 1 addition & 1 deletion client/src/components/InputsModal/AuthTypeSelector.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -55,7 +55,7 @@ const AuthTypeSelector: FC<InputAccessProps> = ({ input, index, inputsMap, setIn

return (
<InputCombobox
requirement={selectorModel}
input={selectorModel}
index={index}
inputsMap={inputsMap}
setInputsMap={setInputsMap}
Expand Down
16 changes: 7 additions & 9 deletions client/src/components/InputsModal/FieldLabel.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,26 +5,24 @@ import useStyles from './styles';
import RequiredInputWarning from './RequiredInputWarning';

export interface FieldLabelProps {
requirement: TestInput;
input: TestInput;
isMissingInput?: boolean;
}

const FieldLabel: FC<FieldLabelProps> = ({ requirement, isMissingInput = false }) => {
const FieldLabel: FC<FieldLabelProps> = ({ input, isMissingInput = false }) => {
const { classes } = useStyles();

const fieldLabelText = (requirement.title || requirement.name) as string;
const fieldLabelText = (input.title || input.name) as string;

// Radio buttons and single checkboxes will always have an input value
const requiredLabel =
!requirement.optional &&
requirement.type !== 'radio' &&
!(requirement.type === 'checkbox' && !requirement.options?.list_options?.length)
!input.optional &&
input.type !== 'radio' &&
!(input.type === 'checkbox' && !input.options?.list_options?.length)
? ' (required)'
: '';

const lockedIcon = requirement.locked && (
<LockIcon fontSize="small" className={classes.lockedIcon} />
);
const lockedIcon = input.locked && <LockIcon fontSize="small" className={classes.lockedIcon} />;

return (
<>
Expand Down
56 changes: 26 additions & 30 deletions client/src/components/InputsModal/InputAccess.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
import React, { FC, useEffect } from 'react';
import { Card, CardContent, InputLabel, List, ListItem, Typography } from '@mui/material';
import { Card, CardContent, InputLabel, List, ListItem } from '@mui/material';
import Markdown from 'react-markdown';
import remarkGfm from 'remark-gfm';
import { Auth, TestInput } from '~/models/testSuiteModels';
import { AuthType, getAccessFields } from '~/components/InputsModal/AuthSettings';
import FieldLabel from '~/components/InputsModal/FieldLabel';
Expand All @@ -8,25 +10,23 @@ import useStyles from './styles';
import AuthTypeSelector from './AuthTypeSelector';

export interface InputAccessProps {
requirement: TestInput;
input: TestInput;
index: number;
inputsMap: Map<string, unknown>;
setInputsMap: (map: Map<string, unknown>, edited?: boolean) => void;
}

const InputAccess: FC<InputAccessProps> = ({ requirement, index, inputsMap, setInputsMap }) => {
const InputAccess: FC<InputAccessProps> = ({ input, index, inputsMap, setInputsMap }) => {
const { classes } = useStyles();
const [accessValues, setAccessValues] = React.useState<Map<string, unknown>>(new Map());
const [accessValuesPopulated, setAccessValuesPopulated] = React.useState<boolean>(false);

// Default auth type settings
const [authType, setAuthType] = React.useState<string>(
requirement.options?.components
? (requirement.options?.components[0].default as string)
: 'public',
input.options?.components ? (input.options?.components[0].default as string) : 'public',
);
const [accessFields, setAccessFields] = React.useState<TestInput[]>(
getAccessFields(authType as AuthType, accessValues, requirement.options?.components || []),
getAccessFields(authType as AuthType, accessValues, input.options?.components || []),
);

useEffect(() => {
Expand Down Expand Up @@ -64,7 +64,7 @@ const InputAccess: FC<InputAccessProps> = ({ requirement, index, inputsMap, setI
useEffect(() => {
// Recalculate hidden fields
setAccessFields(
getAccessFields(authType as AuthType, accessValues, requirement.options?.components || []),
getAccessFields(authType as AuthType, accessValues, input.options?.components || []),
);

// Update inputsMap while maintaining hidden values
Expand All @@ -73,32 +73,28 @@ const InputAccess: FC<InputAccessProps> = ({ requirement, index, inputsMap, setI
const accessValuesObject = Object.fromEntries(accessValues) as Auth;
const combinedValues = { ...combinedStartingValues, ...accessValuesObject };
const stringifiedAccessValues = JSON.stringify(combinedValues);
inputsMap.set(requirement.name, stringifiedAccessValues);
inputsMap.set(input.name, stringifiedAccessValues);
setInputsMap(new Map(inputsMap));
}
}, [accessValues]);

const getStartingValues = () => {
// Pre-populate values from AuthFields, requirement, and inputsMap in order of precedence
// Pre-populate values from AuthFields, input, and inputsMap in order of precedence
const fieldDefaultValues = accessFields.reduce(
(acc, field) => ({ ...acc, [field.name]: field.default }),
{},
) as Auth;
const requirementDefaultValues =
requirement.default && typeof requirement.default === 'string'
? (JSON.parse(requirement.default) as Auth)
: {};
const requirementStartingValues =
requirement.value && typeof requirement.value === 'string'
? (JSON.parse(requirement.value) as Auth)
: {};
const inputsMapValues = inputsMap.get(requirement.name)
? (JSON.parse(inputsMap.get(requirement.name) as string) as Auth)
const inputDefaultValues =
input.default && typeof input.default === 'string' ? (JSON.parse(input.default) as Auth) : {};
const inputStartingValues =
input.value && typeof input.value === 'string' ? (JSON.parse(input.value) as Auth) : {};
const inputsMapValues = inputsMap.get(input.name)
? (JSON.parse(inputsMap.get(input.name) as string) as Auth)
: {};
return {
...fieldDefaultValues,
...requirementDefaultValues,
...requirementStartingValues,
...inputDefaultValues,
...inputStartingValues,
...inputsMapValues,
} as Auth;
};
Expand All @@ -113,20 +109,20 @@ const InputAccess: FC<InputAccessProps> = ({ requirement, index, inputsMap, setI
<Card variant="outlined" className={classes.authCard}>
<CardContent>
<InputLabel
required={!requirement.optional}
disabled={requirement.locked}
required={!input.optional}
disabled={input.locked}
className={classes.inputLabel}
>
<FieldLabel requirement={requirement} />
<FieldLabel input={input} />
</InputLabel>
{requirement.description && (
<Typography variant="subtitle1" component="p" className={classes.inputDescription}>
{requirement.description}
</Typography>
{input.description && (
<Markdown className={classes.inputDescription} remarkPlugins={[remarkGfm]}>
{input.description}
</Markdown>
)}
<List>
<AuthTypeSelector
input={requirement}
input={input}
index={index}
inputsMap={accessValues}
setInputsMap={updateAuthType}
Expand Down
52 changes: 23 additions & 29 deletions client/src/components/InputsModal/InputAuth.tsx
Original file line number Diff line number Diff line change
@@ -1,32 +1,32 @@
import React, { FC, useEffect } from 'react';
import { Box, List, ListItem, Typography } from '@mui/material';
import { Box, List, ListItem } from '@mui/material';
import Markdown from 'react-markdown';
import remarkGfm from 'remark-gfm';
import { Auth, TestInput } from '~/models/testSuiteModels';
import InputFields from './InputFields';
import useStyles from './styles';
import { AuthType, getAuthFields } from './AuthSettings';
import AuthTypeSelector from './AuthTypeSelector';

export interface InputAuthProps {
requirement: TestInput;
input: TestInput;
index: number;
inputsMap: Map<string, unknown>;
setInputsMap: (map: Map<string, unknown>, edited?: boolean) => void;
}

const InputAuth: FC<InputAuthProps> = ({ requirement, index, inputsMap, setInputsMap }) => {
const InputAuth: FC<InputAuthProps> = ({ input, index, inputsMap, setInputsMap }) => {
const { classes } = useStyles();
const [authValues, setAuthValues] = React.useState<Map<string, unknown>>(new Map());
const [authValuesPopulated, setAuthValuesPopulated] = React.useState<boolean>(false);

// Default auth type settings
const [authType, setAuthType] = React.useState<string>(
requirement.options?.components
? (requirement.options?.components[0].default as string)
: 'public',
input.options?.components ? (input.options?.components[0].default as string) : 'public',
);

const [authFields, setAuthFields] = React.useState<TestInput[]>(
getAuthFields(authType as AuthType, authValues, requirement.options?.components || []),
getAuthFields(authType as AuthType, authValues, input.options?.components || []),
);

useEffect(() => {
Expand Down Expand Up @@ -64,40 +64,34 @@ const InputAuth: FC<InputAuthProps> = ({ requirement, index, inputsMap, setInput

useEffect(() => {
// Recalculate hidden fields
setAuthFields(
getAuthFields(authType as AuthType, authValues, requirement.options?.components || []),
);
setAuthFields(getAuthFields(authType as AuthType, authValues, input.options?.components || []));

// Update inputsMap
if (authValuesPopulated) {
const stringifiedAuthValues = JSON.stringify(Object.fromEntries(authValues));
inputsMap.set(requirement.name, stringifiedAuthValues);
inputsMap.set(input.name, stringifiedAuthValues);
setInputsMap(new Map(inputsMap));
}
}, [authValues]);

const getStartingValues = () => {
// Pre-populate values from AuthFields, requirement, and inputsMap in order of precedence
// Pre-populate values from AuthFields, input, and inputsMap in order of precedence
const fieldDefaultValues = authFields.reduce(
(acc, field) => ({ ...acc, [field.name]: field.default }),
{},
) as Auth;
const requirementDefaultValues =
requirement.default && typeof requirement.default === 'string'
? (JSON.parse(requirement.default) as Auth)
: {};
const requirementStartingValues =
requirement.value && typeof requirement.value === 'string'
? (JSON.parse(requirement.value) as Auth)
: {};
const inputsMapValues = inputsMap.get(requirement.name)
? (JSON.parse(inputsMap.get(requirement.name) as string) as Auth)
const inputDefaultValues =
input.default && typeof input.default === 'string' ? (JSON.parse(input.default) as Auth) : {};
const inputStartingValues =
input.value && typeof input.value === 'string' ? (JSON.parse(input.value) as Auth) : {};
const inputsMapValues = inputsMap.get(input.name)
? (JSON.parse(inputsMap.get(input.name) as string) as Auth)
: {};

return {
...fieldDefaultValues,
...requirementDefaultValues,
...requirementStartingValues,
...inputDefaultValues,
...inputStartingValues,
...inputsMapValues,
} as Auth;
};
Expand All @@ -110,14 +104,14 @@ const InputAuth: FC<InputAuthProps> = ({ requirement, index, inputsMap, setInput
return (
<ListItem sx={{ p: 0 }}>
<Box width="100%">
{requirement.description && (
<Typography variant="subtitle1" component="p" className={classes.inputDescription}>
{requirement.description}
</Typography>
{input.description && (
<Markdown className={classes.inputDescription} remarkPlugins={[remarkGfm]}>
{input.description}
</Markdown>
)}
<List>
<AuthTypeSelector
input={requirement}
input={input}
index={index}
inputsMap={authValues}
setInputsMap={updateAuthType}
Expand Down
Loading

0 comments on commit 6e669c1

Please sign in to comment.