diff --git a/.github/ISSUE_TEMPLATE/4.docs-feedback.yml b/.github/ISSUE_TEMPLATE/4.docs-feedback.yml
new file mode 100644
index 00000000000000..88fac0f863eb2e
--- /dev/null
+++ b/.github/ISSUE_TEMPLATE/4.docs-feedback.yml
@@ -0,0 +1,52 @@
+name: Docs feedback
+description: Improve documentation about MUI Core.
+labels: ['status: needs triage', 'support: docs-feedback']
+title: '[docs] '
+body:
+ - type: markdown
+ attributes:
+ value: |
+ Please provide a searchable summary of the issue in the title above ⬆️.
+
+ Thanks for contributing by creating an issue! ❤️
+ - type: checkboxes
+ attributes:
+ label: Duplicates
+ description: Please [search the history](https://github.com/mui/material-ui/issues) to see if an issue already exists for the same problem.
+ options:
+ - label: I have searched the existing issues
+ required: true
+
+ - type: input
+ id: page-url
+ attributes:
+ label: Related page
+ description: Which page of the documentation is this about?
+ placeholder: https://mui.com/material-ui/react-badge/
+ validations:
+ required: true
+
+ - type: dropdown
+ attributes:
+ label: Kind of issue
+ description: What kind of problem are you facing?
+ options:
+ - Unclear explanations
+ - Missing information
+ - Broken demonstration
+ - Other
+ validations:
+ required: true
+
+ - type: textarea
+ attributes:
+ label: Issue description
+ description: |
+ Let us know what went wrong when you were using this documentation and what we could do to improve it.
+ value: |
+ I was looking for ... and it appears that ...
+
+ - type: textarea
+ attributes:
+ label: Context 🔦
+ description: What are you trying to accomplish? What brought you to this page? Your context can help us to come up with solutions that benefit the community as a whole.
diff --git a/docs/data/material/guides/pickers-migration/pickers-migration.md b/docs/data/material/guides/pickers-migration/pickers-migration.md
index 93f2928fe7e0d6..a139b0fca05b9e 100644
--- a/docs/data/material/guides/pickers-migration/pickers-migration.md
+++ b/docs/data/material/guides/pickers-migration/pickers-migration.md
@@ -3,7 +3,7 @@
@material-ui/pickers was moved to the @mui/lab.
:::info
-**Stable package available**: The pickers are not available in `@mui/lab`after `v5.0.0-alpha.89`.
+**Stable package available**: The pickers are not available in `@mui/lab` after `v5.0.0-alpha.89`.
They have been moved from `@mui/lab` to the MUI X packages `@mui/x-date-pickers` and `@mui/x-date-pickers-pro`.
To migrate from `@mui/lab` to `@mui/x-date-pickers` you can follow the dedicated [migration guide](/x/react-date-pickers/migration-lab/).
:::
diff --git a/docs/next.config.js b/docs/next.config.js
index de65ad6f342b8b..aa41d36d65f252 100644
--- a/docs/next.config.js
+++ b/docs/next.config.js
@@ -158,6 +158,7 @@ module.exports = withDocsInfra({
SLACK_FEEDBACKS_TOKEN: process.env.SLACK_FEEDBACKS_TOKEN,
SOURCE_CODE_ROOT_URL: 'https://github.com/mui/material-ui/blob/master', // #default-branch-switch
SOURCE_CODE_REPO: 'https://github.com/mui/material-ui',
+ GITHUB_TEMPLATE_DOCS_FEEDBACK: '4.docs-feedback.yml',
BUILD_ONLY_ENGLISH_LOCALE: buildOnlyEnglishLocale,
},
// Next.js provides a `defaultPathMap` argument, we could simplify the logic.
diff --git a/docs/packages/markdown/parseMarkdown.js b/docs/packages/markdown/parseMarkdown.js
index 3ce6ac8b0c29b1..d14d1c6cbe4092 100644
--- a/docs/packages/markdown/parseMarkdown.js
+++ b/docs/packages/markdown/parseMarkdown.js
@@ -249,6 +249,9 @@ function createRender(context) {
``,
'',
'',
+ ``,
``,
].join('');
};
@@ -458,6 +461,11 @@ ${headers.components
+`);
+ rendered.unshift(``);
docs[userLanguage] = {
diff --git a/docs/src/modules/components/AppLayoutDocs.js b/docs/src/modules/components/AppLayoutDocs.js
index c0b4b9887093ef..dfe039f798ba56 100644
--- a/docs/src/modules/components/AppLayoutDocs.js
+++ b/docs/src/modules/components/AppLayoutDocs.js
@@ -30,6 +30,9 @@ const Main = styled('main', {
[theme.breakpoints.up('lg')]: {
width: 'calc(100% - var(--MuiDocs-navDrawer-width))',
},
+ '& .markdown-body .comment-link-style': {
+ display: 'inline-block',
+ },
}));
const StyledAppContainer = styled(AppContainer, {
@@ -130,7 +133,7 @@ function AppLayoutDocs(props) {
{location && }
{children}
-
+
diff --git a/docs/src/modules/components/AppLayoutDocsFooter.js b/docs/src/modules/components/AppLayoutDocsFooter.js
index 2202051a18d4be..8e07dd740b2704 100644
--- a/docs/src/modules/components/AppLayoutDocsFooter.js
+++ b/docs/src/modules/components/AppLayoutDocsFooter.js
@@ -1,8 +1,10 @@
/* eslint-disable no-restricted-globals */
import * as React from 'react';
-import { styled } from '@mui/material/styles';
+import PropTypes from 'prop-types';
+import { styled, useTheme } from '@mui/material/styles';
import DialogActions from '@mui/material/DialogActions';
import TextField from '@mui/material/TextField';
+import Box from '@mui/material/Box';
import Collapse from '@mui/material/Collapse';
import Button from '@mui/material/Button';
import Divider from '@mui/material/Divider';
@@ -103,10 +105,10 @@ async function postFeedback(data) {
}
async function postFeedbackOnSlack(data) {
- const { rating, comment } = data;
+ const { rating, comment, commentedSection } = data;
if (!comment || comment.length < 10) {
- return;
+ return 'ignored';
}
/**
@@ -152,9 +154,11 @@ async function postFeedbackOnSlack(data) {
*/
const simpleSlackMessage = [
- `New comment ${rating > 0 ? '👍' : '👎'}`,
+ `New comment ${rating === 1 ? '👍' : ''}${rating === 0 ? '👎' : ''}`,
`>${comment.split('\n').join('\n>')}`,
- `sent from ${window.location.href}`,
+ `sent from ${window.location.href}${
+ commentedSection.text ? ` (from section ${commentedSection.text})` : ''
+ }`,
].join('\n\n');
try {
@@ -163,8 +167,10 @@ async function postFeedbackOnSlack(data) {
headers: { 'content-type': 'application/x-www-form-urlencoded' },
body: JSON.stringify({ text: simpleSlackMessage }),
});
+ return 'sent';
} catch (error) {
console.error(error);
+ return null;
}
}
@@ -185,7 +191,7 @@ async function getUserFeedback(id) {
}
}
-async function submitFeedback(page, rating, comment, language) {
+async function submitFeedback(page, rating, comment, language, commentedSection) {
const data = {
id: getCookie('feedbackId'),
page,
@@ -195,18 +201,22 @@ async function submitFeedback(page, rating, comment, language) {
language,
};
- await postFeedbackOnSlack(data);
- const result = await postFeedback(data);
- if (result) {
- document.cookie = `feedbackId=${result.id};path=/;max-age=31536000`;
- setTimeout(async () => {
- const userFeedback = await getUserFeedback(result.id);
- if (userFeedback) {
- document.cookie = `feedback=${JSON.stringify(userFeedback)};path=/;max-age=31536000`;
- }
- });
+ const resultSlack = await postFeedbackOnSlack({ ...data, commentedSection });
+ if (rating !== undefined) {
+ const resultVote = await postFeedback(data);
+ if (resultVote) {
+ document.cookie = `feedbackId=${resultVote.id};path=/;max-age=31536000`;
+ setTimeout(async () => {
+ const userFeedback = await getUserFeedback(resultVote.id);
+ if (userFeedback) {
+ document.cookie = `feedback=${JSON.stringify(userFeedback)};path=/;max-age=31536000`;
+ }
+ });
+ }
+ return resultSlack && resultVote;
}
- return result;
+
+ return resultSlack;
}
function getCurrentRating(pathname) {
@@ -236,19 +246,37 @@ function usePageNeighbours() {
return { prevPage, nextPage };
}
-export default function AppLayoutDocsFooter() {
+const EMPTY_SECTION = { hash: '', text: '' };
+
+export default function AppLayoutDocsFooter(props) {
+ const { tableOfContents = [] } = props;
+
+ const theme = useTheme();
const t = useTranslate();
const userLanguage = useUserLanguage();
const { activePage } = React.useContext(PageContext);
const [rating, setRating] = React.useState();
const [comment, setComment] = React.useState('');
- const [commentOpen, setCommentOpen] = React.useState(false);
const [snackbarOpen, setSnackbarOpen] = React.useState(false);
const [snackbarMessage, setSnackbarMessage] = React.useState(false);
const inputRef = React.useRef();
+ const [commentOpen, setCommentOpen] = React.useState(false);
+ const [commentedSection, setCommentedSection] = React.useState(EMPTY_SECTION);
const { nextPage, prevPage } = usePageNeighbours();
+ const sectionOptions = React.useMemo(
+ () =>
+ tableOfContents.flatMap((section) => [
+ {
+ hash: section.hash,
+ text: section.text,
+ },
+ ...section.children.map(({ hash, text }) => ({ hash, text })),
+ ]),
+ [tableOfContents],
+ );
+
const setCurrentRatingFromCookie = React.useCallback(() => {
if (activePage !== null) {
setRating(getCurrentRating(activePage.pathname));
@@ -264,7 +292,13 @@ export default function AppLayoutDocsFooter() {
setSnackbarMessage(t('feedbackFailed'));
}
- const result = await submitFeedback(activePage.pathname, rating, comment, userLanguage);
+ const result = await submitFeedback(
+ activePage.pathname,
+ rating,
+ comment,
+ userLanguage,
+ commentedSection,
+ );
if (result) {
setSnackbarMessage(t('feedbackSubmitted'));
} else {
@@ -279,6 +313,12 @@ export default function AppLayoutDocsFooter() {
setRating(vote);
setCommentOpen(true);
}
+
+ // Manualy move focus if commment is already open.
+ // If the comment is closed, onEntered will call focus itself;
+ if (inputRef.current) {
+ inputRef.current.focus();
+ }
};
const handleChangeTextfield = (event) => {
@@ -291,9 +331,20 @@ export default function AppLayoutDocsFooter() {
processFeedback();
};
+ // See https://github.com/mui/mui-toolpad/issues/1164 for context.
+ const handleKeyDownForm = (event) => {
+ const modifierKey = (event.metaKey || event.ctrlKey) && !event.shiftKey;
+
+ if (event.key === 'Enter' && modifierKey) {
+ const submitButton = event.currentTarget.querySelector('[type="submit"]');
+ submitButton.click();
+ }
+ };
+
const handleCancelComment = () => {
setCommentOpen(false);
setCurrentRatingFromCookie();
+ setCommentedSection(EMPTY_SECTION);
};
const handleEntered = () => {
@@ -304,6 +355,27 @@ export default function AppLayoutDocsFooter() {
setSnackbarOpen(false);
};
+ React.useEffect(() => {
+ const eventListener = (event) => {
+ const feedbackHash = event.target.getAttribute('data-feedback-hash');
+ if (feedbackHash) {
+ const section = sectionOptions.find((item) => item.hash === feedbackHash) || EMPTY_SECTION;
+ setCommentOpen(true);
+ setCommentedSection(section);
+
+ // Manualy move focus if commment is already open.
+ // If the comment is closed, onEntered will call focus itself;
+ if (inputRef.current) {
+ inputRef.current.focus();
+ }
+ }
+ };
+ document.addEventListener('click', eventListener);
+ return () => {
+ document.removeEventListener('click', eventListener);
+ };
+ }, [sectionOptions]);
+
const hidePagePagination = activePage === null || activePage.ordered === false;
return (
@@ -371,25 +443,42 @@ export default function AppLayoutDocsFooter() {
)}
-
+
+ {/* eslint-disable-next-line jsx-a11y/no-noninteractive-element-interactions */}
@@ -415,3 +519,7 @@ export default function AppLayoutDocsFooter() {
);
}
+
+AppLayoutDocsFooter.propTypes = {
+ tableOfContents: PropTypes.array,
+};
diff --git a/docs/src/modules/components/MarkdownElement.js b/docs/src/modules/components/MarkdownElement.js
index 59841af71cfa10..679276018f7a24 100644
--- a/docs/src/modules/components/MarkdownElement.js
+++ b/docs/src/modules/components/MarkdownElement.js
@@ -105,6 +105,7 @@ const Root = styled('div')(
paddingLeft: 30,
},
'& h1, & h2, & h3, & h4': {
+ position: 'relative',
'& code': {
fontSize: 'inherit',
lineHeight: 'inherit',
@@ -120,18 +121,19 @@ const Root = styled('div')(
borderBottom: '1px solid currentColor',
textDecoration: 'none',
},
- '&:hover .anchor-link-style': {
- display: 'inline-block',
- textAlign: 'center',
+ '&:hover .anchor-link-style, & .comment-link-style': {
lineHeight: '21.5px',
+ textAlign: 'center',
marginLeft: 10,
- height: '26px',
- width: '26px',
- background: `var(--muidocs-palette-primary-50, ${lightTheme.palette.primary[50]})`,
+ height: 26,
+ width: 26,
+ backgroundColor: `var(--muidocs-palette-primary-50, ${lightTheme.palette.primary[50]})`,
border: '1px solid',
borderColor: `var(--muidocs-palette-grey-200, ${lightTheme.palette.grey[200]})`,
borderRadius: 8,
color: `var(--muidocs-palette-text-secondary, ${lightTheme.palette.text.secondary})`,
+ cursor: 'pointer',
+ display: 'inline-block',
'&:hover': {
color: `var(--muidocs-palette-text-primary, ${lightTheme.palette.text.primary})`,
},
@@ -139,6 +141,23 @@ const Root = styled('div')(
width: '0.875rem',
height: '0.875rem',
fill: 'currentColor',
+ pointerEvents: 'none',
+ },
+ },
+ '& .comment-link-style': {
+ display: 'none',
+ position: 'absolute',
+ top: `calc(50% - ${26 / 2}px)`,
+ right: 0,
+ opacity: 0.5,
+ transition: theme.transitions.create('opacity', {
+ duration: theme.transitions.duration.shortest,
+ }),
+ '&:hover': {
+ opacity: 1,
+ },
+ '& svg': {
+ verticalAlign: 'middle',
},
},
},
@@ -441,9 +460,9 @@ const Root = styled('div')(
color: `var(--muidocs-palette-grey-400, ${darkTheme.palette.grey[400]})`,
},
'& h1, & h2, & h3, & h4': {
- '&:hover .anchor-link-style': {
+ '&:hover .anchor-link-style, & .comment-link-style': {
color: `var(--muidocs-palette-text-secondary, ${darkTheme.palette.text.secondary})`,
- background: alpha(darkTheme.palette.primaryDark[800], 0.3),
+ backgroundColor: alpha(darkTheme.palette.primaryDark[800], 0.3),
borderColor: `var(--muidocs-palette-primaryDark-500, ${darkTheme.palette.primaryDark[500]})`,
'&:hover': {
color: `var(--muidocs-palette-text-primary, ${darkTheme.palette.text.primary})`,
diff --git a/docs/translations/translations.json b/docs/translations/translations.json
index 1e92e78e3cf931..9c1bcc7347e977 100644
--- a/docs/translations/translations.json
+++ b/docs/translations/translations.json
@@ -69,11 +69,18 @@
"feedbackCommentLabel": "Comment",
"feedbackFailed": "Couldn't submit feedback. Please try again later.",
"feedbackMessage": "Was this page helpful?",
- "feedbackMessageDown": "Please let us know what we could do to improve this page.",
- "feedbackMessageUp": "Please let us know what we should keep doing more of.",
+ "feedbackMessageDown": "How can we improve this page? (optional)",
+ "feedbackMessageUp": "What did you like about this page? (optional)",
+ "feedbackSectionSpecific": "How can we improve the {{sectionName}} section? (optional)",
+ "feedbackMessageToGitHub": {
+ "usecases": "Is something broken? Do you need a reply to a problem you've encountered?",
+ "callToAction": {
+ "text": "Please ",
+ "link": "open an issue."
+ }
+ },
"feedbackNo": "No",
"feedbackSubmitted": "Feedback submitted",
- "feedbackTitle": "Thanks for your feedback!",
"feedbackYes": "Yes",
"footerCompany": "Company",
"goToHome": "go to homepage",