diff --git a/__tests__/e2e/constants/field.ts b/__tests__/e2e/constants/field.ts index 72886f8c5e..87d0b4ee08 100644 --- a/__tests__/e2e/constants/field.ts +++ b/__tests__/e2e/constants/field.ts @@ -89,7 +89,7 @@ export type E2eFieldMetadata = | (E2ePickFieldMetadata & E2eFieldSingleValue & E2eFieldHidden) - | (E2ePickFieldMetadata & + | (E2ePickFieldMetadata & E2eFieldFilepath & E2eFieldHidden) | (E2ePickFieldMetadata & @@ -203,6 +203,7 @@ export const ALL_FIELDS: E2eFieldMetadata[] = [ // fieldType: BasicField.Image, // path: '__tests__/e2e/files/logo.jpg', // description: 'Logo for an organization.', + // name: 'logo.jpg', // }, { title: 'Your Life Story', @@ -266,8 +267,7 @@ export const ALL_FIELDS: E2eFieldMetadata[] = [ { title: 'Statement', fieldType: BasicField.Statement, - description: - 'This is a paragraph with **markdown** and a url to https://open.gov.sg/.\n\n* Please type your answers here.\n* Please press submit once you are done.\n\nThank you!', + description: 'This is a statement. Read me!', }, { title: 'Family Members', diff --git a/__tests__/e2e/utils/field.ts b/__tests__/e2e/utils/field.ts index 9a3a2e25d7..761f473a92 100644 --- a/__tests__/e2e/utils/field.ts +++ b/__tests__/e2e/utils/field.ts @@ -103,10 +103,12 @@ export const getTitleWithQuestionNumber = ( const field = formFields[i] switch (field.fieldType) { case BasicField.Section: - case BasicField.Image: - case BasicField.Statement: { return field.title - } + case BasicField.Image: + return field.name + case BasicField.Statement: + // Replacement strategy for viewing in single line. + return field.description?.replace(/\s+/, ' ') ?? '' default: { const countNonInput = formFields .slice(0, i) diff --git a/frontend/src/components/Dropdown/MultiSelect/MultiSelectProvider.tsx b/frontend/src/components/Dropdown/MultiSelect/MultiSelectProvider.tsx index df61fbebd2..fa3a74ba93 100644 --- a/frontend/src/components/Dropdown/MultiSelect/MultiSelectProvider.tsx +++ b/frontend/src/components/Dropdown/MultiSelect/MultiSelectProvider.tsx @@ -44,6 +44,10 @@ export interface MultiSelectProviderProps< * Defaults to `4`. Set to `null` to render all items. */ maxItems?: number | null + /** + * If enabled, extends the length of each selected item to be full width in the input box. + */ + isSelectedItemFullWidth?: boolean /** aria-describedby to be attached to the combobox input, if any. */ inputAria?: { id: string @@ -59,6 +63,7 @@ export interface MultiSelectProviderProps< */ downshiftMultiSelectProps?: Partial> } + export const MultiSelectProvider = ({ items: rawItems, values, @@ -75,6 +80,7 @@ export const MultiSelectProvider = ({ isDisabled: isDisabledProp, isRequired: isRequiredProp, maxItems = 4, + isSelectedItemFullWidth, downshiftComboboxProps = {}, downshiftMultiSelectProps = {}, inputAria: inputAriaProp, @@ -333,6 +339,7 @@ export const MultiSelectProvider = ({ removeSelectedItem, reset, maxItems, + isSelectedItemFullWidth, activeIndex, setActiveIndex, }} diff --git a/frontend/src/components/Dropdown/MultiSelectContext.tsx b/frontend/src/components/Dropdown/MultiSelectContext.tsx index 08e29a2b31..0b73054569 100644 --- a/frontend/src/components/Dropdown/MultiSelectContext.tsx +++ b/frontend/src/components/Dropdown/MultiSelectContext.tsx @@ -16,6 +16,7 @@ interface MultiSelectContextReturn 'reset' | 'addSelectedItem' | 'removeSelectedItem' | 'setActiveIndex' > { maxItems: number | null + isSelectedItemFullWidth?: boolean } export const MultiSelectContext = createContext< diff --git a/frontend/src/components/Dropdown/components/MultiSelectCombobox/SelectedItems.tsx b/frontend/src/components/Dropdown/components/MultiSelectCombobox/SelectedItems.tsx index b5bdd3c376..104b4f823f 100644 --- a/frontend/src/components/Dropdown/components/MultiSelectCombobox/SelectedItems.tsx +++ b/frontend/src/components/Dropdown/components/MultiSelectCombobox/SelectedItems.tsx @@ -28,7 +28,8 @@ const ShowMoreItemBlock = ({ amountToShow }: { amountToShow: number }) => { } export const SelectedItems = (): JSX.Element => { - const { selectedItems, maxItems } = useMultiSelectContext() + const { selectedItems, maxItems, isSelectedItemFullWidth } = + useMultiSelectContext() const { isFocused } = useSelectContext() const items = useMemo(() => { @@ -38,7 +39,14 @@ export const SelectedItems = (): JSX.Element => { const item = selectedItems[i] // Key has to be index so focus is maintained correctly when items are removed. // Some downshift quirk it seems. - itemsToRender.push() + itemsToRender.push( + , + ) } else { itemsToRender.push( { } } return itemsToRender - }, [isFocused, maxItems, selectedItems]) + }, [isFocused, isSelectedItemFullWidth, maxItems, selectedItems]) return <>{items} } diff --git a/frontend/src/components/Dropdown/components/MultiSelectItem/MultiSelectItem.tsx b/frontend/src/components/Dropdown/components/MultiSelectItem/MultiSelectItem.tsx index 88ba9a1bdd..705df163a6 100644 --- a/frontend/src/components/Dropdown/components/MultiSelectItem/MultiSelectItem.tsx +++ b/frontend/src/components/Dropdown/components/MultiSelectItem/MultiSelectItem.tsx @@ -1,5 +1,5 @@ import { MouseEvent, useCallback, useMemo } from 'react' -import { Icon, TagLabel } from '@chakra-ui/react' +import { Flex, Icon, Stack, TagLabel } from '@chakra-ui/react' import { useMultiSelectContext } from '~components/Dropdown/MultiSelectContext' import { useSelectContext } from '~components/Dropdown/SelectContext' @@ -8,11 +8,10 @@ import { itemToIcon, itemToLabelString, } from '~components/Dropdown/utils/itemUtils' -import { Tag, TagCloseButton } from '~components/Tag' +import { Tag, TagCloseButton, TagProps } from '~components/Tag' -export interface MultiSelectItemProps< - Item extends ComboboxItem = ComboboxItem, -> { +export interface MultiSelectItemProps + extends TagProps { item: Item index: number } @@ -20,6 +19,7 @@ export interface MultiSelectItemProps< export const MultiSelectItem = ({ item, index, + ...props }: MultiSelectItemProps): JSX.Element => { const { isDisabled, isReadOnly, setIsFocused, closeMenu, isOpen, styles } = useSelectContext() @@ -81,24 +81,33 @@ export const MultiSelectItem = ({ }, onClick: handleTagClick, })} + {...props} > - {itemMeta.icon ? ( - + + {itemMeta.icon ? ( + + ) : null} + {itemMeta.label} + + - ) : null} - {itemMeta.label} - + ) } diff --git a/frontend/src/features/admin-form/create/logic/components/LogicContent/EditLogicBlock/EditCondition/ThenShowBlock.tsx b/frontend/src/features/admin-form/create/logic/components/LogicContent/EditLogicBlock/EditCondition/ThenShowBlock.tsx index 17de9eae1e..01644a5488 100644 --- a/frontend/src/features/admin-form/create/logic/components/LogicContent/EditLogicBlock/EditCondition/ThenShowBlock.tsx +++ b/frontend/src/features/admin-form/create/logic/components/LogicContent/EditLogicBlock/EditCondition/ThenShowBlock.tsx @@ -18,6 +18,8 @@ import { BASICFIELD_TO_DRAWER_META } from '~features/admin-form/create/constants import { EditLogicInputs } from '~features/admin-form/create/logic/types' import { FormFieldWithQuestionNo } from '~features/form/types' +import { getLogicFieldLabel } from '../../utils/getLogicFieldLabel' + import { BlockLabelText } from './BlockLabelText' interface ThenShowBlockProps { @@ -209,24 +211,17 @@ const ThenLogicInput = ({ const thenValueItems = useMemo(() => { // Return every field except fields that are already used in the logic. if (logicTypeValue === LogicType.ShowFields) { + if (!formFields || !mapIdToField) return [] const usedFieldIds = new Set( logicConditionsWatch.value.map((condition) => condition.field), ) - if (!formFields || !mapIdToField) return [] return formFields .filter((f) => !usedFieldIds.has(f._id)) - .map((f) => { - const mappedField = mapIdToField[f._id] - return { - value: f._id, - label: `${ - mappedField.questionNumber - ? `${mappedField.questionNumber}. ` - : '' - }${mappedField.title}`, - icon: BASICFIELD_TO_DRAWER_META[f.fieldType].icon, - } - }) + .map((f) => ({ + value: f._id, + label: getLogicFieldLabel(mapIdToField[f._id]), + icon: BASICFIELD_TO_DRAWER_META[f.fieldType].icon, + })) } return [] // Watch entire <***>Watch variables since <***>Watch.value is a Proxy object @@ -281,6 +276,7 @@ const ThenLogicInput = ({ placeholder={null} items={thenValueItems} values={value ?? []} + isSelectedItemFullWidth {...rest} /> )} diff --git a/frontend/src/features/admin-form/create/logic/components/LogicContent/InactiveLogicBlock/FieldLogicBadge.tsx b/frontend/src/features/admin-form/create/logic/components/LogicContent/InactiveLogicBlock/FieldLogicBadge.tsx index 5a9498bb10..bc5f42d419 100644 --- a/frontend/src/features/admin-form/create/logic/components/LogicContent/InactiveLogicBlock/FieldLogicBadge.tsx +++ b/frontend/src/features/admin-form/create/logic/components/LogicContent/InactiveLogicBlock/FieldLogicBadge.tsx @@ -7,6 +7,8 @@ import Tooltip from '~components/Tooltip' import { BASICFIELD_TO_DRAWER_META } from '~features/admin-form/create/constants' import { FormFieldWithQuestionNo } from '~features/form/types' +import { getLogicFieldLabel } from '../utils/getLogicFieldLabel' + import { LogicBadge } from './LogicBadge' interface FieldLogicBadgeProps { @@ -63,10 +65,7 @@ export const FieldLogicBadge = ({ {field ? ( - <> - {field.questionNumber ? {field.questionNumber}. : null} - {field.title} - + {getLogicFieldLabel(field)} ) : ( {defaults.message} diff --git a/frontend/src/features/admin-form/create/logic/components/LogicContent/utils/getLogicFieldLabel.ts b/frontend/src/features/admin-form/create/logic/components/LogicContent/utils/getLogicFieldLabel.ts new file mode 100644 index 0000000000..bf959d56e4 --- /dev/null +++ b/frontend/src/features/admin-form/create/logic/components/LogicContent/utils/getLogicFieldLabel.ts @@ -0,0 +1,20 @@ +import { BasicField } from '~shared/types' + +import { FormFieldWithQuestionNo } from '~features/form/types' + +export const getLogicFieldLabel = (field: FormFieldWithQuestionNo) => { + const questionNumber = field.questionNumber ? `${field.questionNumber}. ` : '' + let title = field.title + switch (field.fieldType) { + case BasicField.Statement: + // Replaces all continuous whitespace with a single space for display on a single line. + title = field.description.replace(/\s+/, ' ') + break + case BasicField.Image: + title = field.name + break + default: + break + } + return questionNumber + title +}