Skip to content

Commit cdded25

Browse files
authored
fix(react-spinbutton): fix read-only functionality (#35761)
1 parent 15159ba commit cdded25

5 files changed

Lines changed: 72 additions & 0 deletions

File tree

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
{
2+
"type": "patch",
3+
"comment": "fix: fix read-only functionality",
4+
"packageName": "@fluentui/react-spinbutton",
5+
"email": "dmytrokirpa@microsoft.com",
6+
"dependentChangeType": "patch"
7+
}

packages/react-components/react-spinbutton/library/src/components/SpinButton/SpinButton.test.tsx

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -59,6 +59,25 @@ describe('SpinButton', () => {
5959
expect(decrementButton.disabled).toEqual(true);
6060
});
6161

62+
it('can be read-only', () => {
63+
const { getAllByRole } = render(<SpinButton readOnly={true} defaultValue={1} />);
64+
65+
const spinButtonInput = getSpinButtonInput();
66+
67+
expect(spinButtonInput.readOnly).toEqual(true);
68+
expect(spinButtonInput.value).toEqual('1');
69+
70+
const [incrementButton, decrementButton] = getAllByRole('button') as HTMLButtonElement[];
71+
expect(incrementButton.disabled).toEqual(true);
72+
expect(decrementButton.disabled).toEqual(true);
73+
74+
// Try to change the value via keyboard
75+
fireEvent.keyDown(spinButtonInput, { key: ArrowUp });
76+
77+
// Spin button input should not change value when readOnly is true
78+
expect(spinButtonInput.value).toEqual('1');
79+
});
80+
6281
describe('displayValue', () => {
6382
it('does not render `displayValue` when uncontrolled', () => {
6483
render(<SpinButton defaultValue={1} displayValue="$1.00" onChange={jest.fn()} />);

packages/react-components/react-spinbutton/library/src/components/SpinButton/useSpinButton.tsx

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -190,6 +190,10 @@ export const useSpinButton_unstable = (props: SpinButtonProps, ref: React.Ref<HT
190190
const handleKeyDown = (e: React.KeyboardEvent<HTMLInputElement>) => {
191191
let nextKeyboardSpinState: SpinButtonSpinState = 'rest';
192192

193+
if (e.currentTarget.readOnly) {
194+
return;
195+
}
196+
193197
if (e.key === ArrowUp) {
194198
stepValue(e, 'up', textValue);
195199
nextKeyboardSpinState = 'up';
@@ -308,6 +312,7 @@ export const useSpinButton_unstable = (props: SpinButtonProps, ref: React.Ref<HT
308312
tabIndex: -1,
309313
children: <ChevronUp16Regular />,
310314
disabled:
315+
nativeProps.primary.readOnly ||
311316
nativeProps.primary.disabled ||
312317
internalState.current.atBound === 'max' ||
313318
internalState.current.atBound === 'both',
@@ -321,6 +326,7 @@ export const useSpinButton_unstable = (props: SpinButtonProps, ref: React.Ref<HT
321326
tabIndex: -1,
322327
children: <ChevronDown16Regular />,
323328
disabled:
329+
nativeProps.primary.readOnly ||
324330
nativeProps.primary.disabled ||
325331
internalState.current.atBound === 'min' ||
326332
internalState.current.atBound === 'both',
Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
import * as React from 'react';
2+
import type { JSXElement } from '@fluentui/react-components';
3+
import { makeStyles, tokens, useId, Label, SpinButton } from '@fluentui/react-components';
4+
5+
const useLayoutStyles = makeStyles({
6+
base: {
7+
display: 'flex',
8+
flexDirection: 'column',
9+
maxWidth: '500px',
10+
11+
'> label': {
12+
marginBottom: tokens.spacingVerticalXXS,
13+
},
14+
},
15+
});
16+
17+
export const ReadOnly = (): JSXElement => {
18+
const layoutStyles = useLayoutStyles();
19+
const id = useId();
20+
21+
return (
22+
<div className={layoutStyles.base}>
23+
<Label htmlFor={id}>Read-Only</Label>
24+
<SpinButton readOnly id={id} />
25+
</div>
26+
);
27+
};
28+
29+
ReadOnly.parameters = {
30+
docs: {
31+
description: {
32+
story: [
33+
'SpinButton can be read-only which prevents user input',
34+
'but still allows the component to be focused and read by assistive technologies.',
35+
'This is different from disabled, which prevents all interaction with the component.',
36+
].join(' '),
37+
},
38+
},
39+
};

packages/react-components/react-spinbutton/stories/src/SpinButton/index.stories.tsx

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ export { Step } from './SpinButtonStep.stories';
1313
export { Size } from './SpinButtonSize.stories';
1414
export { Appearance } from './SpinButtonAppearance.stories';
1515
export { Disabled } from './SpinButtonDisabled.stories';
16+
export { ReadOnly } from './SpinButtonReadOnly.stories';
1617

1718
const meta: Meta = {
1819
title: 'Components/SpinButton',

0 commit comments

Comments
 (0)