diff --git a/.changeset/warm-points-punch.md b/.changeset/warm-points-punch.md
new file mode 100644
index 00000000000..c53b52d0061
--- /dev/null
+++ b/.changeset/warm-points-punch.md
@@ -0,0 +1,5 @@
+---
+'@shopify/polaris': patch
+---
+
+Updated `TextField` of `type` `number` to focus when a `Spinner` button is clicked
diff --git a/polaris-react/src/components/TextField/TextField.scss b/polaris-react/src/components/TextField/TextField.scss
index 69818ed8ce9..7bd770b94a8 100644
--- a/polaris-react/src/components/TextField/TextField.scss
+++ b/polaris-react/src/components/TextField/TextField.scss
@@ -70,10 +70,11 @@ $spinner-icon-size: 12px;
.focus > .Input,
.focus > .VerticalContent,
+.TextField:focus-within > .Input,
.Input:focus-visible {
outline: none;
- // stylelint-disable-next-line selector-max-class, selector-max-combinators -- generated by polaris-migrator DO NOT COPY
+ // stylelint-disable-next-line selector-max-class, selector-max-combinators, selector-max-specificity -- outline based on child focus requires complex specificity
~ .Backdrop {
// stylelint-disable-next-line -- generated by polaris-migrator DO NOT COPY
@include focus-ring($style: 'focused');
diff --git a/polaris-react/src/components/TextField/TextField.stories.tsx b/polaris-react/src/components/TextField/TextField.stories.tsx
index e32a5e2b30d..ce3972e5845 100644
--- a/polaris-react/src/components/TextField/TextField.stories.tsx
+++ b/polaris-react/src/components/TextField/TextField.stories.tsx
@@ -4,14 +4,17 @@ import {
Button,
LegacyCard,
ChoiceList,
+ Form,
FormLayout,
InlineError,
Select,
LegacyStack,
Tag,
+ Text,
TextField,
Icon,
Tooltip,
+ VerticalStack,
} from '@shopify/polaris';
import {
DeleteMinor,
@@ -829,3 +832,37 @@ export function All() {
);
}
+
+export function WithFormSubmit() {
+ const [adjustment, setAdjustment] = useState('0');
+ const [onHandTotal, setOnHandTotal] = useState(0);
+
+ return (
+
+
+
+ );
+}
diff --git a/polaris-react/src/components/TextField/TextField.tsx b/polaris-react/src/components/TextField/TextField.tsx
index cfa4da3b22e..3de0a291139 100644
--- a/polaris-react/src/components/TextField/TextField.tsx
+++ b/polaris-react/src/components/TextField/TextField.tsx
@@ -401,11 +401,11 @@ export function TextField({
],
);
- const handleButtonRelease = useCallback(() => {
+ const handleSpinnerButtonRelease = useCallback(() => {
clearTimeout(buttonPressTimer.current);
}, []);
- const handleButtonPress: SpinnerProps['onMouseDown'] = useCallback(
+ const handleSpinnerButtonPress: SpinnerProps['onMouseDown'] = useCallback(
(onChange) => {
const minInterval = 50;
const decrementBy = 10;
@@ -422,11 +422,11 @@ export function TextField({
buttonPressTimer.current = window.setTimeout(onChangeInterval, interval);
- document.addEventListener('mouseup', handleButtonRelease, {
+ document.addEventListener('mouseup', handleSpinnerButtonRelease, {
once: true,
});
},
- [handleButtonRelease],
+ [handleSpinnerButtonRelease],
);
const spinnerMarkup =
@@ -434,8 +434,8 @@ export function TextField({
@@ -654,6 +654,7 @@ export function TextField({
}
setFocus(true);
+ inputRef.current?.focus();
}
function handleClearButtonPress() {
diff --git a/polaris-react/src/components/TextField/components/Spinner/Spinner.tsx b/polaris-react/src/components/TextField/components/Spinner/Spinner.tsx
index 9a4d8b36034..b3a108ced04 100644
--- a/polaris-react/src/components/TextField/components/Spinner/Spinner.tsx
+++ b/polaris-react/src/components/TextField/components/Spinner/Spinner.tsx
@@ -31,7 +31,7 @@ export const Spinner = React.forwardRef(
function handleMouseDown(onChange: HandleStepFn) {
return (event: React.MouseEvent) => {
if (event.button !== 0) return;
- onMouseDown(onChange);
+ onMouseDown?.(onChange);
};
}
diff --git a/polaris-react/src/components/TextField/tests/TextField.test.tsx b/polaris-react/src/components/TextField/tests/TextField.test.tsx
index 6251ea884a8..668a0b39f0f 100644
--- a/polaris-react/src/components/TextField/tests/TextField.test.tsx
+++ b/polaris-react/src/components/TextField/tests/TextField.test.tsx
@@ -120,6 +120,24 @@ describe('', () => {
expect(onClick).toHaveBeenCalled();
});
+ it('focuses the text field when the spinner is clicked', () => {
+ const event = new MouseEvent('click', {
+ view: window,
+ bubbles: true,
+ cancelable: true,
+ });
+ const textField = mountWithApp(
+ ,
+ );
+
+ textField
+ .find(Spinner)!
+ .findAll('div', {role: 'button'})[0]!
+ .domNode?.dispatchEvent(event);
+
+ expect(document.activeElement).toBe(textField.find('input')!.domNode);
+ });
+
it('does not bubble up to the parent element when it occurs in an element other than the input', () => {
const onClick = jest.fn();
const children = 'vertical-content-children';
@@ -221,7 +239,7 @@ describe('', () => {
);
expect(textField).toContainReactComponent('input', {
- id: ':ra:',
+ id: expect.any(String),
});
});
@@ -330,11 +348,12 @@ describe('', () => {
helpText="Some help"
onChange={noop}
autoComplete="off"
+ id="textField"
/>,
);
expect(textField).toContainReactComponent('input', {
- 'aria-describedby': ':ri:HelpText',
+ 'aria-describedby': 'textFieldHelpText',
});
expect(textField.find('div')).toContainReactText('Some help');
});
@@ -369,11 +388,12 @@ describe('', () => {
error="Some error"
onChange={noop}
autoComplete="off"
+ id="textField"
/>,
);
expect(textField).toContainReactComponent('input', {
- 'aria-describedby': ':rk:Error',
+ 'aria-describedby': 'textFieldError',
});
});
@@ -407,11 +427,12 @@ describe('', () => {
helpText="Some help"
onChange={noop}
autoComplete="off"
+ id="textField"
/>,
);
expect(textField).toContainReactComponent('input', {
- 'aria-describedby': ':rm:Error :rm:HelpText',
+ 'aria-describedby': 'textFieldError textFieldHelpText',
});
expect(textField.find('div')).toContainReactText('Some error');