Skip to content
Merged
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
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
{
Comment thread
dmytrokirpa marked this conversation as resolved.
"type": "patch",
"comment": "fix: fix read-only functionality",
"packageName": "@fluentui/react-spinbutton",
"email": "dmytrokirpa@microsoft.com",
"dependentChangeType": "patch"
}
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,25 @@ describe('SpinButton', () => {
expect(decrementButton.disabled).toEqual(true);
});

it('can be read-only', () => {
const { getAllByRole } = render(<SpinButton readOnly={true} defaultValue={1} />);

const spinButtonInput = getSpinButtonInput();

expect(spinButtonInput.readOnly).toEqual(true);
expect(spinButtonInput.value).toEqual('1');

const [incrementButton, decrementButton] = getAllByRole('button') as HTMLButtonElement[];
expect(incrementButton.disabled).toEqual(true);
expect(decrementButton.disabled).toEqual(true);

// Try to change the value via keyboard
fireEvent.keyDown(spinButtonInput, { key: ArrowUp });

// Spin button input should not change value when readOnly is true
expect(spinButtonInput.value).toEqual('1');
});

describe('displayValue', () => {
it('does not render `displayValue` when uncontrolled', () => {
render(<SpinButton defaultValue={1} displayValue="$1.00" onChange={jest.fn()} />);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -190,6 +190,10 @@ export const useSpinButton_unstable = (props: SpinButtonProps, ref: React.Ref<HT
const handleKeyDown = (e: React.KeyboardEvent<HTMLInputElement>) => {
let nextKeyboardSpinState: SpinButtonSpinState = 'rest';

if (e.currentTarget.readOnly) {
return;
}

if (e.key === ArrowUp) {
stepValue(e, 'up', textValue);
nextKeyboardSpinState = 'up';
Expand Down Expand Up @@ -308,6 +312,7 @@ export const useSpinButton_unstable = (props: SpinButtonProps, ref: React.Ref<HT
tabIndex: -1,
children: <ChevronUp16Regular />,
disabled:
nativeProps.primary.readOnly ||
nativeProps.primary.disabled ||
internalState.current.atBound === 'max' ||
internalState.current.atBound === 'both',
Expand All @@ -321,6 +326,7 @@ export const useSpinButton_unstable = (props: SpinButtonProps, ref: React.Ref<HT
tabIndex: -1,
children: <ChevronDown16Regular />,
disabled:
nativeProps.primary.readOnly ||
nativeProps.primary.disabled ||
internalState.current.atBound === 'min' ||
internalState.current.atBound === 'both',
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
import * as React from 'react';
import type { JSXElement } from '@fluentui/react-components';
import { makeStyles, tokens, useId, Label, SpinButton } from '@fluentui/react-components';

const useLayoutStyles = makeStyles({
base: {
display: 'flex',
flexDirection: 'column',
maxWidth: '500px',

'> label': {
marginBottom: tokens.spacingVerticalXXS,
},
},
});

export const ReadOnly = (): JSXElement => {
const layoutStyles = useLayoutStyles();
const id = useId();

return (
<div className={layoutStyles.base}>
<Label htmlFor={id}>Read-Only</Label>
<SpinButton readOnly id={id} />
</div>
);
};

ReadOnly.parameters = {
docs: {
description: {
story: [
'SpinButton can be read-only which prevents user input',
'but still allows the component to be focused and read by assistive technologies.',
'This is different from disabled, which prevents all interaction with the component.',
].join(' '),
},
},
};
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ export { Step } from './SpinButtonStep.stories';
export { Size } from './SpinButtonSize.stories';
export { Appearance } from './SpinButtonAppearance.stories';
export { Disabled } from './SpinButtonDisabled.stories';
export { ReadOnly } from './SpinButtonReadOnly.stories';

const meta: Meta = {
title: 'Components/SpinButton',
Expand Down