diff --git a/packages/@react-spectrum/labeledvalue/docs/LabeledValue.mdx b/packages/@react-spectrum/labeledvalue/docs/LabeledValue.mdx index 936b47f93ef..59bfaf755d3 100644 --- a/packages/@react-spectrum/labeledvalue/docs/LabeledValue.mdx +++ b/packages/@react-spectrum/labeledvalue/docs/LabeledValue.mdx @@ -111,6 +111,16 @@ By default, the list is displayed as a conjunction (an "and"-based grouping of i ``` +### Components + +The value can be a component and will be rendered as provided. Components cannot be editable. + +```tsx example +import {Link} from '@adobe/react-spectrum'; + +Adobe.com} /> +``` + ## Labeling A visual label must be provided to the `LabeledValue` using the `label` prop. diff --git a/packages/@react-spectrum/labeledvalue/docs/types.ts b/packages/@react-spectrum/labeledvalue/docs/types.ts index 1f9094c38f9..afe1e2a0b81 100644 --- a/packages/@react-spectrum/labeledvalue/docs/types.ts +++ b/packages/@react-spectrum/labeledvalue/docs/types.ts @@ -1,10 +1,11 @@ import {DateTime, LabeledValueBaseProps} from '@react-spectrum/labeledvalue/src/LabeledValue'; import {RangeValue} from '@react-types/shared'; +import {ReactElement} from 'react'; // The doc generator is not smart enough to handle the real types for LabeledValue so this is a simpler one. export interface LabeledValueProps extends LabeledValueBaseProps { /** The value to display. */ - value: string | string[] | number | RangeValue | DateTime | RangeValue, + value: string | string[] | number | RangeValue | DateTime | RangeValue | ReactElement, /** Formatting options for the value. The available options depend on the type passed to the `value` prop. */ formatOptions?: Intl.NumberFormatOptions | Intl.DateTimeFormatOptions | Intl.ListFormatOptions } diff --git a/packages/@react-spectrum/labeledvalue/src/LabeledValue.tsx b/packages/@react-spectrum/labeledvalue/src/LabeledValue.tsx index 58b91823bb0..0ad3da053cc 100644 --- a/packages/@react-spectrum/labeledvalue/src/LabeledValue.tsx +++ b/packages/@react-spectrum/labeledvalue/src/LabeledValue.tsx @@ -16,7 +16,7 @@ import type {DOMProps, DOMRef, RangeValue, SpectrumLabelableProps, StyleProps} f import {Field} from '@react-spectrum/label'; import {filterDOMProps} from '@react-aria/utils'; import labelStyles from '@adobe/spectrum-css-temp/components/fieldlabel/vars.css'; -import React, {ReactNode} from 'react'; +import React, {ReactElement, ReactNode, useEffect} from 'react'; import {useDateFormatter, useListFormatter, useNumberFormatter} from '@react-aria/i18n'; // NOTE: the types here need to be synchronized with the ones in docs/types.ts, which are simpler so the documentation generator can handle them. @@ -58,14 +58,22 @@ interface StringListProps { formatOptions?: Intl.ListFormatOptions } +interface ReactElementProps { + /** The value to display. */ + value: T, + /** Formatting options for the value. */ + formatOptions?: never +} + type LabeledValueProps = T extends NumberValue ? NumberProps : T extends DateTimeValue ? DateProps : T extends string[] ? StringListProps : T extends string ? StringProps : + T extends ReactElement ? ReactElementProps : never; -type SpectrumLabeledValueTypes = string[] | string | Date | CalendarDate | CalendarDateTime | ZonedDateTime | Time | number | RangeValue | RangeValue; +type SpectrumLabeledValueTypes = string[] | string | Date | CalendarDate | CalendarDateTime | ZonedDateTime | Time | number | RangeValue | RangeValue | ReactElement; export type SpectrumLabeledValueProps = LabeledValueProps & LabeledValueBaseProps; /** @@ -78,6 +86,17 @@ export const LabeledValue = React.forwardRef(function LabeledValue { + if ( + domRef?.current && + domRef.current.querySelectorAll('input, [contenteditable], textarea') + .length > 0 + ) { + throw new Error('LabeledValue cannot contain an editable value.'); + } + }, [domRef]); + + let children; if (Array.isArray(value)) { children = ; @@ -103,6 +122,10 @@ export const LabeledValue = React.forwardRef(function LabeledValue {children} diff --git a/packages/@react-spectrum/labeledvalue/stories/LabeledValue.stories.tsx b/packages/@react-spectrum/labeledvalue/stories/LabeledValue.stories.tsx index b27c847d233..d7d131eec41 100644 --- a/packages/@react-spectrum/labeledvalue/stories/LabeledValue.stories.tsx +++ b/packages/@react-spectrum/labeledvalue/stories/LabeledValue.stories.tsx @@ -11,16 +11,15 @@ */ import {CalendarDate, CalendarDateTime, Time, ZonedDateTime} from '@internationalized/date'; -import {ComponentMeta, ComponentStoryObj} from '@storybook/react'; import {Content} from '@react-spectrum/view'; import {ContextualHelp} from '@react-spectrum/contextualhelp'; import {Heading} from '@react-spectrum/text'; import {LabeledValue} from '..'; +import {Link} from '@react-spectrum/link'; +import {Meta} from '@storybook/react'; import React from 'react'; -type LabeledValueStory = ComponentStoryObj; - -export default { +const meta: Meta = { title: 'LabeledValue', component: LabeledValue, argTypes: { @@ -43,84 +42,95 @@ export default { } } } -} as ComponentMeta; +}; + +export default meta; -export let Default: LabeledValueStory = { +export let Default = { args: {label: 'Test', value: 'foo '.repeat(20)}, name: 'String' }; -export let StringArray: LabeledValueStory = { +export let StringArray = { args: {label: 'Test', value: ['wow', 'cool', 'awesome']}, name: 'String array' }; -export let CalendarDateType: LabeledValueStory = { +export let CalendarDateType = { args: {label: 'Test', value: new CalendarDate(2019, 6, 5)}, name: 'CalendarDate' }; -export let CalendarDateTimeType: LabeledValueStory = { +export let CalendarDateTimeType = { args: {label: 'Test', value: new CalendarDateTime(2020, 2, 3, 12, 23, 24, 120)}, name: 'CalendarDateTime' }; -export let CalendarDateTimeTypeFormatOptions: LabeledValueStory = { +export let CalendarDateTimeTypeFormatOptions = { args: {label: 'Test', value: new CalendarDateTime(2020, 2, 3, 12, 23, 24, 120), formatOptions: {dateStyle: 'short', timeStyle: 'short'}}, name: 'CalendarDateTime with formatOptions' }; -export let ZonedDateTimeType: LabeledValueStory = { +export let ZonedDateTimeType = { args: {label: 'Test', value: new ZonedDateTime(2020, 2, 3, 'America/Los_Angeles', -28800000)}, name: 'ZonedDateTime' }; -export let DateType: LabeledValueStory = { +export let DateType = { args: {label: 'Test', value: new Date(2000, 5, 5)}, name: 'Date' }; -export let TimeType: LabeledValueStory = { +export let TimeType = { args: {label: 'Test', value: new Time(9, 45)}, name: 'Time' }; -export let CalendarDateRange: LabeledValueStory = { +export let CalendarDateRange = { args: {label: 'Test', value: {start: new CalendarDate(2019, 6, 5), end: new CalendarDate(2019, 7, 5)}}, name: 'RangeValue' }; -export let CalendarDateTimeRange: LabeledValueStory = { +export let CalendarDateTimeRange = { args: {label: 'Test', value: {start: new CalendarDateTime(2020, 2, 3, 12, 23, 24, 120), end: new CalendarDateTime(2020, 3, 3, 12, 23, 24, 120)}}, name: 'RangeValue' }; -export let ZonedDateTimeRange: LabeledValueStory = { +export let ZonedDateTimeRange = { args: {label: 'Test', value: {start: new ZonedDateTime(2020, 2, 3, 'America/Los_Angeles', -28800000), end: new ZonedDateTime(2020, 3, 3, 'America/Los_Angeles', -28800000)}}, name: 'RangeValue' }; -export let DateRange: LabeledValueStory = { +export let DateRange = { args: {label: 'Test', value: {start: new Date(2019, 6, 5), end: new Date(2019, 6, 10)}}, name: 'RangeValue' }; -export let TimeRange: LabeledValueStory = { +export let TimeRange = { args: {label: 'Test', value: {start: new Time(9, 45), end: new Time(10, 50)}}, name: 'RangeValue