diff --git a/pages/input/adornments.page.tsx b/pages/input/adornments.page.tsx
new file mode 100644
index 0000000000..ed558b75ca
--- /dev/null
+++ b/pages/input/adornments.page.tsx
@@ -0,0 +1,768 @@
+// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
+// SPDX-License-Identifier: Apache-2.0
+import React, { useState } from 'react';
+
+import Badge from '~components/badge';
+import Box from '~components/box';
+import ColumnLayout from '~components/column-layout';
+import Container from '~components/container';
+import ContentLayout from '~components/content-layout';
+import FormField from '~components/form-field';
+import Header from '~components/header';
+import Icon from '~components/icon';
+import Input from '~components/input';
+import SpaceBetween from '~components/space-between';
+import StatusIndicator from '~components/status-indicator';
+import Toggle from '~components/toggle';
+
+// ─── State controls panel ─────────────────────────────────────────────────────
+
+interface StateControlsProps {
+ disabled: boolean;
+ setDisabled: (v: boolean) => void;
+ readOnly: boolean;
+ setReadOnly: (v: boolean) => void;
+ invalid: boolean;
+ setInvalid: (v: boolean) => void;
+ warning: boolean;
+ setWarning: (v: boolean) => void;
+ compact: boolean;
+ setCompact: (v: boolean) => void;
+ rtl: boolean;
+ setRtl: (v: boolean) => void;
+}
+
+function StateControls({
+ disabled,
+ setDisabled,
+ readOnly,
+ setReadOnly,
+ invalid,
+ setInvalid,
+ warning,
+ setWarning,
+ compact,
+ setCompact,
+ rtl,
+ setRtl,
+}: StateControlsProps) {
+ return (
+ State controls}>
+
+
+ setDisabled(detail.checked)}>
+ Disabled
+
+ setReadOnly(detail.checked)}>
+ Read-only
+
+
+
+ setInvalid(detail.checked)}>
+ Invalid
+
+ setWarning(detail.checked)}>
+ Warning
+
+
+
+ setCompact(detail.checked)}>
+ Compact density
+
+ setRtl(detail.checked)}>
+ RTL layout
+
+
+
+
+ );
+}
+
+// ─── Format hint examples — string adornments ─────────────────────────────────
+
+interface StateProps {
+ disabled: boolean;
+ readOnly: boolean;
+ invalid: boolean;
+ warning: boolean;
+}
+
+function FormatHintExamples({ disabled, readOnly, invalid, warning }: StateProps) {
+ const [currency, setCurrency] = useState('');
+ const [percent, setPercent] = useState('');
+ const [compound, setCompound] = useState('');
+ const [both, setBoth] = useState('');
+ const [ms, setMs] = useState('');
+
+ return (
+
+ Format hint adornments — string
+
+ Decorative
+
+
+ }
+ >
+
+
+
+ setCurrency(e.detail.value)}
+ prefix="$"
+ inputMode="decimal"
+ placeholder="0.00"
+ disabled={disabled}
+ readOnly={readOnly}
+ invalid={invalid}
+ warning={warning}
+ />
+
+
+
+ setPercent(e.detail.value)}
+ suffix="%"
+ inputMode="numeric"
+ placeholder="0"
+ disabled={disabled}
+ readOnly={readOnly}
+ invalid={invalid}
+ warning={warning}
+ />
+
+
+
+ setCompound(e.detail.value)}
+ suffix="TB/mo"
+ inputMode="numeric"
+ placeholder="0"
+ disabled={disabled}
+ readOnly={readOnly}
+ invalid={invalid}
+ warning={warning}
+ />
+
+
+
+ setMs(e.detail.value)}
+ suffix="ms"
+ inputMode="numeric"
+ placeholder="0"
+ disabled={disabled}
+ readOnly={readOnly}
+ invalid={invalid}
+ warning={warning}
+ />
+
+
+
+ setBoth(e.detail.value)}
+ prefix="<"
+ suffix="%"
+ inputMode="decimal"
+ placeholder="0.5"
+ disabled={disabled}
+ readOnly={readOnly}
+ invalid={invalid}
+ warning={warning}
+ />
+
+
+
+
+ );
+}
+
+// ─── ReactNode adornments — icons ─────────────────────────────────────────────
+
+function ReactNodeIconExamples({ disabled, readOnly, invalid, warning }: StateProps) {
+ const [search, setSearch] = useState('');
+ const [email, setEmail] = useState('');
+ const [url, setUrl] = useState('');
+ const [amount, setAmount] = useState('');
+ const [locked, setLocked] = useState('');
+
+ return (
+
+ ReactNode adornments — Icons
+
+ ReactNode
+
+
+ }
+ >
+
+
+
+ setSearch(e.detail.value)}
+ prefix={ }
+ placeholder="Filter by name or tag"
+ disabled={disabled}
+ readOnly={readOnly}
+ invalid={invalid}
+ warning={warning}
+ />
+
+
+
+ setEmail(e.detail.value)}
+ prefix={ }
+ type="email"
+ placeholder="user@example.com"
+ disabled={disabled}
+ readOnly={readOnly}
+ invalid={invalid}
+ warning={warning}
+ />
+
+
+
+ setUrl(e.detail.value)}
+ prefix={ }
+ type="url"
+ placeholder="https://example.com/hook"
+ disabled={disabled}
+ readOnly={readOnly}
+ invalid={invalid}
+ warning={warning}
+ />
+
+
+
+ setAmount(e.detail.value)}
+ prefix={ }
+ suffix="USD"
+ inputMode="decimal"
+ placeholder="0.00"
+ disabled={disabled}
+ readOnly={readOnly}
+ invalid={invalid}
+ warning={warning}
+ />
+
+
+
+ setLocked(e.detail.value)}
+ prefix={ }
+ type="password"
+ placeholder="sk-••••••••"
+ disabled={disabled}
+ readOnly={readOnly}
+ invalid={invalid}
+ warning={warning}
+ />
+
+
+
+
+ );
+}
+
+// ─── ReactNode adornments — Badges ────────────────────────────────────────────
+
+function ReactNodeBadgeExamples({ disabled, readOnly, invalid, warning }: StateProps) {
+ const [beta, setBeta] = useState('');
+ const [required, setRequired] = useState('');
+ const [new_, setNew_] = useState('');
+
+ return (
+
+ ReactNode adornments — Badges
+
+ ReactNode
+
+
+ }
+ >
+
+
+
+ setBeta(e.detail.value)}
+ suffix={Beta }
+ placeholder="my-feature-flag"
+ disabled={disabled}
+ readOnly={readOnly}
+ invalid={invalid}
+ warning={warning}
+ />
+
+
+
+ setRequired(e.detail.value)}
+ suffix={Required }
+ inputMode="numeric"
+ placeholder="123456789012"
+ disabled={disabled}
+ readOnly={readOnly}
+ invalid={invalid}
+ warning={warning}
+ />
+
+
+
+ setNew_(e.detail.value)}
+ suffix={New }
+ placeholder="arn:aws:bedrock:..."
+ disabled={disabled}
+ readOnly={readOnly}
+ invalid={invalid}
+ warning={warning}
+ />
+
+
+
+
+ );
+}
+
+// ─── ReactNode adornments — StatusIndicator ───────────────────────────────────
+
+function ReactNodeStatusExamples({ disabled, readOnly, invalid, warning }: StateProps) {
+ const [endpoint, setEndpoint] = useState('');
+ const [pending, setPending] = useState('');
+
+ return (
+
+ ReactNode adornments — StatusIndicator
+
+ ReactNode
+
+
+ }
+ >
+
+
+
+ setEndpoint(e.detail.value)}
+ suffix={Connected }
+ placeholder="db.example.com:5432"
+ disabled={disabled}
+ readOnly={readOnly}
+ invalid={invalid}
+ warning={warning}
+ />
+
+
+
+ setPending(e.detail.value)}
+ suffix={Queued }
+ placeholder="job-abc123"
+ disabled={disabled}
+ readOnly={readOnly}
+ invalid={invalid}
+ warning={warning}
+ />
+
+
+
+
+ );
+}
+
+// ─── ReactNode adornments — icon + text combination ───────────────────────────
+
+function ReactNodeCompositeExamples({ disabled, readOnly, invalid, warning }: StateProps) {
+ const [rate, setRate] = useState('');
+ const [storage, setStorage] = useState('');
+
+ return (
+
+ ReactNode adornments — Icon + text composite
+
+ ReactNode
+
+
+ }
+ >
+
+
+
+ setRate(e.detail.value)}
+ suffix={
+
+
+ req/s
+
+ }
+ inputMode="numeric"
+ placeholder="1000"
+ disabled={disabled}
+ readOnly={readOnly}
+ invalid={invalid}
+ warning={warning}
+ />
+
+
+
+ setStorage(e.detail.value)}
+ prefix={ }
+ suffix={
+
+
+ GB
+
+ }
+ inputMode="numeric"
+ placeholder="512"
+ disabled={disabled}
+ readOnly={readOnly}
+ invalid={invalid}
+ warning={warning}
+ />
+
+
+
+
+ );
+}
+
+// ─── Required prefix/suffix (userland concatenation) ──────────────────────────
+
+function RequiredAffixExamples({ disabled, readOnly, invalid, warning }: StateProps) {
+ const [url, setUrl] = useState('');
+ const [bucket, setBucket] = useState('');
+ const [host, setHost] = useState('');
+
+ return (
+
+ Required prefix/suffix
+
+ Builder concatenates
+
+
+ }
+ >
+
+
+
+
+ setUrl(e.detail.value)}
+ prefix="https://"
+ type="url"
+ placeholder="example.com/webhook"
+ disabled={disabled}
+ readOnly={readOnly}
+ invalid={invalid}
+ warning={warning}
+ />
+ {url && (
+
+ Submitted value:{' '}
+
+ https://{url}
+
+
+ )}
+
+
+
+
+
+ setBucket(e.detail.value)}
+ prefix="amazon-braket-"
+ placeholder="my-bucket"
+ disabled={disabled}
+ readOnly={readOnly}
+ invalid={invalid}
+ warning={warning}
+ />
+ {bucket && (
+
+ Submitted value:{' '}
+
+ amazon-braket-{bucket}
+
+
+ )}
+
+
+
+
+
+ setHost(e.detail.value)}
+ suffix=".ec2.internal"
+ placeholder="my-host"
+ disabled={disabled}
+ readOnly={readOnly}
+ invalid={invalid}
+ warning={warning}
+ />
+ {host && (
+
+ Submitted value:{' '}
+
+ {host}.ec2.internal
+
+
+ )}
+
+
+
+
+
+ );
+}
+
+// ─── Baseline comparison ──────────────────────────────────────────────────────
+
+function BaselineComparison({ disabled, readOnly, invalid, warning }: StateProps) {
+ const [value, setValue] = useState('');
+ return (
+
+ Baseline: plain Input
+
+ }
+ >
+
+ setValue(e.detail.value)}
+ placeholder="Enter a value"
+ disabled={disabled}
+ readOnly={readOnly}
+ invalid={invalid}
+ warning={warning}
+ />
+
+
+ );
+}
+
+// ─── RTL section ──────────────────────────────────────────────────────────────
+
+function RtlSection() {
+ const [val, setVal] = useState('');
+ const [val2, setVal2] = useState('');
+ const [val3, setVal3] = useState('');
+ return (
+ RTL layout}>
+
+ In RTL locales,{' '}
+
+ prefix
+ {' '}
+ appears on the trailing edge and{' '}
+
+ suffix
+ {' '}
+ appears on the leading edge. The prop names are semantic (before/after the value), not directional. Icons flip
+ correctly because the flex container inherits{' '}
+
+ dir
+
+ .
+
+
+
+
+
+ setVal(e.detail.value)}
+ prefix="$"
+ inputMode="decimal"
+ placeholder="0.00"
+ />
+
+
+ setVal2(e.detail.value)}
+ suffix="%"
+ inputMode="numeric"
+ placeholder="0"
+ />
+
+
+ setVal3(e.detail.value)}
+ prefix={ }
+ type="email"
+ placeholder="user@example.com"
+ />
+
+
+
+
+
+ );
+}
+
+// ─── Main page ────────────────────────────────────────────────────────────────
+
+export default function AdornmentsPage() {
+ const [disabled, setDisabled] = useState(false);
+ const [readOnly, setReadOnly] = useState(false);
+ const [invalid, setInvalid] = useState(false);
+ const [warning, setWarning] = useState(false);
+ const [compact, setCompact] = useState(false);
+ const [rtl, setRtl] = useState(false);
+
+ const stateProps = { disabled, readOnly, invalid, warning };
+
+ return (
+
+
+ Input: prefix/suffix adornments (ReactNode)
+
+ }
+ >
+
+
+
+
+
+
+
+
+
+
+
+
+
+ );
+}
diff --git a/src/__tests__/snapshot-tests/__snapshots__/documenter.test.ts.snap b/src/__tests__/snapshot-tests/__snapshots__/documenter.test.ts.snap
index d835dc74ff..7a7c25be6f 100644
--- a/src/__tests__/snapshot-tests/__snapshots__/documenter.test.ts.snap
+++ b/src/__tests__/snapshot-tests/__snapshots__/documenter.test.ts.snap
@@ -17298,7 +17298,48 @@ with the input using \`ariaDescribedby\`.",
"type": "boolean",
},
],
- "regions": [],
+ "regions": [
+ {
+ "description": "Content rendered in a shaded cell on the leading edge of the input field,
+before the editable value.
+
+Accepts any React node — plain text, a short symbol, an icon, or a badge.
+Use for contextual decorators such as currency symbols (\`$\`, \`€\`),
+URL schemes (\`https://\`), path prefixes (\`/api/v2/\`), or an icon that
+clarifies the expected data type.
+
+The adornment is purely decorative — it does not mutate the \`value\` prop
+or the \`onChange\` detail. Wrap any icon or custom element in
+\`aria-hidden="true"\` if it is already conveyed by the visible
+\`\` label, so screen reader users are not double-announced.
+Ensure the unit or context is also reflected in the visible label
+(for example, label = "Amount in US dollars", not just "Amount").
+
+In RTL locales the \`prefix\` cell appears on the trailing edge.
+"prefix" means "before the value" semantically, not "on the left".
+
+Don't use \`prefix\` as a substitute for a visible label.",
+ "isDefault": false,
+ "name": "prefix",
+ },
+ {
+ "description": "Content rendered in a shaded cell on the trailing edge of the input field,
+after the editable value.
+
+Accepts any React node — plain text, a short unit abbreviation, an icon,
+or a badge. Use for units of measure (\`%\`, \`ms\`, \`TB/mo\`, \`GB\`, \`req/s\`,
+\`days\`), currency codes (\`USD\`, \`EUR\`), degree notation (\`°C\`, \`°E\`), or
+an icon indicating a secondary action.
+
+The adornment is purely decorative — it does not mutate the \`value\` prop
+or the \`onChange\` detail. Ensure the unit is also reflected in the visible
+\`\` label (for example, label = "CPU utilization (percent)").
+
+In RTL locales the \`suffix\` cell appears on the leading edge.",
+ "isDefault": false,
+ "name": "suffix",
+ },
+ ],
"releaseStatus": "stable",
}
`;
@@ -35226,6 +35267,34 @@ Supported options:
],
},
},
+ {
+ "description": "Returns the prefix adornment element, or null if no prefix is set.",
+ "name": "findPrefix",
+ "parameters": [],
+ "returnType": {
+ "isNullable": true,
+ "name": "ElementWrapper",
+ "typeArguments": [
+ {
+ "name": "HTMLElement",
+ },
+ ],
+ },
+ },
+ {
+ "description": "Returns the suffix adornment element, or null if no suffix is set.",
+ "name": "findSuffix",
+ "parameters": [],
+ "returnType": {
+ "isNullable": true,
+ "name": "ElementWrapper",
+ "typeArguments": [
+ {
+ "name": "HTMLElement",
+ },
+ ],
+ },
+ },
{
"inheritedFrom": {
"name": "BaseInputWrapper.focus",
@@ -36880,6 +36949,23 @@ To find a specific row use the \`findRow(n)\` function as chaining \`findRows().
],
},
},
+ {
+ "description": "Returns the prefix adornment element, or null if no prefix is set.",
+ "inheritedFrom": {
+ "name": "InputWrapper.findPrefix",
+ },
+ "name": "findPrefix",
+ "parameters": [],
+ "returnType": {
+ "isNullable": true,
+ "name": "ElementWrapper",
+ "typeArguments": [
+ {
+ "name": "HTMLElement",
+ },
+ ],
+ },
+ },
{
"name": "findStatusIndicator",
"parameters": [
@@ -36903,6 +36989,23 @@ To find a specific row use the \`findRow(n)\` function as chaining \`findRows().
],
},
},
+ {
+ "description": "Returns the suffix adornment element, or null if no suffix is set.",
+ "inheritedFrom": {
+ "name": "InputWrapper.findSuffix",
+ },
+ "name": "findSuffix",
+ "parameters": [],
+ "returnType": {
+ "isNullable": true,
+ "name": "ElementWrapper",
+ "typeArguments": [
+ {
+ "name": "HTMLElement",
+ },
+ ],
+ },
+ },
{
"inheritedFrom": {
"name": "BaseInputWrapper.focus",
@@ -43165,6 +43268,23 @@ and this method will have no effect.",
],
},
},
+ {
+ "description": "Returns the prefix adornment element, or null if no prefix is set.",
+ "inheritedFrom": {
+ "name": "InputWrapper.findPrefix",
+ },
+ "name": "findPrefix",
+ "parameters": [],
+ "returnType": {
+ "isNullable": true,
+ "name": "ElementWrapper",
+ "typeArguments": [
+ {
+ "name": "HTMLElement",
+ },
+ ],
+ },
+ },
{
"description": "Returns custom property form cancel button.",
"name": "findPropertyCancelButton",
@@ -43256,6 +43376,23 @@ and this method will have no effect.",
],
},
},
+ {
+ "description": "Returns the suffix adornment element, or null if no suffix is set.",
+ "inheritedFrom": {
+ "name": "InputWrapper.findSuffix",
+ },
+ "name": "findSuffix",
+ "parameters": [],
+ "returnType": {
+ "isNullable": true,
+ "name": "ElementWrapper",
+ "typeArguments": [
+ {
+ "name": "HTMLElement",
+ },
+ ],
+ },
+ },
{
"name": "findTokens",
"parameters": [],
@@ -47245,6 +47382,24 @@ Supported options:
"name": "ElementWrapper",
},
},
+ {
+ "description": "Returns the prefix adornment element, or null if no prefix is set.",
+ "name": "findPrefix",
+ "parameters": [],
+ "returnType": {
+ "isNullable": false,
+ "name": "ElementWrapper",
+ },
+ },
+ {
+ "description": "Returns the suffix adornment element, or null if no suffix is set.",
+ "name": "findSuffix",
+ "parameters": [],
+ "returnType": {
+ "isNullable": false,
+ "name": "ElementWrapper",
+ },
+ },
],
"name": "InputWrapper",
},
@@ -48381,6 +48536,18 @@ To find a specific row use the \`findRow(n)\` function as chaining \`findRows().
"name": "ElementWrapper",
},
},
+ {
+ "description": "Returns the prefix adornment element, or null if no prefix is set.",
+ "inheritedFrom": {
+ "name": "InputWrapper.findPrefix",
+ },
+ "name": "findPrefix",
+ "parameters": [],
+ "returnType": {
+ "isNullable": false,
+ "name": "ElementWrapper",
+ },
+ },
{
"name": "findStatusIndicator",
"parameters": [
@@ -48401,6 +48568,18 @@ To find a specific row use the \`findRow(n)\` function as chaining \`findRows().
"name": "ElementWrapper",
},
},
+ {
+ "description": "Returns the suffix adornment element, or null if no suffix is set.",
+ "inheritedFrom": {
+ "name": "InputWrapper.findSuffix",
+ },
+ "name": "findSuffix",
+ "parameters": [],
+ "returnType": {
+ "isNullable": false,
+ "name": "ElementWrapper",
+ },
+ },
],
"name": "AutosuggestWrapper",
},
@@ -52863,6 +53042,18 @@ In this case, use findContentEditableElement() instead.",
"name": "ElementWrapper",
},
},
+ {
+ "description": "Returns the prefix adornment element, or null if no prefix is set.",
+ "inheritedFrom": {
+ "name": "InputWrapper.findPrefix",
+ },
+ "name": "findPrefix",
+ "parameters": [],
+ "returnType": {
+ "isNullable": false,
+ "name": "ElementWrapper",
+ },
+ },
{
"description": "Returns custom property form cancel button.",
"name": "findPropertyCancelButton",
@@ -52945,6 +53136,18 @@ In this case, use findContentEditableElement() instead.",
"name": "ElementWrapper",
},
},
+ {
+ "description": "Returns the suffix adornment element, or null if no suffix is set.",
+ "inheritedFrom": {
+ "name": "InputWrapper.findSuffix",
+ },
+ "name": "findSuffix",
+ "parameters": [],
+ "returnType": {
+ "isNullable": false,
+ "name": "ElementWrapper",
+ },
+ },
{
"name": "findTokens",
"parameters": [],
diff --git a/src/__tests__/snapshot-tests/__snapshots__/test-utils-selectors.test.tsx.snap b/src/__tests__/snapshot-tests/__snapshots__/test-utils-selectors.test.tsx.snap
index 4b1af42bc5..ed248e6f53 100644
--- a/src/__tests__/snapshot-tests/__snapshots__/test-utils-selectors.test.tsx.snap
+++ b/src/__tests__/snapshot-tests/__snapshots__/test-utils-selectors.test.tsx.snap
@@ -378,6 +378,8 @@ exports[`test-utils selectors 1`] = `
"input": [
"awsui_input-button-right_2rhyz",
"awsui_input-container_2rhyz",
+ "awsui_input-prefix_2rhyz",
+ "awsui_input-suffix_2rhyz",
"awsui_input_2rhyz",
"awsui_root_2rhyz",
],
diff --git a/src/input/index.tsx b/src/input/index.tsx
index 351bdf14af..f194cff2a6 100644
--- a/src/input/index.tsx
+++ b/src/input/index.tsx
@@ -44,6 +44,8 @@ const Input = React.forwardRef(
clearAriaLabel,
nativeInputAttributes,
style,
+ prefix,
+ suffix,
...rest
}: InputProps,
ref: Ref
@@ -101,6 +103,8 @@ const Input = React.forwardRef(
clearAriaLabel,
nativeInputAttributes,
style,
+ prefix,
+ suffix,
}}
className={clsx(styles.root, baseProps.className)}
__inheritFormFieldProps={true}
diff --git a/src/input/interfaces.ts b/src/input/interfaces.ts
index 86a9cbeffd..1f75e611f5 100644
--- a/src/input/interfaces.ts
+++ b/src/input/interfaces.ts
@@ -1,5 +1,7 @@
// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
// SPDX-License-Identifier: Apache-2.0
+import React from 'react';
+
import { BaseComponentProps } from '../types/base-component';
import { BaseKeyDetail, CancelableEventHandler, NonCancelableEventHandler } from '../types/events';
import { FormFieldValidationControlProps } from '../types/form-field';
@@ -188,6 +190,50 @@ export interface InputProps
* @awsuiSystem core
*/
style?: InputProps.Style;
+
+ /**
+ * Content rendered in a shaded cell on the leading edge of the input field,
+ * before the editable value.
+ *
+ * Accepts any React node — plain text, a short symbol, an icon, or a badge.
+ * Use for contextual decorators such as currency symbols (`$`, `€`),
+ * URL schemes (`https://`), path prefixes (`/api/v2/`), or an icon that
+ * clarifies the expected data type.
+ *
+ * The adornment is purely decorative — it does not mutate the `value` prop
+ * or the `onChange` detail. Wrap any icon or custom element in
+ * `aria-hidden="true"` if it is already conveyed by the visible
+ * `` label, so screen reader users are not double-announced.
+ * Ensure the unit or context is also reflected in the visible label
+ * (for example, label = "Amount in US dollars", not just "Amount").
+ *
+ * In RTL locales the `prefix` cell appears on the trailing edge.
+ * "prefix" means "before the value" semantically, not "on the left".
+ *
+ * Don't use `prefix` as a substitute for a visible label.
+ *
+ * @see suffix
+ */
+ prefix?: React.ReactNode;
+
+ /**
+ * Content rendered in a shaded cell on the trailing edge of the input field,
+ * after the editable value.
+ *
+ * Accepts any React node — plain text, a short unit abbreviation, an icon,
+ * or a badge. Use for units of measure (`%`, `ms`, `TB/mo`, `GB`, `req/s`,
+ * `days`), currency codes (`USD`, `EUR`), degree notation (`°C`, `°E`), or
+ * an icon indicating a secondary action.
+ *
+ * The adornment is purely decorative — it does not mutate the `value` prop
+ * or the `onChange` detail. Ensure the unit is also reflected in the visible
+ * `` label (for example, label = "CPU utilization (percent)").
+ *
+ * In RTL locales the `suffix` cell appears on the leading edge.
+ *
+ * @see prefix
+ */
+ suffix?: React.ReactNode;
}
export namespace InputProps {
diff --git a/src/input/internal.tsx b/src/input/internal.tsx
index 7dad564dbe..516da8f39d 100644
--- a/src/input/internal.tsx
+++ b/src/input/internal.tsx
@@ -57,6 +57,9 @@ export interface InternalInputProps
__skipNativeAttributesWarnings?: SkipWarnings;
__inlineLabelText?: string;
__fullWidth?: boolean;
+
+ prefix?: React.ReactNode;
+ suffix?: React.ReactNode;
}
function InternalInput(
@@ -101,6 +104,8 @@ function InternalInput(
__inlineLabelText,
__fullWidth,
style,
+ prefix,
+ suffix,
...rest
}: InternalInputProps,
ref: Ref
@@ -142,6 +147,7 @@ function InternalInput(
__rightIcon && styles['input-has-icon-right'],
__leftIcon && styles['input-has-icon-left'],
__noBorderRadius && styles['input-has-no-border-radius'],
+ (prefix !== undefined || suffix !== undefined) && styles['input-adorned'],
{
[styles['input-readonly']]: readOnly,
[styles['input-invalid']]: invalid,
@@ -215,6 +221,25 @@ function InternalInput(
/>
);
+ // The input element, optionally wrapped in an inline-label structure.
+ const inputWithLabel = __inlineLabelText ? (
+
+
+ {__inlineLabelText}
+
+
+ {mainInput}
+
+
+ ) : (
+ mainInput
+ );
+
return (
)}
- {__inlineLabelText ? (
-
-
- {__inlineLabelText}
-
-
- {mainInput}
-
+ {prefix !== undefined || suffix !== undefined ? (
+ // One flex bar owns the border, radius, background, and focus ring.
+ // Layout: [prefix][divider][input][divider][suffix]
+ // Dividers only render on sides that have an adornment.
+
+ {prefix !== undefined && (
+
+ {prefix}
+
+ )}
+ {prefix !== undefined && }
+ {inputWithLabel}
+ {suffix !== undefined && }
+ {suffix !== undefined && (
+
+ {suffix}
+
+ )}
) : (
- mainInput
+ inputWithLabel
)}
{__rightIcon && (