Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 9 additions & 0 deletions packages/@react-spectrum/s2/chromatic/ColorField.stories.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@
*/

import {ColorField, ColorFieldProps} from '../src/ColorField';
import {ColorSwatch} from '../exports';
import {generateComboChunks, shortName} from './utils';
import type {Meta, StoryObj} from '@storybook/react';
import {ReactElement} from 'react';
Expand Down Expand Up @@ -144,3 +145,11 @@ export const LabelPositionTopPt5: StoryObj<typeof Template> = {
};

// Skipped the contextual help stories from here on out since its all shared

export const ExampleWithPrefix: StoryObj<typeof Template> = {
render: args => <ColorField {...args} value="#ff00ff" />,
args: {
label: 'Color',
prefix: <ColorSwatch size="XS" color="#ff00ff" />
}
};
8 changes: 8 additions & 0 deletions packages/@react-spectrum/s2/chromatic/Combobox.stories.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ import {
WithAvatars,
WithIcons
} from '../stories/ComboBox.stories';
import {Avatar} from '../exports';
import {ComboBox} from '../src/ComboBox';
import {expect} from '@storybook/jest';
import type {Meta, StoryObj} from '@storybook/react';
Expand Down Expand Up @@ -192,3 +193,10 @@ export const Filtering: StoryObj<AsyncComboBoxStoryType> = {
);
}
};

export const WithPrefix: Story = {
...Example,
args: {
prefix: <Avatar size={20} src="https://i.imgur.com/xIe7Wlb.png" />
}
};
10 changes: 10 additions & 0 deletions packages/@react-spectrum/s2/chromatic/NumberField.stories.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -111,3 +111,13 @@ export const ContextualHelpExample: Story = {
)
}
};

export const ExampleWithPrefix: Story = {
render: args => <NumberField {...args} />,
args: {
label: 'Price',
prefix: 'USD',
formatOptions: {style: 'currency', currency: 'USD', currencyDisplay: 'narrowSymbol'},
placeholder: '0.00'
}
};
6 changes: 5 additions & 1 deletion packages/@react-spectrum/s2/src/CenterBaseline.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -12,11 +12,13 @@

import {css} from '../style/style-macro' with {type: 'macro'};
import {CSSProperties, ReactNode} from 'react';
import {DOMAttributes} from '@react-types/shared';
import {filterDOMProps} from 'react-aria/filterDOMProps';
import {mergeStyles} from '../style/runtime';
import {style} from '../style' with {type: 'macro'};
import {StyleString} from '../style/types';

interface CenterBaselineProps {
interface CenterBaselineProps extends DOMAttributes {
style?: CSSProperties;
styles?: StyleString;
children: ReactNode;
Expand All @@ -29,8 +31,10 @@ const styles = style({
});

export function CenterBaseline(props: CenterBaselineProps): ReactNode {
let domProps = filterDOMProps(props);
return (
<div
{...domProps}
slot={props.slot}
style={props.style}
className={mergeStyles(styles, props.styles) + ' ' + centerBaselineBefore}>
Expand Down
33 changes: 28 additions & 5 deletions packages/@react-spectrum/s2/src/ColorField.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -14,15 +14,23 @@ import {
ColorField as AriaColorField,
ColorFieldProps as AriaColorFieldProps
} from 'react-aria-components/ColorField';

import {ContextValue} from 'react-aria-components/slots';
import {createContext, forwardRef, Ref, useContext, useImperativeHandle, useRef} from 'react';
import {
createContext,
forwardRef,
ReactNode,
Ref,
useContext,
useImperativeHandle,
useRef
} from 'react';
import {createFocusableRef} from './useDOMRef';
import {field, getAllowedOverrides, StyleProps} from './style-utils' with {type: 'macro'};
import {FieldErrorIcon, FieldGroup, FieldLabel, HelpText, Input} from './Field';
import {FormContext, useFormProps} from './Form';
import {GlobalDOMAttributes, HelpTextProps, SpectrumLabelableProps} from '@react-types/shared';
import {InputProps} from 'react-aria-components/Input';
import {InputContext, InputProps} from 'react-aria-components/Input';
import {mergeRefs} from 'react-aria/mergeRefs';
import {style} from '../style' with {type: 'macro'};
import {TextFieldRef} from './TextField';
import {useSpectrumContextProps} from './useSpectrumContextProps';
Expand All @@ -43,6 +51,11 @@ export interface ColorFieldProps
* @default 'M'
*/
size?: 'S' | 'M' | 'L' | 'XL';
/**
* The prefix to display in the ColorField. A non-interactive element that appears before the
* input.
*/
prefix?: ReactNode;
}

export const ColorFieldContext =
Expand Down Expand Up @@ -114,8 +127,18 @@ export const ColorField = forwardRef(function ColorField(
contextualHelp={props.contextualHelp}>
{label}
</FieldLabel>
<FieldGroup size={props.size}>
<Input ref={inputRef} />
<FieldGroup prefix={props.prefix} size={props.size}>
<InputContext.Consumer>
{ctx => (
<InputContext.Provider
value={{
...ctx,
ref: mergeRefs((ctx as any)?.ref, inputRef)
}}>
<Input />
</InputContext.Provider>
)}
</InputContext.Consumer>
{isInvalid && <FieldErrorIcon isDisabled={isDisabled} />}
</FieldGroup>
<HelpText
Expand Down
11 changes: 10 additions & 1 deletion packages/@react-spectrum/s2/src/ComboBox.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -99,6 +99,10 @@ export interface ComboboxStyleProps {
* @default 'M'
*/
size?: 'S' | 'M' | 'L' | 'XL';
/**
* The prefix to display in the ComboBox. A non-interactive element that appears before the input.
*/
prefix?: ReactNode;
}
export interface ComboBoxProps<T extends object>
extends
Expand Down Expand Up @@ -702,6 +706,7 @@ const ComboboxInner = forwardRef(function ComboboxInner(
{label}
</FieldLabel>
<FieldGroup
prefix={props.prefix}
role="presentation"
isDisabled={isDisabled}
isInvalid={isInvalid}
Expand All @@ -715,7 +720,11 @@ const ComboboxInner = forwardRef(function ComboboxInner(
})({size})}>
<InputContext.Consumer>
{ctx => (
<InputContext.Provider value={{...ctx, ref: mergeRefs((ctx as any)?.ref, inputRef)}}>
<InputContext.Provider
value={{
...ctx,
ref: mergeRefs((ctx as any)?.ref, inputRef)
}}>
<Input aria-describedby={spinnerId} />
</InputContext.Provider>
)}
Expand Down
55 changes: 49 additions & 6 deletions packages/@react-spectrum/s2/src/Field.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -26,15 +26,19 @@ import {
UnsafeStyles
} from './style-utils' with {type: 'macro'};
import {FieldError, FieldErrorProps} from 'react-aria-components/FieldError';
import {ForwardedRef, forwardRef, ReactNode} from 'react';
import {ForwardedRef, forwardRef, ReactNode, useContext} from 'react';
import {getEventTarget} from 'react-aria/private/utils/shadowdom/DOMFunctions';
import {Group, GroupProps} from 'react-aria-components/Group';
import {IconContext} from './Icon';
import {
InputContext,
Input as RACInput,
InputProps as RACInputProps
} from 'react-aria-components/Input';
import intlMessages from '../intl/*.json';
import {Label, LabelProps} from 'react-aria-components/Label';
import {mergeStyles} from '../style/runtime';
import {Provider} from 'react-aria-components/slots';
import {Input as RACInput, InputProps as RACInputProps} from 'react-aria-components/Input';
// @ts-ignore
import {StyleString} from '../style/types';
import {Text} from 'react-aria-components/Text';
Expand Down Expand Up @@ -187,11 +191,12 @@ export const FieldLabel = forwardRef(function FieldLabel(
});

interface FieldGroupProps
extends Omit<GroupProps, 'className' | 'style' | 'render' | 'children'>, UnsafeStyles {
extends Omit<GroupProps, 'className' | 'style' | 'render' | 'children' | 'prefix'>, UnsafeStyles {
size?: 'S' | 'M' | 'L' | 'XL';
children: ReactNode;
styles?: StyleString;
shouldTurnOffFocusRing?: boolean;
prefix?: ReactNode;
}

const fieldGroupStyles = style({
Expand Down Expand Up @@ -240,7 +245,13 @@ export const FieldGroup = forwardRef(function FieldGroup(
props: FieldGroupProps,
ref: ForwardedRef<HTMLDivElement>
) {
let {shouldTurnOffFocusRing, ...otherProps} = props;
let {children, prefix, shouldTurnOffFocusRing, ...otherProps} = props;
let ctx = useContext(InputContext);
let prefixId = useId();
let newAriaLabelledby = ctx?.['aria-labelledby'];
if (prefix) {
newAriaLabelledby = newAriaLabelledby ? `${newAriaLabelledby} ${prefixId}` : prefixId;
}
return (
<Group
ref={ref}
Expand Down Expand Up @@ -276,8 +287,40 @@ export const FieldGroup = forwardRef(function FieldGroup(
}),
props.styles
)
}
/>
}>
{props.prefix ? (
<Provider
values={[
[
IconContext,
{
styles: style({
size: fontRelative(20),
'--iconPrimary': {type: 'fill', value: 'currentColor'}
})
}
]
]}>
<CenterBaseline
id={prefixId}
styles={style({
minWidth: 20,
color: 'gray-600',
flexShrink: 0,
marginEnd: 'text-to-visual'
})}>
{props.prefix}
</CenterBaseline>
</Provider>
) : null}
<InputContext.Consumer>
{ctx => (
<InputContext.Provider value={{...ctx, 'aria-labelledby': newAriaLabelledby}}>
{children}
</InputContext.Provider>
)}
</InputContext.Consumer>
</Group>
);
});

Expand Down
11 changes: 10 additions & 1 deletion packages/@react-spectrum/s2/src/NumberField.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -79,6 +79,11 @@ export interface NumberFieldProps
* @default 'M'
*/
size?: 'S' | 'M' | 'L' | 'XL';
/**
* The prefix to display in the NumberField. A non-interactive element that appears before the
* input.
*/
prefix?: ReactNode;
}

export const NumberFieldContext =
Expand Down Expand Up @@ -239,6 +244,7 @@ export const NumberField = forwardRef(function NumberField(
{label}
</FieldLabel>
<FieldGroup
prefix={props.prefix}
size={size}
styles={style({
...fieldInput(),
Expand All @@ -251,7 +257,10 @@ export const NumberField = forwardRef(function NumberField(
<InputContext.Consumer>
{ctx => (
<InputContext.Provider
value={{...ctx, ref: mergeRefs((ctx as any)?.ref, inputRef)}}>
value={{
...ctx,
ref: mergeRefs((ctx as any)?.ref, inputRef)
}}>
<Input />
</InputContext.Provider>
)}
Expand Down
62 changes: 10 additions & 52 deletions packages/@react-spectrum/s2/src/TextField.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -14,15 +14,13 @@ import {
TextArea as AriaTextArea,
TextAreaContext as AriaTextAreaContext
} from 'react-aria-components/TextArea';
import {TextContext as AriaTextContext} from 'react-aria-components/Text';
import {
TextField as AriaTextField,
TextFieldProps as AriaTextFieldProps
} from 'react-aria-components/TextField';
import {centerBaseline} from './CenterBaseline';
import {centerPadding, fontRelative, style} from '../style' with {type: 'macro'};
import {centerPadding, style} from '../style' with {type: 'macro'};
import {composeRenderProps} from 'react-aria-components/composeRenderProps';
import {ContextValue, DEFAULT_SLOT, Provider, useSlottedContext} from 'react-aria-components/slots';
import {ContextValue, Provider, useSlottedContext} from 'react-aria-components/slots';
import {
controlSize,
field,
Expand All @@ -48,11 +46,10 @@ import {
SpectrumLabelableProps
} from '@react-types/shared';
import {FormContext, useFormProps} from './Form';
import {IconContext} from './Icon';
import {InputContext, InputProps} from 'react-aria-components/Input';
import {mergeRefs} from 'react-aria/mergeRefs';
import {StyleString} from '../style/types';
import {Text, TextContext} from './Content';
import {TextContext} from './Content';
import {useSpectrumContextProps} from './useSpectrumContextProps';

export interface TextFieldRef<
Expand All @@ -79,7 +76,8 @@ export interface TextFieldProps
*/
size?: 'S' | 'M' | 'L' | 'XL';
/**
* The prefix to display in the text field. Either a string or workflow icon.
* The prefix to display in the text field.
* A non-interactive element that appears before the input.
*/
prefix?: ReactNode;
}
Expand Down Expand Up @@ -194,55 +192,15 @@ export const TextFieldBase = forwardRef(function TextFieldBase(
contextualHelp={props.contextualHelp}>
{label}
</FieldLabel>
<FieldGroup size={props.size} styles={fieldGroupCss}>
<FieldGroup prefix={props.prefix} size={props.size} styles={fieldGroupCss}>
<Provider values={[[TextContext, {}]]}>
{props.prefix ? (
<Provider
values={[
[
IconContext,
{
render: centerBaseline({}),
styles: style({
size: fontRelative(20),
'--iconPrimary': {
type: 'fill',
value: 'currentColor'
}
})
}
],
[AriaTextContext, {}],
[
TextContext,
{
slots: {
[DEFAULT_SLOT]: {
styles: style({
minWidth: 20,
display: 'flex',
alignItems: 'center',
justifyContent: 'center'
})
}
}
}
]
]}>
<div
className={style({
color: 'gray-600',
flexShrink: 0,
marginEnd: 'text-to-visual'
})}>
{typeof props.prefix === 'string' ? <Text>{props.prefix}</Text> : props.prefix}
</div>
</Provider>
) : null}
<InputContext.Consumer>
{ctx => (
<InputContext.Provider
value={{...ctx, ref: mergeRefs((ctx as any)?.ref, inputRef)}}>
value={{
...ctx,
ref: mergeRefs((ctx as any)?.ref, inputRef)
}}>
{children}
</InputContext.Provider>
)}
Expand Down
Loading
Loading