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