diff --git a/.changeset/fieldstatus-tooltip-deprecated.md b/.changeset/fieldstatus-tooltip-deprecated.md new file mode 100644 index 0000000000000..ca5dcd9961832 --- /dev/null +++ b/.changeset/fieldstatus-tooltip-deprecated.md @@ -0,0 +1,25 @@ +--- +'@astryxdesign/core': patch +--- + +[feat] FieldStatus: the `tooltip` variant is deprecated and is removed in the next minor release. FieldStatus has no tooltip presentation: a direct `` draws a plain message box with no tooltip, and your editor now marks it deprecated. To show a status message from a field's on-field icon, set `statusVariant="tooltip"` on the field control instead; that stays supported. + +Before: + +```tsx + +``` + +After: + +```tsx + +``` + +@cixzhang diff --git a/.changeset/richtext-status-placement-tooltip.md b/.changeset/richtext-status-placement-tooltip.md new file mode 100644 index 0000000000000..d3d757a647876 --- /dev/null +++ b/.changeset/richtext-status-placement-tooltip.md @@ -0,0 +1,7 @@ +--- +'@astryxdesign/richtext': patch +--- + +[chore] RichTextEditor: `statusVariant` names `'tooltip'` in its own type, so it keeps accepting `statusVariant="tooltip"` after FieldStatus drops its deprecated `tooltip` variant in the next minor release. No behavior change. + +@cixzhang diff --git a/packages/core/src/ComplexSelector/ComplexSelector.tsx b/packages/core/src/ComplexSelector/ComplexSelector.tsx index 1ff17bf856ad2..9724af078e97e 100644 --- a/packages/core/src/ComplexSelector/ComplexSelector.tsx +++ b/packages/core/src/ComplexSelector/ComplexSelector.tsx @@ -37,7 +37,7 @@ import {useDevWarning} from '../hooks/useDevWarning'; import {mergeRefs} from '../utils/mergeRefs'; import {useMergedRefs} from '../hooks/useMergedRefs'; import {useBusyAnnouncement} from '../hooks/useBusyAnnouncement'; -import {Field, inputWrapperStyles, type FieldStatusVariant} from '../Field'; +import {Field, inputWrapperStyles} from '../Field'; import {Icon, renderIconSlot, type IconType} from '../Icon'; import {Spinner} from '../Spinner'; import {useTranslator} from '../i18n'; @@ -65,6 +65,7 @@ import {usePressFeedback} from '../hooks/usePressFeedback'; import type {SizeValue} from '../utils/types'; import {themeProps} from '../utils/themeProps'; import {layerViewportInset} from '../Layer/layerViewportInset.stylex'; +import type {FieldStatusPlacement} from '../Field/fieldStatusPlacement'; // The popup content's own block-size cap; the viewport cap is the layer // runtime's. @@ -417,7 +418,7 @@ export interface ComplexSelectorProps extends Omit< * - 'tooltip': no message box; the status icon becomes a focusable info-tip button that reveals the message on hover, keyboard focus, or tap * @default 'attached' for input selectors; 'detached' for secondary and ghost selectors */ - statusVariant?: FieldStatusVariant; + statusVariant?: FieldStatusPlacement; /** Tooltip text displayed next to the label. */ labelTooltip?: string; /** Trigger and field size. */ diff --git a/packages/core/src/DateInput/DateInput.tsx b/packages/core/src/DateInput/DateInput.tsx index 72699ae10ab84..1b9c9a8ee2932 100644 --- a/packages/core/src/DateInput/DateInput.tsx +++ b/packages/core/src/DateInput/DateInput.tsx @@ -41,7 +41,6 @@ import { inputStatusBorderStyles, inputStatusHoverShadowStyles, inputStatusFocusWithinStyles, - type FieldStatusVariant, } from '../Field'; import {Icon} from '../Icon'; import {VisuallyHidden} from '../VisuallyHidden'; @@ -215,6 +214,7 @@ import {focusOutlineStyles} from '../utils/focusOutline.stylex'; import {useLocale, useTranslator} from '../i18n'; import {useMergedRefs} from '../hooks/useMergedRefs'; +import type {FieldStatusPlacement} from '../Field/fieldStatusPlacement'; export interface DateInputProps extends Omit< FieldInputBaseProps, 'onChange' | 'defaultValue' @@ -344,7 +344,7 @@ export interface DateInputProps extends Omit< * - 'tooltip': no message box; the status icon becomes a focusable info-tip button that reveals the message on hover, keyboard focus, or tap * @default 'attached' */ - statusVariant?: FieldStatusVariant; + statusVariant?: FieldStatusPlacement; /** * Width of the field. Numbers are treated as pixels, strings are used as-is diff --git a/packages/core/src/DateRangeInput/DateRangeInput.tsx b/packages/core/src/DateRangeInput/DateRangeInput.tsx index b1d1c698412c5..5e8612002251f 100644 --- a/packages/core/src/DateRangeInput/DateRangeInput.tsx +++ b/packages/core/src/DateRangeInput/DateRangeInput.tsx @@ -50,7 +50,6 @@ import { inputStatusBorderStyles, inputStatusHoverShadowStyles, inputStatusFocusWithinStyles, - type FieldStatusVariant, } from '../Field'; import {Icon} from '../Icon'; import {Spinner} from '../Spinner'; @@ -82,6 +81,7 @@ import {themeProps} from '../utils/themeProps'; import {focusOutlineStyles} from '../utils/focusOutline.stylex'; import {useLocale, useTranslator} from '../i18n'; import type {Locale} from '../i18n/types'; +import type {FieldStatusPlacement} from '../Field/fieldStatusPlacement'; export type {DateRange} from '../Calendar'; @@ -443,7 +443,7 @@ export interface DateRangeInputProps extends Omit< * - 'tooltip': no message box; the status icon becomes a focusable info-tip button that reveals the message on hover, keyboard focus, or tap * @default 'attached' */ - statusVariant?: FieldStatusVariant; + statusVariant?: FieldStatusPlacement; /** * Width of the field. Numbers are treated as pixels, strings are used as-is diff --git a/packages/core/src/Field/Field.tsx b/packages/core/src/Field/Field.tsx index fe9790fbfa586..924a5a55dcbe6 100644 --- a/packages/core/src/Field/Field.tsx +++ b/packages/core/src/Field/Field.tsx @@ -22,7 +22,6 @@ import type {BaseProps} from '../BaseProps'; import type {SizeValue} from '../utils/types'; import {FieldLabel} from './FieldLabel'; import {FieldStatus} from '../FieldStatus/FieldStatus'; -import type {FieldStatusVariant} from '../FieldStatus/FieldStatus'; import {useFieldStatusAnnouncement} from '../FieldStatus/useFieldStatusAnnouncement'; import {spacingVars, borderVars, sizeVars} from '../theme/tokens.stylex'; import type {IconType} from '../Icon'; @@ -32,6 +31,7 @@ import {FormLayoutContext} from '../FormLayout/FormLayoutContext'; import {Text} from '../Text/Text'; import {VisuallyHidden} from '../VisuallyHidden'; import {themeProps} from '../utils/themeProps'; +import type {FieldStatusPlacement} from './fieldStatusPlacement'; const styles = stylex.create({ container: { @@ -187,7 +187,7 @@ export interface FieldProps extends Omit< * - 'tooltip': No message box; the input surfaces status through a tooltip on its on-field icon * @default 'attached' */ - statusVariant?: FieldStatusVariant; + statusVariant?: FieldStatusPlacement; /** * Width of the field. Numbers are treated as pixels, strings are used as-is * (e.g. `'100%'`). Sizes the whole field — label, control, and status — so diff --git a/packages/core/src/Field/fieldStatusPlacement.ts b/packages/core/src/Field/fieldStatusPlacement.ts new file mode 100644 index 0000000000000..dc4551bdbe585 --- /dev/null +++ b/packages/core/src/Field/fieldStatusPlacement.ts @@ -0,0 +1,18 @@ +// Copyright (c) Meta Platforms, Inc. and affiliates. + +/** + * @file fieldStatusPlacement.ts + * @input The FieldStatus variant map + * @output FieldStatusPlacement, where a field control shows its status + * @position Field family type behind every field control's `statusVariant` + * + * A field control places its status message as a FieldStatus variant, or under + * `'tooltip'`, which the Field family consumes before rendering FieldStatus and + * shows through the control's on-field status icon (`component:FieldStatus` + * FR7, DEC-2). `'tooltip'` belongs here, not to FieldStatus itself, whose map + * keeps it only as a deprecated entry until the next minor. + */ + +import type {FieldStatusVariantMap} from '../FieldStatus'; + +export type FieldStatusPlacement = keyof FieldStatusVariantMap | 'tooltip'; diff --git a/packages/core/src/FieldStatus/FieldStatus.deprecation.test.ts b/packages/core/src/FieldStatus/FieldStatus.deprecation.test.ts new file mode 100644 index 0000000000000..bd15a2fee0225 --- /dev/null +++ b/packages/core/src/FieldStatus/FieldStatus.deprecation.test.ts @@ -0,0 +1,85 @@ +// Copyright (c) Meta Platforms, Inc. and affiliates. + +/** + * @file FieldStatus.deprecation.test.ts + * @input The TypeScript language service and FieldStatus.tsx + * @output Verifies an editor marks a direct `variant="tooltip"` deprecated, + * and leaves the supported variants and Field-family placements unmarked + * @position Static deprecation evidence for `component:FieldStatus` FR7 + * (`spec:AST-017` FR29a: the warning lives in the declarations) + */ + +import path from 'node:path'; +import {fileURLToPath} from 'node:url'; +import ts from 'typescript'; +import {describe, expect, it} from 'vitest'; + +const __dirname = path.dirname(fileURLToPath(import.meta.url)); +const CORE_ROOT = path.resolve(__dirname, '../..'); +const FIXTURE = path.join(__dirname, '__deprecation_fixture__.tsx'); + +const SOURCE = ` +import {FieldStatus} from './FieldStatus'; +import type {FieldStatusVariant} from './FieldStatus'; +import {TextInput} from '../TextInput/TextInput'; + +declare const someVariant: FieldStatusVariant; + +export const direct = ; +export const attached = ; +export const detached = ; +export const typed = ; +export const field = ( + {}} status={{type: 'error', message: 'm'}} statusVariant="tooltip" /> +); +`; + +function deprecatedLines(): number[] { + const configPath = path.join(CORE_ROOT, 'tsconfig.json'); + const config = ts.readConfigFile(configPath, ts.sys.readFile); + const parsed = ts.parseJsonConfigFileContent( + config.config, + ts.sys, + CORE_ROOT, + undefined, + configPath, + ); + const files = new Map([[FIXTURE, SOURCE]]); + const host: ts.LanguageServiceHost = { + getScriptFileNames: () => [FIXTURE], + getScriptVersion: () => '1', + getScriptSnapshot: name => { + const text = files.get(name) ?? ts.sys.readFile(name); + return text == null ? undefined : ts.ScriptSnapshot.fromString(text); + }, + getCurrentDirectory: () => CORE_ROOT, + getCompilationSettings: () => ({...parsed.options, noEmit: true}), + getDefaultLibFileName: options => ts.getDefaultLibFilePath(options), + fileExists: name => files.has(name) || ts.sys.fileExists(name), + readFile: name => files.get(name) ?? ts.sys.readFile(name), + readDirectory: ts.sys.readDirectory, + directoryExists: ts.sys.directoryExists, + getDirectories: ts.sys.getDirectories, + }; + const service = ts.createLanguageService(host); + const errors = service.getSemanticDiagnostics(FIXTURE); + expect( + errors.map(d => ts.flattenDiagnosticMessageText(d.messageText, '\n')), + ).toEqual([]); + const source = service.getProgram()!.getSourceFile(FIXTURE)!; + return service + .getSuggestionDiagnostics(FIXTURE) + .filter(d => d.reportsDeprecated) + .map(d => source.getLineAndCharacterOfPosition(d.start ?? 0).line); +} + +function lineOf(name: string): number { + return SOURCE.split('\n').findIndex(line => line.includes(`const ${name} =`)); +} + +describe('FieldStatus tooltip deprecation (component:FieldStatus FR7)', () => { + it('marks a direct variant="tooltip" deprecated and nothing else', () => { + const lines = deprecatedLines(); + expect(lines).toEqual([lineOf('direct')]); + }, 120_000); +}); diff --git a/packages/core/src/FieldStatus/FieldStatus.tsx b/packages/core/src/FieldStatus/FieldStatus.tsx index 3307dc4734173..304a452644e64 100644 --- a/packages/core/src/FieldStatus/FieldStatus.tsx +++ b/packages/core/src/FieldStatus/FieldStatus.tsx @@ -143,6 +143,28 @@ export interface FieldStatusProps extends BaseProps { variant?: FieldStatusVariant; } +// A direct `tooltip` resolves to the deprecated signature first; every other +// call, a `FieldStatusVariant`-typed value included, resolves to the second, +// which `ComponentProps` reads. +/** + * @deprecated FieldStatus has no `tooltip` presentation + * (`component:FieldStatus` FR7): it draws a plain message box with no tooltip. + * Set `statusVariant="tooltip"` on the field control instead, which shows the + * message from its on-field status icon. Removed in the next minor release. + */ +export function FieldStatus( + props: FieldStatusProps & {variant: 'tooltip'}, +): React.JSX.Element; +/** + * A status message component for form fields: an `attached` message under a + * field, or a `detached` one with its own status icon. + * + * @example + * ``` + * + * ``` + */ +export function FieldStatus(props: FieldStatusProps): React.JSX.Element; /** * A status message component for form fields. * @@ -151,9 +173,10 @@ export interface FieldStatusProps extends BaseProps { * decorative for assistive tech (`aria-hidden`): the message text already names * the status in words and is announced through the live region. The `attached` * variant keeps its status affordance on the bordered input, so it renders no - * icon here to avoid a duplicate. The `tooltip` variant renders no message box - * at all — the input surfaces the status through a tooltip on its on-field - * icon — so callers skip rendering FieldStatus for it. + * icon here to avoid a duplicate. FieldStatus has no `tooltip` presentation: + * a field control given `statusVariant="tooltip"` surfaces the status through + * a tooltip on its on-field icon and renders no FieldStatus for it + * (`component:FieldStatus` FR7). * * Screen-reader announcements go through the persistent `useAnnounce` live * regions (assertive for errors, polite otherwise) rather than `role`/ diff --git a/packages/core/src/FieldStatus/index.ts b/packages/core/src/FieldStatus/index.ts index fe674412d418f..302aac21318d1 100644 --- a/packages/core/src/FieldStatus/index.ts +++ b/packages/core/src/FieldStatus/index.ts @@ -25,6 +25,13 @@ export interface FieldStatusVariantMap { attached: true; detached: true; + /** + * @deprecated FieldStatus has no `tooltip` presentation + * (`component:FieldStatus` FR7): given it directly, it draws a plain message + * box with no tooltip. Set `statusVariant="tooltip"` on the field control + * (TextInput, Selector, and the rest), which shows the message from its + * on-field status icon. Removed in the next minor release. + */ tooltip: true; } diff --git a/packages/core/src/FileInput/FileInput.tsx b/packages/core/src/FileInput/FileInput.tsx index 65e9038aa12c5..cba10eb0d4143 100644 --- a/packages/core/src/FileInput/FileInput.tsx +++ b/packages/core/src/FileInput/FileInput.tsx @@ -37,12 +37,7 @@ import { durationVars, easeVars, } from '../theme/tokens.stylex'; -import { - Field, - InputClearButton, - type InputStatus, - type FieldStatusVariant, -} from '../Field'; +import {Field, InputClearButton, type InputStatus} from '../Field'; import {Icon} from '../Icon'; import {Spinner} from '../Spinner'; import {VisuallyHidden} from '../VisuallyHidden'; @@ -70,6 +65,7 @@ import type {TranslatorFn} from '../i18n'; import {useMergedRefs} from '../hooks/useMergedRefs'; import {useListFormatter} from '../i18n/useListFormatter'; import {useResolvedRequired} from '../hooks/useResolvedRequired'; +import type {FieldStatusPlacement} from '../Field/fieldStatusPlacement'; function formatFileSize(bytes: number, t: TranslatorFn): string { if (bytes < 1024) { return t('@astryx.fileInput.fileSize.bytes', {size: bytes}); @@ -406,7 +402,7 @@ export interface FileInputProps extends Omit< * - 'tooltip': no message box; the status icon becomes a focusable info-tip button that reveals the message on hover, keyboard focus, or tap * @default 'attached' */ - statusVariant?: FieldStatusVariant; + statusVariant?: FieldStatusPlacement; /** * Description text displayed below the label. */ diff --git a/packages/core/src/MultiSelector/MultiSelector.tsx b/packages/core/src/MultiSelector/MultiSelector.tsx index aa23936e92a03..6c72ed4e654bb 100644 --- a/packages/core/src/MultiSelector/MultiSelector.tsx +++ b/packages/core/src/MultiSelector/MultiSelector.tsx @@ -39,7 +39,6 @@ import { inputStatusBorderStyles, inputStatusHoverShadowStyles, inputWrapperStyles, - type FieldStatusVariant, } from '../Field'; import {useKeepLayerOpenProps} from '../Layer/useLayer'; import {InternalInputClearButton} from '../Field/InputClearButton'; @@ -110,6 +109,7 @@ import type {AdaptivePresentation} from '../hooks/useAdaptivePresentation'; import {SelectorBottomSheet} from '../Selector/SelectorBottomSheet'; import {useSelectorPresentation} from '../Selector/useSelectorPresentation'; import {selectorPresentationStyles} from '../Selector/selectorPresentation.stylex'; +import type {FieldStatusPlacement} from '../Field/fieldStatusPlacement'; // Sentinel value for the select-all item in keyboard navigation const SELECT_ALL_VALUE = '__xds_select_all__'; @@ -807,7 +807,7 @@ export interface MultiSelectorProps< * - 'tooltip': message is exposed from the on-field status icon * @default 'attached' for input selectors; 'detached' for secondary and ghost selectors */ - statusVariant?: FieldStatusVariant; + statusVariant?: FieldStatusPlacement; /** * Width of the field. Numbers are treated as pixels, strings are used as-is diff --git a/packages/core/src/NumberInput/NumberInput.tsx b/packages/core/src/NumberInput/NumberInput.tsx index 81e208dc0b7bd..4c2b810df9e86 100644 --- a/packages/core/src/NumberInput/NumberInput.tsx +++ b/packages/core/src/NumberInput/NumberInput.tsx @@ -47,7 +47,6 @@ import { inputStatusBorderStyles, inputStatusHoverShadowStyles, inputStatusFocusWithinStyles, - type FieldStatusVariant, } from '../Field'; import {Icon, renderIconSlot, type IconType} from '../Icon'; import {VisuallyHidden} from '../VisuallyHidden'; @@ -235,6 +234,7 @@ import {themeProps} from '../utils/themeProps'; import {useTranslator, useLocale} from '../i18n'; import {formatEditableNumber} from './numberParser'; import {parseNumberInput, resolveNumberInputCommit} from './numberInputCommit'; +import type {FieldStatusPlacement} from '../Field/fieldStatusPlacement'; interface NumberInputPropsBase extends Omit< FieldInputBaseProps, @@ -324,7 +324,7 @@ interface NumberInputPropsBase extends Omit< * - 'tooltip': no message box; the status icon becomes a focusable info-tip button that reveals the message on hover, keyboard focus, or tap * @default 'attached' */ - statusVariant?: FieldStatusVariant; + statusVariant?: FieldStatusPlacement; /** * The size of the input. * - 'sm': Compact size (28px height) diff --git a/packages/core/src/PowerSearch/PowerSearch.tsx b/packages/core/src/PowerSearch/PowerSearch.tsx index 2927f7021c502..986d508fc98aa 100644 --- a/packages/core/src/PowerSearch/PowerSearch.tsx +++ b/packages/core/src/PowerSearch/PowerSearch.tsx @@ -38,7 +38,7 @@ import {layerAnimations} from '../Layer/layerAnimations.stylex'; import {Icon} from '../Icon'; import type {IconType} from '../Icon'; import type {IconName} from '../Icon/globalIconRegistry'; -import type {InputStatus, FieldStatusVariant} from '../Field'; +import type {InputStatus} from '../Field'; import {usePopover} from '../Popover/usePopover'; import {layerViewportInset} from '../Layer/layerViewportInset.stylex'; import {useAnnounce} from '../hooks/useAnnounce'; @@ -77,6 +77,7 @@ import type { } from './types'; import {useMergedRefs} from '../hooks/useMergedRefs'; +import type {FieldStatusPlacement} from '../Field/fieldStatusPlacement'; // ============================================================================= // Icon mapping for typeahead entries // ============================================================================= @@ -413,7 +414,7 @@ export interface PowerSearchProps extends Omit< * - 'detached': message floats below as a separate element with spacing * @default 'attached' */ - statusVariant?: FieldStatusVariant; + statusVariant?: FieldStatusPlacement; /** Max width for dropdown menu. */ menuWidth?: number; /** Max display length for filter token values. @default 40 */ diff --git a/packages/core/src/Selector/Selector.tsx b/packages/core/src/Selector/Selector.tsx index 5e4528be5cb90..eb77b4e5648e1 100644 --- a/packages/core/src/Selector/Selector.tsx +++ b/packages/core/src/Selector/Selector.tsx @@ -42,7 +42,6 @@ import { inputStatusBorderStyles, inputStatusHoverShadowStyles, inputWrapperStyles, - type FieldStatusVariant, } from '../Field'; import {Divider} from '../Divider'; import {layerAnimations} from '../Layer/layerAnimations.stylex'; @@ -104,6 +103,7 @@ import type {AdaptivePresentation} from '../hooks/useAdaptivePresentation'; import {SelectorBottomSheet} from './SelectorBottomSheet'; import {useSelectorPresentation} from './useSelectorPresentation'; import {selectorPresentationStyles} from './selectorPresentation.stylex'; +import type {FieldStatusPlacement} from '../Field/fieldStatusPlacement'; const styles = stylex.create({ // Trigger container — the enhanced click target wrapping the combobox button and clear button as siblings @@ -726,7 +726,7 @@ interface SelectorPropsBase< * - 'tooltip': message is exposed from the on-field status icon * @default 'attached' for input selectors; 'detached' for secondary/ghost selectors */ - statusVariant?: FieldStatusVariant; + statusVariant?: FieldStatusPlacement; /** * Width of the field. Numbers are treated as pixels, strings are used as-is diff --git a/packages/core/src/TextArea/TextArea.tsx b/packages/core/src/TextArea/TextArea.tsx index f74186ac66a1f..767b56d248de0 100644 --- a/packages/core/src/TextArea/TextArea.tsx +++ b/packages/core/src/TextArea/TextArea.tsx @@ -41,7 +41,6 @@ import { inputStatusBorderStyles, inputStatusHoverShadowStyles, inputStatusFocusWithinStyles, - type FieldStatusVariant, } from '../Field'; import {Icon, renderIconSlot, type IconType} from '../Icon'; import {Spinner} from '../Spinner'; @@ -66,6 +65,7 @@ import {useTranslator} from '../i18n'; import {useMergedRefs} from '../hooks/useMergedRefs'; import {useBusyAnnouncement} from '../hooks/useBusyAnnouncement'; +import type {FieldStatusPlacement} from '../Field/fieldStatusPlacement'; const COUNTER_WARNING_THRESHOLD = 0.8; const styles = stylex.create({ @@ -304,7 +304,7 @@ export interface TextAreaProps extends Omit< * - 'tooltip': no message box; the status icon becomes a focusable info-tip button that reveals the message on hover, keyboard focus, or tap * @default 'attached' */ - statusVariant?: FieldStatusVariant; + statusVariant?: FieldStatusPlacement; /** * Width of the field. Numbers are treated as pixels, strings are used as-is * (e.g. `'100%'`). Sizes the whole field (label, control, and status) so they diff --git a/packages/core/src/TextInput/TextInput.tsx b/packages/core/src/TextInput/TextInput.tsx index e2897888e623f..c74803d88ff13 100644 --- a/packages/core/src/TextInput/TextInput.tsx +++ b/packages/core/src/TextInput/TextInput.tsx @@ -42,7 +42,6 @@ import { inputStatusBorderStyles, inputStatusHoverShadowStyles, inputStatusFocusWithinStyles, - type FieldStatusVariant, } from '../Field'; import {renderIconSlot, type IconType} from '../Icon'; import {Spinner} from '../Spinner'; @@ -123,6 +122,7 @@ import {useTranslator} from '../i18n'; import {useMergedRefs} from '../hooks/useMergedRefs'; import {useBusyAnnouncement} from '../hooks/useBusyAnnouncement'; +import type {FieldStatusPlacement} from '../Field/fieldStatusPlacement'; export type TextInputType = 'text' | 'password' | 'email'; export interface TextInputProps extends Omit< @@ -214,7 +214,7 @@ export interface TextInputProps extends Omit< * - 'tooltip': no message box; the status icon becomes a focusable info-tip button that reveals the message on hover, keyboard focus, or tap * @default 'attached' */ - statusVariant?: FieldStatusVariant; + statusVariant?: FieldStatusPlacement; /** * The size of the input. * - 'sm': Compact size (18px height) diff --git a/packages/core/src/TimeInput/TimeInput.tsx b/packages/core/src/TimeInput/TimeInput.tsx index ae586664d258d..128877640abed 100644 --- a/packages/core/src/TimeInput/TimeInput.tsx +++ b/packages/core/src/TimeInput/TimeInput.tsx @@ -45,7 +45,6 @@ import { inputStatusBorderStyles, inputStatusHoverShadowStyles, inputStatusFocusWithinStyles, - type FieldStatusVariant, } from '../Field'; import {Icon} from '../Icon'; import {Spinner} from '../Spinner'; @@ -94,6 +93,7 @@ import { type InputPresentation, } from '../utils/inputPresentation'; import {useDevWarning} from '../hooks/useDevWarning'; +import type {FieldStatusPlacement} from '../Field/fieldStatusPlacement'; const TOUCH_POINTER_QUERY = '(pointer: coarse)'; @@ -377,7 +377,7 @@ export interface TimeInputProps extends Omit< * - 'tooltip': no message box; the status icon becomes a focusable info-tip button that reveals the message on hover, keyboard focus, or tap * @default 'attached' */ - statusVariant?: FieldStatusVariant; + statusVariant?: FieldStatusPlacement; /** * Width of the field. Numbers are treated as pixels, strings are used as-is diff --git a/packages/core/src/Tokenizer/Tokenizer.tsx b/packages/core/src/Tokenizer/Tokenizer.tsx index 8c914bb3b4878..eb0102c23b867 100644 --- a/packages/core/src/Tokenizer/Tokenizer.tsx +++ b/packages/core/src/Tokenizer/Tokenizer.tsx @@ -69,7 +69,6 @@ import { inputStatusBorderStyles, inputStatusHoverShadowStyles, inputStatusFocusWithinStyles, - type FieldStatusVariant, } from '../Field'; import {Token} from '../Token'; import {Spinner} from '../Spinner'; @@ -92,6 +91,7 @@ import {themeProps} from '../utils/themeProps'; import {useTranslator} from '../i18n'; import type {TokenizerBottomSheetProps} from './TokenizerBottomSheet'; import {useResolvedRequired} from '../hooks/useResolvedRequired'; +import type {FieldStatusPlacement} from '../Field/fieldStatusPlacement'; // Re-export status types for convenience export type { @@ -193,7 +193,7 @@ export interface TokenizerProps extends Omit< * - 'tooltip': no message box; the status icon becomes a focusable info-tip button that reveals the message on hover, keyboard focus, or tap * @default 'attached' */ - statusVariant?: FieldStatusVariant; + statusVariant?: FieldStatusPlacement; /** * Icon to display at the start of the input. * Accepts a ReactNode (e.g. ``) or an SVG icon component directly. diff --git a/packages/core/src/Typeahead/Typeahead.tsx b/packages/core/src/Typeahead/Typeahead.tsx index c8de37c11d971..42a3d37985bff 100644 --- a/packages/core/src/Typeahead/Typeahead.tsx +++ b/packages/core/src/Typeahead/Typeahead.tsx @@ -46,7 +46,6 @@ import { inputStatusBorderStyles, inputStatusHoverShadowStyles, inputStatusFocusWithinStyles, - type FieldStatusVariant, } from '../Field'; import {Token} from '../Token'; import {useTooltip} from '../Tooltip'; @@ -77,6 +76,7 @@ import {useTranslator} from '../i18n'; import {useMergedRefs} from '../hooks/useMergedRefs'; import {useBusyAnnouncement} from '../hooks/useBusyAnnouncement'; import {useResolvedRequired} from '../hooks/useResolvedRequired'; +import type {FieldStatusPlacement} from '../Field/fieldStatusPlacement'; export type { InputStatus as TypeaheadStatus, InputStatusType as TypeaheadStatusType, @@ -116,7 +116,7 @@ export interface TypeaheadProps extends Omit< * - 'tooltip': no message box; the status icon becomes a focusable info-tip button that reveals the message on hover, keyboard focus, or tap * @default 'attached' */ - statusVariant?: FieldStatusVariant; + statusVariant?: FieldStatusPlacement; /** * Icon to display at the start of the input. * Accepts a ReactNode (e.g. ``) or an SVG icon component directly. diff --git a/packages/core/src/hooks/useInputStatusIcon.tsx b/packages/core/src/hooks/useInputStatusIcon.tsx index ce4fddb8ad04a..5985f2f537560 100644 --- a/packages/core/src/hooks/useInputStatusIcon.tsx +++ b/packages/core/src/hooks/useInputStatusIcon.tsx @@ -36,13 +36,13 @@ import {useCallback, useEffect, useState} from 'react'; import type {ReactNode} from 'react'; import * as stylex from '@stylexjs/stylex'; import {Icon, type IconName, type IconSize} from '../Icon'; -import type {FieldStatusVariant} from '../FieldStatus/FieldStatus'; import type {InputStatus, InputStatusType} from '../Field/types'; import {useTooltip} from '../Tooltip'; import {useTranslator} from '../i18n'; import {radiusVars} from '../theme/tokens.stylex'; import {themeProps} from '../utils/themeProps'; import {focusOutlineStyles} from '../utils/focusOutline.stylex'; +import type {FieldStatusPlacement} from '../Field/fieldStatusPlacement'; /** * Maps each status type to its glyph. Shared so every input shows the same icon @@ -104,7 +104,7 @@ export interface UseInputStatusIconOptions { /** The input's status, or undefined when there is none. */ status?: InputStatus; /** How the status is presented relative to the input. */ - statusVariant?: FieldStatusVariant; + statusVariant?: FieldStatusPlacement; /** Whether the input is inside an InputGroup (which owns status rendering). */ isInGroup?: boolean; /** Size of the on-field icon. @default 'md' */ diff --git a/packages/lab/src/MobilePowerSearch/MobilePowerSearch.tsx b/packages/lab/src/MobilePowerSearch/MobilePowerSearch.tsx index 6e6d154cecb69..9b11636504262 100644 --- a/packages/lab/src/MobilePowerSearch/MobilePowerSearch.tsx +++ b/packages/lab/src/MobilePowerSearch/MobilePowerSearch.tsx @@ -207,7 +207,7 @@ export interface MobilePowerSearchProps extends Omit< /** Validation status. */ status?: InputStatus; /** How the status message is placed. @default 'attached' */ - statusVariant?: FieldStatusVariant; + statusVariant?: FieldStatusVariant | 'tooltip'; /** Max display length for token values. @default 40 */ maxTokenLength?: number; /** Max suggestions in string and entity value typeaheads. @default 10 */ diff --git a/packages/lab/src/TransferList/TransferList.doc.mjs b/packages/lab/src/TransferList/TransferList.doc.mjs index fb6373d330019..b536351c9b57a 100644 --- a/packages/lab/src/TransferList/TransferList.doc.mjs +++ b/packages/lab/src/TransferList/TransferList.doc.mjs @@ -224,7 +224,7 @@ export const docs = { }, { name: 'statusVariant', - type: 'FieldStatusVariant', + type: "'attached' | 'detached' | 'tooltip'", description: 'Placement treatment for the status message.', }, { diff --git a/packages/richtext/src/RichTextEditor.tsx b/packages/richtext/src/RichTextEditor.tsx index d64beeffa5a5a..2ce42ba809011 100644 --- a/packages/richtext/src/RichTextEditor.tsx +++ b/packages/richtext/src/RichTextEditor.tsx @@ -370,7 +370,7 @@ export interface RichTextEditorProps extends Omit< * info-tip button that reveals the message * @default 'attached' */ - statusVariant?: FieldStatusVariant; + statusVariant?: FieldStatusVariant | 'tooltip'; /** * Width of the field. Numbers are treated as pixels, strings are used as-is * (e.g. `'100%'`).