diff --git a/packages/material-ui/src/Rating/Rating.js b/packages/material-ui/src/Rating/Rating.js
index a7703d7c2182ea..3eb36763cc5b0e 100644
--- a/packages/material-ui/src/Rating/Rating.js
+++ b/packages/material-ui/src/Rating/Rating.js
@@ -193,6 +193,116 @@ IconContainer.propTypes = {
value: PropTypes.number.isRequired,
};
+function RatingItem(props) {
+ const {
+ classes,
+ disabled,
+ emptyIcon,
+ focus,
+ getLabelText,
+ highlightSelectedOnly,
+ hover,
+ icon,
+ IconContainerComponent,
+ isActive,
+ itemValue,
+ labelProps,
+ name,
+ onBlur,
+ onChange,
+ onClick,
+ onFocus,
+ readOnly,
+ styleProps,
+ ratingValue,
+ ratingValueRounded,
+ } = props;
+
+ const isFilled = highlightSelectedOnly ? itemValue === ratingValue : itemValue <= ratingValue;
+ const isHovered = itemValue <= hover;
+ const isFocused = itemValue <= focus;
+ const isChecked = itemValue === ratingValueRounded;
+
+ const id = useId();
+ const container = (
+
+ {emptyIcon && !isFilled ? emptyIcon : icon}
+
+ );
+
+ if (readOnly) {
+ return {container};
+ }
+
+ return (
+
+
+ {container}
+ {getLabelText(itemValue)}
+
+
+
+ );
+}
+
+RatingItem.propTypes = {
+ classes: PropTypes.object.isRequired,
+ disabled: PropTypes.bool.isRequired,
+ emptyIcon: PropTypes.node,
+ focus: PropTypes.number.isRequired,
+ getLabelText: PropTypes.func.isRequired,
+ highlightSelectedOnly: PropTypes.bool.isRequired,
+ hover: PropTypes.number.isRequired,
+ icon: PropTypes.node,
+ IconContainerComponent: PropTypes.elementType.isRequired,
+ isActive: PropTypes.bool.isRequired,
+ itemValue: PropTypes.number.isRequired,
+ labelProps: PropTypes.object,
+ name: PropTypes.string,
+ onBlur: PropTypes.func.isRequired,
+ onChange: PropTypes.func.isRequired,
+ onClick: PropTypes.func.isRequired,
+ onFocus: PropTypes.func.isRequired,
+ ratingValue: PropTypes.number,
+ ratingValueRounded: PropTypes.number,
+ readOnly: PropTypes.bool.isRequired,
+ styleProps: PropTypes.object.isRequired,
+};
+
const defaultIcon = ;
const defaultEmptyIcon = ;
@@ -397,67 +507,6 @@ const Rating = React.forwardRef(function Rating(inProps, ref) {
const classes = useUtilityClasses(styleProps);
- const item = (state, labelProps) => {
- const id = `${name}-${String(state.value).replace('.', '-')}`;
- const container = (
-
- {emptyIcon && !state.filled ? emptyIcon : icon}
-
- );
-
- if (readOnly) {
- return (
-
- {container}
-
- );
- }
-
- return (
-
-
- {container}
- {getLabelText(state.value)}
-
-
-
- );
- };
-
return (
{
const itemValue = index + 1;
+ const ratingItemProps = {
+ classes,
+ disabled,
+ emptyIcon,
+ focus,
+ getLabelText,
+ highlightSelectedOnly,
+ hover,
+ icon,
+ IconContainerComponent,
+ name,
+ onBlur: handleBlur,
+ onChange: handleChange,
+ onClick: handleClear,
+ onFocus: handleFocus,
+ ratingValue: value,
+ ratingValueRounded: valueRounded,
+ readOnly,
+ styleProps,
+ };
+
+ const isActive = itemValue === Math.ceil(value) && (hover !== -1 || focus !== -1);
if (precision < 1) {
const items = Array.from(new Array(1 / precision));
- const iconActive = itemValue === Math.ceil(value) && (hover !== -1 || focus !== -1);
return (
{items.map(($, indexDecimal) => {
@@ -490,44 +560,42 @@ const Rating = React.forwardRef(function Rating(inProps, ref) {
precision,
);
- return item(
- {
- value: itemDecimalValue,
- filled: highlightSelectedOnly
- ? itemDecimalValue === value
- : itemDecimalValue <= value,
- hover: itemDecimalValue <= hover,
- focus: itemDecimalValue <= focus,
- checked: itemDecimalValue === valueRounded,
- },
- {
- style:
- items.length - 1 === indexDecimal
- ? {}
- : {
- width:
- itemDecimalValue === value
- ? `${(indexDecimal + 1) * precision * 100}%`
- : '0%',
- overflow: 'hidden',
- zIndex: 1,
- position: 'absolute',
- },
- },
+ return (
+
);
})}
);
}
- return item({
- value: itemValue,
- active: itemValue === value && (hover !== -1 || focus !== -1),
- filled: highlightSelectedOnly ? itemValue === value : itemValue <= value,
- hover: itemValue <= hover,
- focus: itemValue <= focus,
- checked: itemValue === valueRounded,
- });
+ return (
+
+ );
})}
{!readOnly && !disabled && (
', () => {
const handleChange = spy();
const { container } = render();
- fireEvent.click(container.querySelector('#rating-test-2'), {
+ fireEvent.click(container.querySelector('input[name="rating-test"][value="2"]'), {
clientX: 1,
});
@@ -77,7 +77,7 @@ describe('', () => {
it('should select the rating', () => {
const handleChange = spy();
const { container } = render();
- fireEvent.click(container.querySelector('#rating-test-3'));
+ fireEvent.click(container.querySelector('input[name="rating-test"][value="3"]'));
expect(handleChange.callCount).to.equal(1);
expect(handleChange.args[0][1]).to.deep.equal(3);
const checked = container.querySelector('input[name="rating-test"]:checked');
@@ -105,7 +105,7 @@ describe('', () => {
checked = container.querySelector('input[name="rating-test"]:checked');
expect(checked.value).to.equal('3');
- fireEvent.click(container.querySelector('#rating-test-2'));
+ fireEvent.click(container.querySelector('input[name="rating-test"][value="2"]'));
checked = container.querySelector('input[name="rating-test"]:checked');
expect(checked.value).to.equal('2');
});
diff --git a/test/regressions/fixtures/Rating/PreciseFocusVisibleRating.js b/test/regressions/fixtures/Rating/PreciseFocusVisibleRating.js
new file mode 100644
index 00000000000000..03c61edd42f721
--- /dev/null
+++ b/test/regressions/fixtures/Rating/PreciseFocusVisibleRating.js
@@ -0,0 +1,6 @@
+import * as React from 'react';
+import Rating from '@material-ui/core/Rating';
+
+export default function FocusVisibleRating() {
+ return ;
+}
diff --git a/test/regressions/index.test.js b/test/regressions/index.test.js
index 86daafffced0e7..b15cc2e3c75475 100644
--- a/test/regressions/index.test.js
+++ b/test/regressions/index.test.js
@@ -100,6 +100,17 @@ async function main() {
await page.keyboard.press('ArrowLeft');
await takeScreenshot({ testcase, route: '/regression-Rating/FocusVisibleRating3' });
});
+
+ it('should handle focus-visible with precise ratings correctly', async () => {
+ const index = routes.findIndex(
+ (route) => route === '/regression-Rating/PreciseFocusVisibleRating',
+ );
+ const testcase = await renderFixture(index);
+ await page.keyboard.press('Tab');
+ await takeScreenshot({ testcase, route: '/regression-Rating/PreciseFocusVisibleRating2' });
+ await page.keyboard.press('ArrowRight');
+ await takeScreenshot({ testcase, route: '/regression-Rating/PreciseFocusVisibleRating3' });
+ });
});
describe('DateTimePicker', () => {