diff --git a/.changeset/fix-mui-button-nested-interactive.md b/.changeset/fix-mui-button-nested-interactive.md new file mode 100644 index 0000000000000..245652b172d66 --- /dev/null +++ b/.changeset/fix-mui-button-nested-interactive.md @@ -0,0 +1,11 @@ +--- +"@refinedev/mui": patch +--- + +fix(mui): remove nested interactive elements from button components for better accessibility + +Fixed accessibility issue where Material UI button components (CreateButton, EditButton, CloneButton, ListButton, ShowButton) rendered a button element nested inside an anchor element. This caused problems with keyboard navigation (buttons were focused twice) and violated accessibility guidelines. + +Changed implementation to use Material UI's Button `component` prop to render the button directly as a link, eliminating the nested structure. + +[Resolves #7085](https://github.com/refinedev/refine/issues/7085) diff --git a/packages/inferencer/src/inferencers/mui/__snapshots__/index.test.tsx.snap b/packages/inferencer/src/inferencers/mui/__snapshots__/index.test.tsx.snap index b840c574fe135..7b7eaec42a940 100644 --- a/packages/inferencer/src/inferencers/mui/__snapshots__/index.test.tsx.snap +++ b/packages/inferencer/src/inferencers/mui/__snapshots__/index.test.tsx.snap @@ -1687,50 +1687,42 @@ exports[`MuiInferencer > should match the snapshot 1`] = ` tabindex="-1" > - + + - + +
+ + - + +
+ + + + Posts + + + + Create
@@ -1718,50 +1713,40 @@ exports[`MuiListInferencer > should match the snapshot 1`] = ` tabindex="-1" > - + + - + + @@ -1997,50 +1982,40 @@ exports[`MuiListInferencer > should match the snapshot 1`] = ` tabindex="-1" > - + + - + + diff --git a/packages/inferencer/src/inferencers/mui/__tests__/__snapshots__/show.test.tsx.snap b/packages/inferencer/src/inferencers/mui/__tests__/__snapshots__/show.test.tsx.snap index 43a3d93517e95..9eac00eb44fce 100644 --- a/packages/inferencer/src/inferencers/mui/__tests__/__snapshots__/show.test.tsx.snap +++ b/packages/inferencer/src/inferencers/mui/__tests__/__snapshots__/show.test.tsx.snap @@ -95,32 +95,27 @@ exports[`MuiShowInferencer > should match the snapshot 1`] = ` class="MuiBox-root css-10egq61" > - + + + + Posts - + {hideText ? ( + + ) : ( + children ?? label + )} + ); }; diff --git a/packages/mui/src/components/buttons/create/index.tsx b/packages/mui/src/components/buttons/create/index.tsx index 65b72471067e0..b046bcaacc254 100644 --- a/packages/mui/src/components/buttons/create/index.tsx +++ b/packages/mui/src/components/buttons/create/index.tsx @@ -42,9 +42,17 @@ export const CreateButton: React.FC = ({ const { sx, ...restProps } = props; return ( - } + title={title} + variant="contained" + sx={{ minWidth: 0, textDecoration: "none", ...sx }} + data-testid={RefineButtonTestIds.CreateButton} + className={RefineButtonClassNames.CreateButton} onClick={(e: React.MouseEvent) => { if (isDisabled) { e.preventDefault(); @@ -55,24 +63,13 @@ export const CreateButton: React.FC = ({ onClick(e); } }} - style={{ textDecoration: "none" }} + {...restProps} > - - + {hideText ? ( + + ) : ( + children ?? label + )} + ); }; diff --git a/packages/mui/src/components/buttons/edit/index.tsx b/packages/mui/src/components/buttons/edit/index.tsx index a188eb3c04a18..e2853f83f36ff 100644 --- a/packages/mui/src/components/buttons/edit/index.tsx +++ b/packages/mui/src/components/buttons/edit/index.tsx @@ -43,9 +43,20 @@ export const EditButton: React.FC = ({ const { sx, ...restProps } = rest; return ( - + ) + } + title={title} + sx={{ minWidth: 0, textDecoration: "none", ...sx }} + data-testid={RefineButtonTestIds.EditButton} + className={RefineButtonClassNames.EditButton} onClick={(e: React.MouseEvent) => { if (isDisabled) { e.preventDefault(); @@ -56,27 +67,13 @@ export const EditButton: React.FC = ({ onClick(e); } }} - style={{ textDecoration: "none" }} + {...restProps} > - - + {hideText ? ( + + ) : ( + children ?? label + )} + ); }; diff --git a/packages/mui/src/components/buttons/list/index.tsx b/packages/mui/src/components/buttons/list/index.tsx index f350193ae4660..0b152efdadb71 100644 --- a/packages/mui/src/components/buttons/list/index.tsx +++ b/packages/mui/src/components/buttons/list/index.tsx @@ -41,9 +41,16 @@ export const ListButton: React.FC = ({ const { sx, ...restProps } = rest; return ( - } + title={title} + sx={{ minWidth: 0, textDecoration: "none", ...sx }} + data-testid={RefineButtonTestIds.ListButton} + className={RefineButtonClassNames.ListButton} onClick={(e: React.MouseEvent) => { if (isDisabled) { e.preventDefault(); @@ -54,23 +61,13 @@ export const ListButton: React.FC = ({ onClick(e); } }} - style={{ textDecoration: "none" }} + {...restProps} > - - + {hideText ? ( + + ) : ( + children ?? label + )} + ); }; diff --git a/packages/mui/src/components/buttons/show/index.tsx b/packages/mui/src/components/buttons/show/index.tsx index ebc80cfe4a202..63a326ec021fc 100644 --- a/packages/mui/src/components/buttons/show/index.tsx +++ b/packages/mui/src/components/buttons/show/index.tsx @@ -43,9 +43,16 @@ export const ShowButton: React.FC = ({ const { sx, ...restProps } = rest; return ( - } + title={title} + sx={{ minWidth: 0, textDecoration: "none", ...sx }} + data-testid={RefineButtonTestIds.ShowButton} + className={RefineButtonClassNames.ShowButton} onClick={(e: React.MouseEvent) => { if (isDisabled) { e.preventDefault(); @@ -56,23 +63,13 @@ export const ShowButton: React.FC = ({ onClick(e); } }} - style={{ textDecoration: "none" }} + {...restProps} > - - + {hideText ? ( + + ) : ( + children ?? label + )} + ); }; diff --git a/packages/mui/src/components/themedLayout/title/index.tsx b/packages/mui/src/components/themedLayout/title/index.tsx index 467cfe94f7411..d3bc2ba3c2b3f 100644 --- a/packages/mui/src/components/themedLayout/title/index.tsx +++ b/packages/mui/src/components/themedLayout/title/index.tsx @@ -23,32 +23,33 @@ export const ThemedTitle: React.FC = ({ const Link = useLink(); return ( - - - - {icon} - - {!collapsed && ( - - {text} - - )} - - + + + {icon} + + {!collapsed && ( + + {text} + + )} + ); };