diff --git a/apps/vr-tests-react-components/package.json b/apps/vr-tests-react-components/package.json
index 61614353572ef..d94c76f2ef254 100644
--- a/apps/vr-tests-react-components/package.json
+++ b/apps/vr-tests-react-components/package.json
@@ -49,6 +49,7 @@
"@fluentui/react-progress": "*",
"@fluentui/react-provider": "*",
"@fluentui/react-radio": "*",
+ "@fluentui/react-rating-preview": "*",
"@fluentui/react-search-preview": "*",
"@fluentui/react-select": "*",
"@fluentui/react-shared-contexts": "*",
diff --git a/apps/vr-tests-react-components/src/stories/Rating.stories.tsx b/apps/vr-tests-react-components/src/stories/Rating.stories.tsx
new file mode 100644
index 0000000000000..e5fef1f5696b6
--- /dev/null
+++ b/apps/vr-tests-react-components/src/stories/Rating.stories.tsx
@@ -0,0 +1,58 @@
+import * as React from 'react';
+import { Steps, StoryWright } from 'storywright';
+import { storiesOf } from '@storybook/react';
+import { CircleRegular, CircleFilled, SquareRegular, SquareFilled } from '@fluentui/react-icons';
+import { Rating } from '@fluentui/react-rating-preview';
+import { TestWrapperDecoratorFixedWidth } from '../utilities/TestWrapperDecorator';
+
+storiesOf('Rating Converged', module)
+ .addDecorator(TestWrapperDecoratorFixedWidth)
+ .addDecorator(story => (
+
+ {story()}
+
+ ))
+ .addStory('Neutral Rating with half star', () => , {
+ includeHighContrast: true,
+ includeDarkMode: true,
+ })
+ .addStory('Brand Rating with half star', () => , {
+ includeHighContrast: true,
+ includeDarkMode: true,
+ })
+ .addStory('Marigold Rating with half star', () => , {
+ includeHighContrast: true,
+ includeDarkMode: true,
+ });
+
+storiesOf('Rating Converged', module)
+ .addDecorator(TestWrapperDecoratorFixedWidth)
+ .addDecorator(story => (
+ {story()}
+ ))
+ .addStory('Rating size small', () => , {})
+ .addStory('Rating size medium', () => , {})
+ .addStory('Rating size large', () => , {})
+ .addStory('Rating size extra-large', () => , {});
+
+storiesOf('Rating Converged', module)
+ .addDecorator(TestWrapperDecoratorFixedWidth)
+ .addDecorator(story => (
+ {story()}
+ ))
+ .addStory(
+ 'Rating with custom circle icons',
+ () => } iconOutline={} defaultValue={3.5} />,
+ {},
+ )
+ .addStory(
+ 'Rating with custom square icons',
+ () => } iconOutline={} defaultValue={3.5} />,
+ {},
+ );
diff --git a/apps/vr-tests-react-components/src/stories/RatingDisplay.stories.tsx b/apps/vr-tests-react-components/src/stories/RatingDisplay.stories.tsx
new file mode 100644
index 0000000000000..ccfd4dda9b8df
--- /dev/null
+++ b/apps/vr-tests-react-components/src/stories/RatingDisplay.stories.tsx
@@ -0,0 +1,34 @@
+import * as React from 'react';
+import { storiesOf } from '@storybook/react';
+import { CircleFilled, SquareFilled } from '@fluentui/react-icons';
+import { RatingDisplay } from '@fluentui/react-rating-preview';
+import { TestWrapperDecoratorFixedWidth } from '../utilities/TestWrapperDecorator';
+import { Steps, StoryWright } from 'storywright';
+
+storiesOf('RatingDisplay Converged', module)
+ .addDecorator(TestWrapperDecoratorFixedWidth)
+ .addDecorator(story => (
+ {story()}
+ ))
+ .addStory('no value', () => )
+ .addStory('size small with value and count', () => )
+ .addStory('size medium with value and count', () => )
+ .addStory('size large with value and count', () => )
+ .addStory('size extra-large with value and count', () => )
+ .addStory('custom circle icons', () => } value={3} />)
+ .addStory('custom square icons', () => } value={3} />)
+ .addStory('rounded up', () => )
+ .addStory('rounded down', () => )
+ .addStory('Neutral with half value', () => , {
+ includeHighContrast: true,
+ includeDarkMode: true,
+ })
+ .addStory('Brand with half value', () => , {
+ includeHighContrast: true,
+ includeDarkMode: true,
+ })
+ .addStory('Marigold with half value', () => , {
+ includeHighContrast: true,
+ includeDarkMode: true,
+ })
+ .addStory('Compact', () => , {});
diff --git a/change/@fluentui-react-rating-preview-53ee592c-ae0d-4b3b-9828-2c08ee70057e.json b/change/@fluentui-react-rating-preview-53ee592c-ae0d-4b3b-9828-2c08ee70057e.json
new file mode 100644
index 0000000000000..60c0507244e1e
--- /dev/null
+++ b/change/@fluentui-react-rating-preview-53ee592c-ae0d-4b3b-9828-2c08ee70057e.json
@@ -0,0 +1,7 @@
+{
+ "type": "patch",
+ "comment": "chore: add accesibility props to Rating and RatingDisplay",
+ "packageName": "@fluentui/react-rating-preview",
+ "email": "ololubek@microsoft.com",
+ "dependentChangeType": "patch"
+}
diff --git a/packages/react-components/react-rating-preview/src/components/Rating/Rating.test.tsx b/packages/react-components/react-rating-preview/src/components/Rating/Rating.test.tsx
index 1d7da749a3300..8c4b6bb5017c9 100644
--- a/packages/react-components/react-rating-preview/src/components/Rating/Rating.test.tsx
+++ b/packages/react-components/react-rating-preview/src/components/Rating/Rating.test.tsx
@@ -1,15 +1,56 @@
import * as React from 'react';
import { render } from '@testing-library/react';
-// import { isConformant } from '../../testing/isConformant';
+import { isConformant } from '../../testing/isConformant';
import { Rating } from './Rating';
describe('Rating', () => {
- // isConformant({
- // Component: Rating,
- // displayName: 'Rating',
- // });
- it('renders a default state', () => {
- const result = render(Default RatingItem);
- expect(result.container).toMatchSnapshot();
+ isConformant({
+ Component: Rating,
+ displayName: 'Rating',
+ // TODO re-enable consistent-callback-args once https://github.com/microsoft/fluentui/pull/30376 is merged
+ disabledTests: ['consistent-callback-args'],
+ });
+ it('respects a default value', () => {
+ const { getAllByRole } = render();
+ const checkedItems = getAllByRole('radio').filter(item => (item as HTMLInputElement).checked);
+ expect(checkedItems[0].getAttribute('value')).toBe('3');
+ expect(checkedItems.length).toEqual(1);
+ });
+ it('only sets the selected rating item to checked', () => {
+ const { getAllByRole } = render();
+ const checkedItems = getAllByRole('radio').filter(item => (item as HTMLInputElement).checked);
+ expect(checkedItems[0].getAttribute('value')).toBe('3');
+ expect(checkedItems.length).toEqual(1);
+ });
+ it('renders the correct number of items', () => {
+ const { getAllByRole } = render();
+ const items = getAllByRole('radio');
+ expect(items.length).toEqual(10);
+ });
+ it('calle onChange when a rating is clicked', () => {
+ const onChange = jest.fn();
+ const { getAllByRole } = render();
+ const items = getAllByRole('radio');
+ items[0].click();
+ expect(onChange).toHaveBeenCalledTimes(1);
+ });
+ it('does not call onChange when a rating is clicked and the value is the same', () => {
+ const onChange = jest.fn();
+ const { getAllByRole } = render();
+ const items = getAllByRole('radio');
+ items[2].click();
+ expect(onChange).toHaveBeenCalledTimes(0);
+ });
+ it('calls onChange with the correct value when a rating is clicked', () => {
+ const onChange = jest.fn();
+ const { getAllByRole } = render();
+ const items = getAllByRole('radio');
+ items[3].click();
+ items[2].click();
+ items[1].click();
+ expect(onChange).toHaveBeenCalledTimes(3);
+ expect(onChange.mock.calls[0][1].value).toBe(4);
+ expect(onChange.mock.calls[1][1].value).toBe(3);
+ expect(onChange.mock.calls[2][1].value).toBe(2);
});
});
diff --git a/packages/react-components/react-rating-preview/src/components/Rating/__snapshots__/Rating.test.tsx.snap b/packages/react-components/react-rating-preview/src/components/Rating/__snapshots__/Rating.test.tsx.snap
deleted file mode 100644
index 4cae3c2a39910..0000000000000
--- a/packages/react-components/react-rating-preview/src/components/Rating/__snapshots__/Rating.test.tsx.snap
+++ /dev/null
@@ -1,12 +0,0 @@
-// Jest Snapshot v1, https://goo.gl/fbAQLP
-
-exports[`Rating renders a default state 1`] = `
-
-
- Default RatingItem
-
-
-`;
diff --git a/packages/react-components/react-rating-preview/src/components/RatingDisplay/RatingDisplay.test.tsx b/packages/react-components/react-rating-preview/src/components/RatingDisplay/RatingDisplay.test.tsx
index 2a1f88f7cb36b..9fdcea6235e6a 100644
--- a/packages/react-components/react-rating-preview/src/components/RatingDisplay/RatingDisplay.test.tsx
+++ b/packages/react-components/react-rating-preview/src/components/RatingDisplay/RatingDisplay.test.tsx
@@ -1,18 +1,41 @@
import * as React from 'react';
import { render } from '@testing-library/react';
-//import { isConformant } from '../../testing/isConformant';
+import { isConformant } from '../../testing/isConformant';
import { RatingDisplay } from './RatingDisplay';
describe('RatingDisplay', () => {
- // isConformant({
- // Component: RatingDisplay,
- // displayName: 'RatingDisplay',
- // });
-
- // TODO add more tests here, and create visual regression tests in /apps/vr-tests
-
- it('renders a default state', () => {
- const result = render(Default RatingDisplay);
- expect(result.container).toMatchSnapshot();
+ isConformant({
+ Component: RatingDisplay,
+ displayName: 'RatingDisplay',
+ requiredProps: { count: 1160, value: 4.5 },
+ });
+ it('renders the correct number of items', () => {
+ const { getByRole } = render();
+ expect(getByRole('img').children.length).toEqual(10);
+ });
+ it('renders the valueText slot when a value is provided', () => {
+ const { getByText } = render();
+ const valueText = getByText('3');
+ expect(valueText).toBeDefined();
+ expect(valueText.classList.contains('fui-RatingDisplay__valueText')).toBeTruthy();
+ });
+ it('does not render the valueText slot when a value is not provided', () => {
+ const { container } = render();
+ expect(container?.querySelector('.fui-RatingDisplay__valueText')).toBeNull();
+ });
+ it('renders the countText slot when a count is provided', () => {
+ const { getByText } = render();
+ const countText = getByText('1,160');
+ expect(countText).toBeDefined();
+ expect(countText.classList.contains('fui-RatingDisplay__countText')).toBeTruthy();
+ });
+ it('does not render the countText slot when a count is not provided', () => {
+ const { container } = render();
+ expect(container?.querySelector('.fui-RatingDisplay__countText')).toBeNull();
+ });
+ it('renders only one item when compact is true', () => {
+ const { getByRole } = render();
+ const items = getByRole('img');
+ expect(items.getElementsByClassName('fui-RatingItem').length).toEqual(1);
});
});
diff --git a/packages/react-components/react-rating-preview/src/components/RatingDisplay/__snapshots__/RatingDisplay.test.tsx.snap b/packages/react-components/react-rating-preview/src/components/RatingDisplay/__snapshots__/RatingDisplay.test.tsx.snap
deleted file mode 100644
index 47f6019ebf1ba..0000000000000
--- a/packages/react-components/react-rating-preview/src/components/RatingDisplay/__snapshots__/RatingDisplay.test.tsx.snap
+++ /dev/null
@@ -1,13 +0,0 @@
-// Jest Snapshot v1, https://goo.gl/fbAQLP
-
-exports[`RatingDisplay renders a default state 1`] = `
-
-
- Default RatingDisplay
-
-
-`;
diff --git a/packages/react-components/react-rating-preview/src/components/RatingItem/RatingItem.test.tsx b/packages/react-components/react-rating-preview/src/components/RatingItem/RatingItem.test.tsx
index 28f019552d428..d51bb8926ada1 100644
--- a/packages/react-components/react-rating-preview/src/components/RatingItem/RatingItem.test.tsx
+++ b/packages/react-components/react-rating-preview/src/components/RatingItem/RatingItem.test.tsx
@@ -1,10 +1,112 @@
import * as React from 'react';
import { render } from '@testing-library/react';
+import { isConformant } from '../../testing/isConformant';
import { RatingItem } from './RatingItem';
+import { RatingItemProvider } from '../../contexts/RatingItemContext';
+import { RatingItemContextValue } from './RatingItem.types';
+import { StarFilled, StarRegular } from '@fluentui/react-icons';
describe('RatingItem', () => {
- it('renders a default state', () => {
- const result = render(Default RatingItem);
- expect(result.container).toMatchSnapshot();
+ isConformant({
+ Component: RatingItem,
+ displayName: 'RatingItem',
+ // Need to disable this test because certain slots are not rendered without specific context values
+ disabledTests: ['component-has-static-classnames-object'],
+ });
+ it('does not render input elements when interactive is false', () => {
+ const contextValues: RatingItemContextValue = {
+ value: 3,
+ interactive: false,
+ iconFilled: ,
+ iconOutline: ,
+ color: 'neutral',
+ step: 1,
+ size: 'medium',
+ };
+ const { queryAllByRole } = render(
+
+
+ ,
+ );
+ const inputs = queryAllByRole('radio');
+ expect(inputs.length).toEqual(0);
+ });
+ it('renders only the full value input when step is 1 and interactive is true', () => {
+ const contextValues: RatingItemContextValue = {
+ value: 3,
+ interactive: true,
+ iconFilled: ,
+ iconOutline: ,
+ color: 'neutral',
+ step: 1,
+ size: 'medium',
+ };
+ const { getByRole } = render(
+
+
+ ,
+ );
+ const input = getByRole('radio') as HTMLInputElement;
+ expect(input.value).toEqual('2');
+ });
+ it('renders both the full value input and the half value input when step is 0.5 and interactive is true', () => {
+ const contextValues: RatingItemContextValue = {
+ value: 3,
+ interactive: true,
+ iconFilled: ,
+ iconOutline: ,
+ color: 'neutral',
+ step: 0.5,
+ size: 'medium',
+ };
+ const { getAllByRole } = render(
+
+
+ ,
+ );
+ const inputs = getAllByRole('radio') as HTMLInputElement[];
+ expect(inputs.length).toEqual(2);
+ expect(inputs[0].value).toEqual('1.5');
+ expect(inputs[1].value).toEqual('2');
+ });
+ it('sets the half value input to checked when the value is 2.5', () => {
+ const contextValues: RatingItemContextValue = {
+ value: 2.5,
+ interactive: true,
+ iconFilled: ,
+ iconOutline: ,
+ color: 'neutral',
+ step: 0.5,
+ size: 'medium',
+ };
+ const { queryAllByRole } = render(
+
+
+ ,
+ );
+ const inputs = queryAllByRole('radio') as HTMLInputElement[];
+ const checkedInputs = inputs.filter(input => input.checked);
+ expect(checkedInputs.length).toEqual(1);
+ expect(checkedInputs[0].value).toEqual(contextValues.value?.toString());
+ });
+ it('sets the full value input to checked when the value is 3', () => {
+ const contextValues: RatingItemContextValue = {
+ value: 3,
+ interactive: true,
+ iconFilled: ,
+ iconOutline: ,
+ color: 'neutral',
+ step: 1,
+ size: 'medium',
+ };
+ const { queryAllByRole } = render(
+
+
+ ,
+ );
+ const inputs = queryAllByRole('radio') as HTMLInputElement[];
+ const checkedInputs = inputs.filter(input => input.checked);
+ expect(checkedInputs.length).toEqual(1);
+ expect(checkedInputs[0].value).toEqual(contextValues.value?.toString());
});
});
diff --git a/packages/react-components/react-rating-preview/src/components/RatingItem/__snapshots__/RatingItem.test.tsx.snap b/packages/react-components/react-rating-preview/src/components/RatingItem/__snapshots__/RatingItem.test.tsx.snap
deleted file mode 100644
index 22912565ed2bc..0000000000000
--- a/packages/react-components/react-rating-preview/src/components/RatingItem/__snapshots__/RatingItem.test.tsx.snap
+++ /dev/null
@@ -1,29 +0,0 @@
-// Jest Snapshot v1, https://goo.gl/fbAQLP
-
-exports[`RatingItem renders a default state 1`] = `
-
-`;