Commit 47904e4c authored by Stefan Probst's avatar Stefan Probst
Browse files

feat: add form field help text

parent d59e2a73
{
"accessibleAt": "URL, ideally landing page, of the resource",
"actor": "Person/group/body who helped create resource",
"description": "Should be concise and raise interest in entry",
"externalId": "Other URLs relevant to resource, eg code, DOI, etc.",
"label": "Name of the MP entry, max 255 char",
"media-object": "Media can be images or videos",
"relatedItem": "Relations with other Marketplace items",
"thumbnail": "Logo related to MP entry",
"properties": {
"access-policy-url": "URL pointing to info about the access policy",
"activity": "The activities you can do with the resource",
"authentication": "Is authentication needed? Yes or no",
"conference": "Name of the conference where paper was given",
"discipline": "Describes the discipline covered by resource",
"extent": "",
"geographical-availability": "Locations where the Resource is offered",
"helpdesk-url": "URL to helpdesk for incidents & user requests",
"issue": "A particular published issue of a journal",
"journal": "The journal the work was published in",
"keyword": "Concept or term related to MP entry",
"language": "Language(s) in which a resource is available",
"license": "Select license pertaining to resource",
"life-cycle-status": "Status of the Resource life-cycle",
"mode-of-use": "Mode of use for the resource",
"object-format": "File format of the linked resource",
"pages": "Page #s of a resource, separate by commas",
"privacy-policy-url": "Link to the privacy policy of resource",
"publication-type": "The publication type, eg Book, article, etc",
"publication-place": "The place of publication",
"publisher": "Entity responsible for making the resource available",
"resource-category": "",
"see-also": "Links to non-MP materials that are relevant",
"service-level-url": "Info about performance levels provider expected to deliver",
"_service-type": "Type of service",
"source-last-update": "",
"standard": "",
"technical-readiness-level": "Technology Readiness Level of the Resource",
"terms-of-use": "If license unknown, fill in textbox",
"terms-of-use-url": "Webpage describing terms of use",
"tool-family": "Type of tool",
"user-manual-url": "Resource user manual and documentation URL",
"version": "Version of the tool or service that is linked",
"volume": "The volume of a journal or multivolume book",
"year": "The year of publication"
}
}
......@@ -26,6 +26,7 @@ import { FormTextField } from '@/modules/form/components/FormTextField/FormTextF
import { Form } from '@/modules/form/Form'
import { FormFieldArray } from '@/modules/form/FormFieldArray'
import { isEmail, isUrl } from '@/modules/form/validate'
import helpText from '@@/config/form-helptext.json'
export interface ActorsFormSectionProps {
initialValues?: any
......@@ -163,6 +164,7 @@ function ActorComboBox(props: ActorComboBoxProps): JSX.Element {
onInputChange={setSearchTerm}
variant="form"
style={{ flex: 1 }}
helpText={helpText.actor}
>
{(item) => <FormComboBox.Item>{item.name}</FormComboBox.Item>}
</FormComboBox>
......
......@@ -8,6 +8,7 @@ import { FormSelect } from '@/modules/form/components/FormSelect/FormSelect'
import { FormTextArea } from '@/modules/form/components/FormTextArea/FormTextArea'
import { FormTextField } from '@/modules/form/components/FormTextField/FormTextField'
import { FormFieldArray } from '@/modules/form/FormFieldArray'
import helpText from '@@/config/form-helptext.json'
export interface MainFormSectionProps {
prefix?: string
......@@ -27,11 +28,13 @@ export function MainFormSection(props: MainFormSectionProps): JSX.Element {
label={'Label'}
isRequired
variant="form"
helpText={helpText.label}
/>
<FormTextField
name={`${prefix}version`}
label={'Version'}
variant="form"
// helpText={helpText.version}
/>
</div>
<FormTextArea
......@@ -40,6 +43,7 @@ export function MainFormSection(props: MainFormSectionProps): JSX.Element {
isRequired
rows={8}
variant="form"
helpText={helpText.description}
/>
<FormFieldArray name={`${prefix}accessibleAt`}>
{({ fields }) => {
......@@ -61,6 +65,7 @@ export function MainFormSection(props: MainFormSectionProps): JSX.Element {
label={'Accessible at'}
variant="form"
style={{ flex: 1 }}
helpText={helpText.accessibleAt}
/>
</FormRecord>
)
......@@ -96,6 +101,7 @@ export function MainFormSection(props: MainFormSectionProps): JSX.Element {
label="Identifier"
variant="form"
style={{ flex: 1 }}
helpText={helpText.externalId}
/>
</FormRecord>
)
......
......@@ -10,6 +10,7 @@ import { FormFieldAddButton } from '@/modules/form/components/FormFieldAddButton
import { FormSection } from '@/modules/form/components/FormSection/FormSection'
import { FormField } from '@/modules/form/FormField'
import { FormFieldArray } from '@/modules/form/FormFieldArray'
import helpText from '@@/config/form-helptext.json'
export interface MediaFormSectionProps {
initialValues?: any
......
......@@ -19,6 +19,7 @@ import { FormTextField } from '@/modules/form/components/FormTextField/FormTextF
import { FormField } from '@/modules/form/FormField'
import { FormFieldArray } from '@/modules/form/FormFieldArray'
import { FormFieldCondition } from '@/modules/form/FormFieldCondition'
import helpText from '@@/config/form-helptext.json'
export interface PropertiesFormSectionProps {
initialValues?: any
......@@ -113,12 +114,18 @@ export function PropertiesFormSection(
propertyTypesById[id].type !== 'concept'
}
>
<FormTextField
name={`${name}.value`}
label={'Value'}
variant="form"
style={{ flex: 1 }}
/>
{(id: string) => {
return (
<FormTextField
name={`${name}.value`}
label={'Value'}
variant="form"
style={{ flex: 1 }}
// @ts-expect-error It's ok
helpText={helpText.properties[id]}
/>
)
}}
</FormFieldCondition>
</FormRecord>
)
......@@ -225,6 +232,12 @@ function PropertyConceptSelect(props: PropertyConceptSelectProps): JSX.Element {
onInputChange={setSearchTerm}
variant="form"
style={{ flex: 1 }}
helpText={
props.propertyTypeId != null
? // @ts-expect-error It's ok
helpText.properties[props.propertyTypeId]
: undefined
}
>
{(item) => (
<FormComboBox.Item key={item.uri}>{item.label}</FormComboBox.Item>
......
......@@ -10,6 +10,7 @@ import { FormRecords } from '@/modules/form/components/FormRecords/FormRecords'
import { FormSection } from '@/modules/form/components/FormSection/FormSection'
import { FormSelect } from '@/modules/form/components/FormSelect/FormSelect'
import { FormFieldArray } from '@/modules/form/FormFieldArray'
import helpText from '@@/config/form-helptext.json'
export interface RelatedItemsFormSectionProps {
initialValues?: any
......@@ -136,6 +137,7 @@ function RelatedItemComboBox(props: RelatedItemComboBoxProps): JSX.Element {
onInputChange={setSearchTerm}
variant="form"
style={{ flex: 1 }}
helpText={helpText.relatedItem}
>
{(item) => (
<FormComboBox.Item key={item.persistentId}>
......
......@@ -3,6 +3,7 @@ import { Fragment } from 'react'
import type { MediaDetails } from '@/api/sshoc'
import { AddMediaForm as AddThumbnailForm } from '@/components/item/AddMediaForm/AddMediaForm'
import { Thumbnail } from '@/components/item/Thumbnail/Thumbnail'
import { Icon } from '@/elements/Icon/Icon'
import { Svg as CloseIcon } from '@/elements/icons/small/cross.svg'
import { MediaError } from '@/lib/error/MediaError'
......@@ -10,8 +11,7 @@ import { useDialogState } from '@/lib/hooks/useDialogState'
import { FormFieldAddButton } from '@/modules/form/components/FormFieldAddButton/FormFieldAddButton'
import { FormSection } from '@/modules/form/components/FormSection/FormSection'
import { FormField } from '@/modules/form/FormField'
import { Thumbnail } from '../Thumbnail/Thumbnail'
import helpText from '@@/config/form-helptext.json'
export interface ThumbnailFormSectionProps {
initialValues?: any
......
......@@ -33,6 +33,7 @@ export interface ComboBoxProps<T>
AsyncLoadable {
name?: string
validationMessage?: ReactNode
helpText?: ReactNode
/** @default "icon" */
necessityIndicator?: NecessityIndicator
// loadingState?: 'loading'
......@@ -187,6 +188,7 @@ export function ComboBox<T extends object>(
necessityIndicator={props.necessityIndicator}
validationState={props.validationState}
validationMessage={props.validationMessage}
helpText={props.helpText}
errorMessageProps={errorMessageProps}
style={props.style}
>
......
......@@ -10,10 +10,11 @@ import type {
LabelHTMLAttributes,
ReactNode,
} from 'react'
import { Fragment } from 'react'
import { ErrorMessage } from '@/elements/ErrorMessage/ErrorMessage'
import { HelpText } from '@/elements/HelpText/HelpText'
import { Label } from '@/elements/Label/Label'
import { SuccessMessage } from '@/elements/SuccessMessage/SuccessMessage'
export interface FieldProps extends LabelableProps, Validation {
children?: JSX.Element
......@@ -24,6 +25,7 @@ export interface FieldProps extends LabelableProps, Validation {
/** @default "icon" */
necessityIndicator?: NecessityIndicator
validationMessage?: ReactNode
helpText?: ReactNode
errorMessageProps?: HTMLAttributes<HTMLElement>
style?: CSSProperties
}
......@@ -63,6 +65,11 @@ export function Field(props: FieldProps): JSX.Element {
<ErrorMessage {...props.errorMessageProps}>
{props.validationMessage}
</ErrorMessage>
) : props.validationState === 'valid' &&
props.validationMessage !== undefined ? (
<SuccessMessage>{props.validationMessage}</SuccessMessage>
) : props.helpText !== undefined ? (
<HelpText>{props.helpText}</HelpText>
) : null}
</div>
)
......
import type { DOMProps } from '@react-types/shared'
import type { ReactNode } from 'react'
export interface HelpTextProps extends DOMProps {
children?: ReactNode
}
/**
* Help text for form fields.
*/
export function HelpText(props: HelpTextProps): JSX.Element {
const message = props.children
const styles = {
message:
'font-body font-normal text-ui-base text-gray-550 inline-flex items-center space-x-1 select-none cursor-default',
}
return (
<div className={styles.message}>
<span id={props.id}>{message}</span>
</div>
)
}
......@@ -25,6 +25,7 @@ import { useErrorMessage } from '@/modules/a11y/useErrorMessage'
export interface SelectProps<T> extends AriaSelectProps<T> {
name?: string
validationMessage?: ReactNode
helpText?: ReactNode
/** @default "icon" */
necessityIndicator?: NecessityIndicator
shouldFocusWrap?: boolean
......@@ -173,6 +174,7 @@ export function Select<T extends object>(props: SelectProps<T>): JSX.Element {
necessityIndicator={props.necessityIndicator}
validationState={props.validationState}
validationMessage={props.validationMessage}
helpText={props.helpText}
errorMessageProps={errorMessageProps}
style={props.style}
>
......
import { useMessageFormatter } from '@react-aria/i18n'
import type { DOMProps } from '@react-types/shared'
import type { ReactNode } from 'react'
import { Icon } from '@/elements/Icon/Icon'
import { Svg as InfoIcon } from '@/elements/icons/small/info.svg'
import dictionary from '@/elements/SuccessMessage/dictionary.json'
export interface SuccessMessageProps extends DOMProps {
children?: ReactNode
}
/**
* Success message for form fields.
*/
export function SuccessMessage(props: SuccessMessageProps): JSX.Element {
const t = useMessageFormatter(dictionary)
const message = props.children ?? t('success')
const styles = {
message:
'font-body font-normal text-ui-base text-success-500 inline-flex items-center space-x-1 select-none cursor-default',
}
return (
<div className={styles.message}>
<Icon icon={InfoIcon} />
<span id={props.id}>{message}</span>
</div>
)
}
{
"en": {
"success": "Success"
}
}
......@@ -13,6 +13,7 @@ export interface TextAreaProps extends AriaTextFieldProps {
size?: 'md' | 'lg'
necessityIndicator?: NecessityIndicator
validationMessage?: ReactNode
helpText?: ReactNode
rows?: number
}
......
......@@ -13,6 +13,7 @@ export interface TextFieldProps extends AriaTextFieldProps {
size?: 'md' | 'lg'
necessityIndicator?: NecessityIndicator
validationMessage?: ReactNode
helpText?: ReactNode
style?: CSSProperties
}
......
......@@ -99,6 +99,7 @@ export function TextFieldBase(
necessityIndicator={props.necessityIndicator}
validationState={props.validationState}
validationMessage={props.validationMessage}
helpText={props.helpText}
errorMessageProps={errorMessageProps}
style={props.style}
>
......
......@@ -11,7 +11,7 @@ import { useInteractionModality } from '@react-aria/interactions'
import { SSRProvider } from '@react-aria/ssr'
import Layout from '@stefanprobst/next-app-layout'
import ErrorBoundary from '@stefanprobst/next-error-boundary'
import type { AppProps, NextWebVitalsMetric } from 'next/app'
import type { AppProps } from 'next/app'
import Head from 'next/head'
import { Router } from 'next/router'
import np from 'nprogress'
......
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment