diff --git a/packages/react-components/react-field/etc/react-field.api.md b/packages/react-components/react-field/etc/react-field.api.md
index 4833ea08b380e..dfc30e5b686c2 100644
--- a/packages/react-components/react-field/etc/react-field.api.md
+++ b/packages/react-components/react-field/etc/react-field.api.md
@@ -4,9 +4,16 @@
```ts
+///
+
import type { ComponentProps } from '@fluentui/react-utilities';
import type { ComponentState } from '@fluentui/react-utilities';
+import { ContextSelector } from '@fluentui/react-context-selector';
+import { FC } from 'react';
import type { ForwardRefComponent } from '@fluentui/react-utilities';
+import { Label } from '@fluentui/react-label';
+import { Provider } from 'react';
+import { ProviderProps } from 'react';
import * as React_2 from 'react';
import type { Slot } from '@fluentui/react-utilities';
import type { SlotClassNames } from '@fluentui/react-utilities';
@@ -15,30 +22,63 @@ import type { SlotClassNames } from '@fluentui/react-utilities';
export const Field: ForwardRefComponent;
// @public (undocumented)
-export const fieldClassName = "fui-Field";
+export const fieldClassNames: SlotClassNames;
// @public (undocumented)
-export const fieldClassNames: SlotClassNames;
+export type FieldContextValue = Readonly>;
+
+// @public (undocumented)
+export type FieldContextValues = {
+ field: FieldContextValue;
+};
// @public
-export type FieldProps = ComponentProps & {};
+export type FieldProps = Omit>, 'children'> & {
+ children: React_2.ReactElement<{
+ id?: string;
+ }>;
+ labelPosition?: 'above' | 'before';
+ required?: boolean;
+ size?: 'small' | 'medium' | 'large';
+ status?: 'error' | 'warning' | 'success';
+ htmlFor?: string;
+};
+
+// @public (undocumented)
+export const FieldProvider: Provider> | undefined> & FC> | undefined>>;
// @public (undocumented)
export type FieldSlots = {
- root: Slot<'div'>;
+ root: NonNullable>;
+ label?: Slot;
+ statusText?: Slot<'span'>;
+ statusIcon?: Slot<'span'>;
+ helperText?: Slot<'span'>;
};
// @public
-export type FieldState = ComponentState;
+export type FieldState = ComponentState> & Pick & Required> & {
+ generatedChildId: string | undefined;
+ labelId: string | undefined;
+};
+
+// @public (undocumented)
+export const filterFieldSize: (size: FieldProps['size'], supportedSizes: SupportedSizes[]) => SupportedSizes | undefined;
// @public
-export const renderField_unstable: (state: FieldState) => JSX.Element;
+export const renderField_unstable: (state: FieldState, contextValues: FieldContextValues) => JSX.Element;
// @public
-export const useField_unstable: (props: FieldProps, ref: React_2.Ref) => FieldState;
+export const useField_unstable: (props: FieldProps, ref: React_2.Ref) => FieldState;
+
+// @public (undocumented)
+export const useFieldContext: (selector: ContextSelector> | undefined, T>) => T;
+
+// @public (undocumented)
+export const useFieldContextValues: (state: FieldState) => FieldContextValues;
// @public
-export const useFieldStyles_unstable: (state: FieldState) => FieldState;
+export const useFieldStyles_unstable: (state: FieldState) => void;
// (No @packageDocumentation comment for this package)
diff --git a/packages/react-components/react-field/package.json b/packages/react-components/react-field/package.json
index 3a0b6f8773d85..c6e239b6900b4 100644
--- a/packages/react-components/react-field/package.json
+++ b/packages/react-components/react-field/package.json
@@ -32,6 +32,9 @@
"@fluentui/scripts": "^1.0.0"
},
"dependencies": {
+ "@fluentui/react-context-selector": "^9.0.2",
+ "@fluentui/react-icons": "^2.0.175",
+ "@fluentui/react-label": "^9.0.4",
"@fluentui/react-theme": "^9.0.0",
"@fluentui/react-utilities": "^9.0.2",
"@griffel/react": "^1.3.0",
diff --git a/packages/react-components/react-field/src/components/Field/Field.test.tsx b/packages/react-components/react-field/src/components/Field/Field.test.tsx
index 7f09ee09b58c6..954f0e646a003 100644
--- a/packages/react-components/react-field/src/components/Field/Field.test.tsx
+++ b/packages/react-components/react-field/src/components/Field/Field.test.tsx
@@ -2,17 +2,44 @@ import * as React from 'react';
import { render } from '@testing-library/react';
import { Field } from './Field';
import { isConformant } from '../../common/isConformant';
+import { fieldClassNames } from './useFieldStyles';
describe('Field', () => {
isConformant({
Component: Field,
displayName: 'Field',
+ requiredProps: {
+ children: ,
+ },
+ testOptions: {
+ 'has-static-classnames': [
+ {
+ props: {
+ label: 'Label',
+ status: 'error',
+ statusText: 'Status text',
+ helperText: 'Helper text',
+ },
+ expectedClassNames: {
+ root: fieldClassNames.root,
+ label: fieldClassNames.label,
+ statusText: fieldClassNames.statusText,
+ statusIcon: fieldClassNames.statusIcon,
+ helperText: fieldClassNames.helperText,
+ },
+ },
+ ],
+ },
});
// TODO add more tests here, and create visual regression tests in /apps/vr-tests
it('renders a default state', () => {
- const result = render(Default Field);
+ const result = render(
+
+
+ ,
+ );
expect(result.container).toMatchSnapshot();
});
});
diff --git a/packages/react-components/react-field/src/components/Field/Field.tsx b/packages/react-components/react-field/src/components/Field/Field.tsx
index 46d8d9cfc9629..f6086ac44c7c8 100644
--- a/packages/react-components/react-field/src/components/Field/Field.tsx
+++ b/packages/react-components/react-field/src/components/Field/Field.tsx
@@ -1,18 +1,20 @@
import * as React from 'react';
+import type { ForwardRefComponent } from '@fluentui/react-utilities';
+import type { FieldProps } from './Field.types';
import { useField_unstable } from './useField';
import { renderField_unstable } from './renderField';
import { useFieldStyles_unstable } from './useFieldStyles';
-import type { FieldProps } from './Field.types';
-import type { ForwardRefComponent } from '@fluentui/react-utilities';
+import { useFieldContextValues } from '../../contexts/useFieldContextValues';
/**
* Field component - TODO: add more docs
*/
export const Field: ForwardRefComponent = React.forwardRef((props, ref) => {
const state = useField_unstable(props, ref);
+ const contextValues = useFieldContextValues(state);
useFieldStyles_unstable(state);
- return renderField_unstable(state);
+ return renderField_unstable(state, contextValues);
});
Field.displayName = 'Field';
diff --git a/packages/react-components/react-field/src/components/Field/Field.types.ts b/packages/react-components/react-field/src/components/Field/Field.types.ts
index 949bf781eb4ff..6b49a3d4395c2 100644
--- a/packages/react-components/react-field/src/components/Field/Field.types.ts
+++ b/packages/react-components/react-field/src/components/Field/Field.types.ts
@@ -1,17 +1,112 @@
+import { Label } from '@fluentui/react-label';
import type { ComponentProps, ComponentState, Slot } from '@fluentui/react-utilities';
+import * as React from 'react';
export type FieldSlots = {
- root: Slot<'div'>;
+ root: NonNullable>;
+
+ /**
+ * The label associated with the field.
+ */
+ label?: Slot;
+
+ /**
+ * A status or validation message. The appearance of the statusText depends on the value of the `status` prop.
+ */
+ statusText?: Slot<'span'>;
+
+ /**
+ * The icon associated with the status. If the `status` prop is set, this will default to a corresponding icon.
+ *
+ * This will only be displayed if `statusText` is set.
+ */
+ statusIcon?: Slot<'span'>;
+
+ /**
+ * Additional text below the field.
+ */
+ helperText?: Slot<'span'>;
};
/**
* Field Props
*/
-export type FieldProps = ComponentProps & {};
+export type FieldProps = Omit>, 'children'> & {
+ /**
+ * Field must have exactly one child that is a form component.
+ */
+ children: React.ReactElement<{ id?: string }>;
+
+ /**
+ * The position of the label relative to the field. This only affects the label, and not the statusText or helperText
+ * (which always appear below the field).
+ *
+ * @default above
+ */
+ labelPosition?: 'above' | 'before';
+
+ /**
+ * Marks the field as required, and adds required styling to the label (red asterisk).
+ *
+ * @default false
+ */
+ required?: boolean;
+
+ /**
+ * Size of the field and label.
+ *
+ * NOTE: Not all components support all available sizes. Check the documentation of the component to see what values
+ * it supports for its `size` prop.
+ *
+ * @default medium
+ */
+ size?: 'small' | 'medium' | 'large';
+
+ /**
+ * The status affects the color of the statusText, the statusIcon, and for some field components, an error status
+ * causes the border to become red.
+ *
+ * @default undefined
+ */
+ status?: 'error' | 'warning' | 'success';
+
+ /**
+ * The ID of the form component in this Field (the child of the Field).
+ *
+ * `htmlFor` will default to the `id` property of the Field's child, if set.
+ * Otherwise, it will default to `generatedChildId` on the FieldContext.
+ *
+ * In most cases, it isn't necessary to set this property. It only needs to be set if the child component
+ * doesn't assign its ID via an `id` prop, and doesn't use FieldContext (such as a component from another library).
+ */
+ htmlFor?: string;
+};
/**
* State used in rendering Field
*/
-export type FieldState = ComponentState;
-// TODO: Remove semicolon from previous line, uncomment next line, and provide union of props to pick from FieldProps.
-// & Required>
+export type FieldState = ComponentState> &
+ Pick &
+ Required> & {
+ /**
+ * The generated ID used as the label's htmlFor prop.
+ *
+ * This will be undefined if either (a) the Field's htmlFor was set, or (b) the child has an id property set.
+ */
+ generatedChildId: string | undefined;
+
+ /**
+ * The (generated) ID of the field's label component.
+ *
+ * This can be used as the `aria-labelledby` prop when the label's htmlFor doesn't work (such as RadioGroup).
+ */
+ labelId: string | undefined;
+ };
+
+export type FieldContextValue = Readonly<
+ Pick
+>;
+
+export type FieldContextValues = {
+ field: FieldContextValue;
+};
diff --git a/packages/react-components/react-field/src/components/Field/__snapshots__/Field.test.tsx.snap b/packages/react-components/react-field/src/components/Field/__snapshots__/Field.test.tsx.snap
index 7126776f33687..72f05814ab3c6 100644
--- a/packages/react-components/react-field/src/components/Field/__snapshots__/Field.test.tsx.snap
+++ b/packages/react-components/react-field/src/components/Field/__snapshots__/Field.test.tsx.snap
@@ -5,7 +5,7 @@ exports[`Field renders a default state 1`] = `
- Default Field
+
`;
diff --git a/packages/react-components/react-field/src/components/Field/renderField.tsx b/packages/react-components/react-field/src/components/Field/renderField.tsx
index 858515b2b4fbb..687605d0ab339 100644
--- a/packages/react-components/react-field/src/components/Field/renderField.tsx
+++ b/packages/react-components/react-field/src/components/Field/renderField.tsx
@@ -1,13 +1,27 @@
import * as React from 'react';
import { getSlots } from '@fluentui/react-utilities';
-import type { FieldState, FieldSlots } from './Field.types';
+import type { FieldState, FieldSlots, FieldContextValues } from './Field.types';
+import { FieldContext } from '../../contexts/FieldContext';
/**
* Render the final JSX of Field
*/
-export const renderField_unstable = (state: FieldState) => {
+export const renderField_unstable = (state: FieldState, contextValues: FieldContextValues) => {
const { slots, slotProps } = getSlots(state);
- // TODO Add additional slots in the appropriate place
- return ;
+ return (
+
+
+ {slots.label && }
+ {slotProps.root.children}
+ {slots.statusText && (
+
+ {slots.statusIcon && }
+ {slotProps.statusText.children}
+
+ )}
+ {slots.helperText && }
+
+
+ );
};
diff --git a/packages/react-components/react-field/src/components/Field/useField.ts b/packages/react-components/react-field/src/components/Field/useField.ts
deleted file mode 100644
index f5f26387f6def..0000000000000
--- a/packages/react-components/react-field/src/components/Field/useField.ts
+++ /dev/null
@@ -1,28 +0,0 @@
-import * as React from 'react';
-import { getNativeElementProps } from '@fluentui/react-utilities';
-import type { FieldProps, FieldState } from './Field.types';
-
-/**
- * Create the state required to render Field.
- *
- * The returned state can be modified with hooks such as useFieldStyles_unstable,
- * before being passed to renderField_unstable.
- *
- * @param props - props from this instance of Field
- * @param ref - reference to root HTMLElement of Field
- */
-export const useField_unstable = (props: FieldProps, ref: React.Ref): FieldState => {
- return {
- // TODO add appropriate props/defaults
- components: {
- // TODO add each slot's element type or component
- root: 'div',
- },
- // TODO add appropriate slots, for example:
- // mySlot: resolveShorthand(props.mySlot),
- root: getNativeElementProps('div', {
- ref,
- ...props,
- }),
- };
-};
diff --git a/packages/react-components/react-field/src/components/Field/useField.tsx b/packages/react-components/react-field/src/components/Field/useField.tsx
new file mode 100644
index 0000000000000..ad29f22d83793
--- /dev/null
+++ b/packages/react-components/react-field/src/components/Field/useField.tsx
@@ -0,0 +1,108 @@
+import * as React from 'react';
+import type { FieldProps, FieldState } from './Field.types';
+import { CheckmarkCircle12Filled, ErrorCircle12Filled, Warning12Filled } from '@fluentui/react-icons';
+import { Label } from '@fluentui/react-label';
+import { getNativeElementProps, resolveShorthand, useId } from '@fluentui/react-utilities';
+
+/**
+ * Create the state required to render Field.
+ *
+ * The returned state can be modified with hooks such as useFieldStyles_unstable,
+ * before being passed to renderField_unstable.
+ *
+ * @param props - props from this instance of Field
+ * @param ref - reference to root HTMLElement of Field
+ */
+export const useField_unstable = (props: FieldProps, ref: React.Ref): FieldState => {
+ const generatedChildId = useId('field__input-');
+ const child = React.Children.only(props.children);
+
+ const {
+ htmlFor = child?.props?.id || generatedChildId,
+ labelPosition = 'above',
+ required,
+ size = 'medium',
+ status,
+ } = props;
+
+ const label = resolveShorthand(props.label, {
+ defaultProps: {
+ id: useId('field__label-'),
+ htmlFor,
+ required,
+ size,
+ },
+ });
+
+ // TODO https://github.com/microsoft/fluentui/issues/24236: re-enable check once FieldContext is hooked up
+ // if (process.env.NODE_ENV !== 'production') {
+ // // eslint-disable-next-line react-hooks/rules-of-hooks
+ // ref = useMergedRefs(ref, useLabelDebugCheck(label));
+ // }
+
+ let defaultStatusIcon;
+ if (status === 'error') {
+ defaultStatusIcon = ;
+ } else if (status === 'warning') {
+ defaultStatusIcon = ;
+ } else if (status === 'success') {
+ defaultStatusIcon = ;
+ }
+
+ return {
+ // Only pass the generatedChildId if it is the one that was used for the htmlFor prop
+ generatedChildId: htmlFor === generatedChildId ? generatedChildId : undefined,
+ labelId: label?.id,
+ labelPosition,
+ required,
+ size,
+ status,
+ components: {
+ root: 'div',
+ label: Label,
+ statusText: 'span',
+ statusIcon: 'span',
+ helperText: 'span',
+ },
+ root: getNativeElementProps('div', {
+ ...props,
+ ref,
+ }),
+ label,
+ statusIcon: resolveShorthand(props.statusIcon, {
+ required: !!defaultStatusIcon,
+ defaultProps: {
+ children: defaultStatusIcon,
+ },
+ }),
+ statusText: resolveShorthand(props.statusText),
+ helperText: resolveShorthand(props.helperText),
+ };
+};
+
+// TODO https://github.com/microsoft/fluentui/issues/24236: re-enable check once FieldContext is hooked up
+// const useLabelDebugCheck = (label: FieldState['label']) => {
+// const labelFor = label?.htmlFor;
+// const labelId = label?.id;
+// const labelText = label?.children;
+// const rootRef = React.useRef(null);
+
+// React.useEffect(() => {
+// if (!rootRef.current || !labelText || !labelFor || !labelId) {
+// return;
+// }
+
+// if (!rootRef.current.querySelector(`[id='${labelFor}'], [aria-labelledby='${labelId}']`)) {
+// // eslint-disable-next-line no-console
+// console.error(
+// `Field with label "${labelText}" requires the label be associated with its input. Try one of these fixes:\n`+
+// '1. Use a control that sets its ID from FieldContext.generatedChildId (form controls in this library).\n' +
+// '2. Or, set the `id` prop of the child of field.\n' +
+// '3. Or, set `htmlFor` to the ID used by the field component.\n' +
+// '4. Or, set `label={{ id: ... }}` to the `aria-labelledby` prop of the field component.\n',
+// );
+// }
+// }, [labelFor, labelId, labelText]);
+
+// return rootRef;
+// };
diff --git a/packages/react-components/react-field/src/components/Field/useFieldStyles.ts b/packages/react-components/react-field/src/components/Field/useFieldStyles.ts
index 9efd955895889..0bb12776453e3 100644
--- a/packages/react-components/react-field/src/components/Field/useFieldStyles.ts
+++ b/packages/react-components/react-field/src/components/Field/useFieldStyles.ts
@@ -1,34 +1,128 @@
import { makeStyles, mergeClasses } from '@griffel/react';
import type { FieldSlots, FieldState } from './Field.types';
import type { SlotClassNames } from '@fluentui/react-utilities';
+import { tokens, typographyStyles } from '@fluentui/react-theme';
-export const fieldClassName = 'fui-Field';
export const fieldClassNames: SlotClassNames = {
root: 'fui-Field',
- // TODO: add class names for all slots on FieldSlots.
- // Should be of the form `: 'fui-Field__`
+ label: 'fui-Field__label',
+ statusText: 'fui-Field__statusText',
+ statusIcon: 'fui-Field__statusIcon',
+ helperText: 'fui-Field__helperText',
};
/**
* Styles for the root slot
*/
-const useStyles = makeStyles({
- root: {
- // TODO Add default styles for the root element
+const useRootStyles = makeStyles({
+ base: {
+ display: 'inline-grid',
+ gridAutoFlow: 'row',
+ justifyItems: 'start',
},
- // TODO add additional classes for different states and/or slots
+ labelBefore: {
+ gridTemplateRows: 'repeat(4, auto)',
+ gridTemplateColumns: '1fr 2fr',
+ [`> :not(.${fieldClassNames.label})`]: {
+ gridColumnStart: '2',
+ },
+ },
+});
+
+const useLabelStyles = makeStyles({
+ base: {
+ marginTop: tokens.spacingVerticalXXS,
+ marginBottom: tokens.spacingVerticalXXS,
+ },
+
+ before: {
+ gridRowStart: '1',
+ gridRowEnd: '-1',
+ marginRight: tokens.spacingHorizontalM,
+ alignSelf: 'start',
+ justifySelf: 'stretch',
+ },
+});
+
+const useSecondaryTextStyles = makeStyles({
+ base: {
+ marginTop: tokens.spacingVerticalXXS,
+ color: tokens.colorNeutralForeground3,
+ ...typographyStyles.caption1,
+ },
+
+ error: {
+ color: tokens.colorPaletteRedForeground1,
+ },
+});
+
+const useStatusIconStyles = makeStyles({
+ base: {
+ fontSize: '12px',
+ lineHeight: '12px',
+ verticalAlign: 'middle',
+ marginRight: tokens.spacingHorizontalXS,
+ },
+
+ error: {
+ color: tokens.colorPaletteRedForeground1,
+ },
+ warning: {
+ color: tokens.colorPaletteDarkOrangeForeground1,
+ },
+ success: {
+ color: tokens.colorPaletteGreenForeground1,
+ },
});
/**
* Apply styling to the Field slots based on the state
*/
-export const useFieldStyles_unstable = (state: FieldState): FieldState => {
- const styles = useStyles();
- state.root.className = mergeClasses(fieldClassName, styles.root, state.root.className);
+export const useFieldStyles_unstable = (state: FieldState) => {
+ const rootStyles = useRootStyles();
+ state.root.className = mergeClasses(
+ fieldClassNames.root,
+ rootStyles.base,
+ state.labelPosition === 'before' && rootStyles.labelBefore,
+ state.root.className,
+ );
+
+ const labelStyles = useLabelStyles();
+ if (state.label) {
+ state.label.className = mergeClasses(
+ fieldClassNames.label,
+ labelStyles.base,
+ state.labelPosition === 'before' && labelStyles.before,
+ state.label.className,
+ );
+ }
+
+ const statusIconStyles = useStatusIconStyles();
+ if (state.statusIcon) {
+ state.statusIcon.className = mergeClasses(
+ fieldClassNames.statusIcon,
+ statusIconStyles.base,
+ !!state.status && statusIconStyles[state.status],
+ state.statusIcon.className,
+ );
+ }
- // TODO Add class names to slots, for example:
- // state.mySlot.className = mergeClasses(styles.mySlot, state.mySlot.className);
+ const secondaryTextStyles = useSecondaryTextStyles();
+ if (state.statusText) {
+ state.statusText.className = mergeClasses(
+ fieldClassNames.statusText,
+ secondaryTextStyles.base,
+ state.status === 'error' && secondaryTextStyles.error,
+ state.statusText.className,
+ );
+ }
- return state;
+ if (state.helperText) {
+ state.helperText.className = mergeClasses(
+ fieldClassNames.helperText,
+ secondaryTextStyles.base,
+ state.helperText.className,
+ );
+ }
};
diff --git a/packages/react-components/react-field/src/contexts/FieldContext.ts b/packages/react-components/react-field/src/contexts/FieldContext.ts
new file mode 100644
index 0000000000000..e76e386ab09bb
--- /dev/null
+++ b/packages/react-components/react-field/src/contexts/FieldContext.ts
@@ -0,0 +1,10 @@
+import { createContext, useContextSelector, ContextSelector } from '@fluentui/react-context-selector';
+import type { FieldContextValue } from '../Field';
+
+export const FieldContext = createContext(undefined);
+
+export const FieldProvider = FieldContext.Provider;
+
+export const useFieldContext = (selector: ContextSelector): T => {
+ return useContextSelector(FieldContext, selector);
+};
diff --git a/packages/react-components/react-field/src/contexts/index.ts b/packages/react-components/react-field/src/contexts/index.ts
new file mode 100644
index 0000000000000..c32893bd9b267
--- /dev/null
+++ b/packages/react-components/react-field/src/contexts/index.ts
@@ -0,0 +1,3 @@
+export * from './FieldContext';
+export * from './useFieldContextValues';
+// export * from './useFieldChildProps';
diff --git a/packages/react-components/react-field/src/contexts/useFieldContextValues.ts b/packages/react-components/react-field/src/contexts/useFieldContextValues.ts
new file mode 100644
index 0000000000000..27fe2d5cfd680
--- /dev/null
+++ b/packages/react-components/react-field/src/contexts/useFieldContextValues.ts
@@ -0,0 +1,8 @@
+import type { FieldContextValues, FieldState } from '../Field';
+
+export const useFieldContextValues = (state: FieldState): FieldContextValues => {
+ const { size, required, status, labelId, generatedChildId } = state;
+ return {
+ field: { size, required, status, labelId, generatedChildId },
+ };
+};
diff --git a/packages/react-components/react-field/src/index.ts b/packages/react-components/react-field/src/index.ts
index f40e2c50f7b06..7c94ca46daa30 100644
--- a/packages/react-components/react-field/src/index.ts
+++ b/packages/react-components/react-field/src/index.ts
@@ -1,3 +1,4 @@
-// TODO: replace with real exports
-export {};
-export * from './Field';
+export { Field, fieldClassNames, renderField_unstable, useFieldStyles_unstable, useField_unstable } from './Field';
+export type { FieldContextValue, FieldContextValues, FieldProps, FieldSlots, FieldState } from './Field';
+export { FieldProvider, useFieldContextValues, useFieldContext } from './contexts/index';
+export { filterFieldSize } from './util/index';
diff --git a/packages/react-components/react-field/src/stories/Field/FieldDefault.stories.tsx b/packages/react-components/react-field/src/stories/Field/FieldDefault.stories.tsx
index b8a93a1dfb9f6..810a477a60eac 100644
--- a/packages/react-components/react-field/src/stories/Field/FieldDefault.stories.tsx
+++ b/packages/react-components/react-field/src/stories/Field/FieldDefault.stories.tsx
@@ -1,4 +1,15 @@
import * as React from 'react';
+import { Input } from '@fluentui/react-components';
import { Field, FieldProps } from '@fluentui/react-field';
-export const Default = (props: Partial) => ;
+export const Default = (props: Partial) => (
+
+
+
+);
diff --git a/packages/react-components/react-field/src/stories/Field/FieldDescription.md b/packages/react-components/react-field/src/stories/Field/FieldDescription.md
index e69de29bb2d1d..28d53aa2c4c10 100644
--- a/packages/react-components/react-field/src/stories/Field/FieldDescription.md
+++ b/packages/react-components/react-field/src/stories/Field/FieldDescription.md
@@ -0,0 +1,3 @@
+Field is a combination of Label, a form component such as Input or Combobox, as well as status and helper text.
+
+Field does not handle input validation, but it does allow a validation status message to be displayed.
diff --git a/packages/react-components/react-field/src/stories/Field/FieldExamples.stories.tsx b/packages/react-components/react-field/src/stories/Field/FieldExamples.stories.tsx
new file mode 100644
index 0000000000000..8eaf58c296d24
--- /dev/null
+++ b/packages/react-components/react-field/src/stories/Field/FieldExamples.stories.tsx
@@ -0,0 +1,73 @@
+import * as React from 'react';
+import {
+ Checkbox,
+ Input,
+ makeStyles,
+ Radio,
+ RadioGroup,
+ Slider,
+ SpinButton,
+ Switch,
+ tokens,
+} from '@fluentui/react-components';
+import { Combobox, Option } from '@fluentui/react-combobox';
+import { Field } from '@fluentui/react-field';
+
+const useStyles = makeStyles({
+ stack: {
+ display: 'inline-grid',
+ rowGap: tokens.spacingVerticalM,
+ width: '480px',
+ },
+});
+
+export const Examples = () => {
+ const styles = useStyles();
+ const [radioValue, setRadioValue] = React.useState('one');
+ return (
+