Redirect: Add documentation. Cleanup (#26041)

* Add documentation. Cleanup. Add guardrail not to redirect to current url.
---------

Signed-off-by: Sydney Achinger <sydneynicoleachinger@spotify.com>
This commit is contained in:
Sydney Achinger
2024-08-15 14:34:15 -04:00
committed by GitHub
parent c47c82fd1b
commit 4490d733b5
8 changed files with 115 additions and 56 deletions
@@ -44,9 +44,9 @@ import {
copyToClipboard,
useSanitizerTransformer,
useStylesTransformer,
handleMetaRedirects,
} from '../../transformers';
import { useNavigateUrl } from './useNavigateUrl';
import { handleMetaRedirects } from '../../transformers/handleMetaRedirects';
const MOBILE_MEDIA_QUERY = 'screen and (max-width: 76.1875em)';
@@ -0,0 +1,67 @@
/*
* Copyright 2024 The Backstage Authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import { makeStyles } from '@material-ui/core/styles';
import React, { useState } from 'react';
import Snackbar from '@material-ui/core/Snackbar';
import Button from '@material-ui/core/Button';
type TechDocsRedirectNotificationProps = {
handleButtonClick: () => void;
message: string;
autoHideDuration: number;
};
const useStyles = makeStyles(theme => ({
button: {
color: theme.palette.primary.light,
textDecoration: 'underline',
},
}));
export const TechDocsRedirectNotification = ({
message,
handleButtonClick,
autoHideDuration,
}: TechDocsRedirectNotificationProps) => {
const classes = useStyles();
const [open, setOpen] = useState(true);
const handleClose = () => setOpen(false);
return (
<Snackbar
open={open}
anchorOrigin={{ vertical: 'top', horizontal: 'right' }}
autoHideDuration={autoHideDuration}
color="primary"
onClose={handleClose}
message={message}
action={
<Button
classes={{ root: classes.button }}
size="small"
onClick={() => {
handleClose();
handleButtonClick();
}}
>
Redirect now
</Button>
}
/>
);
};
@@ -0,0 +1,17 @@
/*
* Copyright 2024 The Backstage Authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
export { TechDocsRedirectNotification } from './TechDocsRedirectNotification';
@@ -82,7 +82,9 @@ describe('handleMetaRedirects', () => {
),
).toBeInTheDocument();
jest.runAllTimers();
expect(navigate).toHaveBeenCalledWith('/docs/default/component/testEntity');
expect(navigate).toHaveBeenCalledWith(
'http://localhost/docs/default/component/testEntity',
);
});
it('should navigate to absolute URL if meta redirect tag is present and not external', async () => {
@@ -16,64 +16,19 @@
import { Transformer } from './transformer';
import { normalizeUrl } from './rewriteDocLinks';
import Snackbar from '@material-ui/core/Snackbar';
import React, { useState } from 'react';
import React from 'react';
import { renderReactElement } from './renderReactElement';
import Button from '@material-ui/core/Button';
import { makeStyles } from '@material-ui/core/styles';
type RedirectNotificationProps = {
handleButtonClick: () => void;
message: string;
autoHideDuration: number;
};
const useStyles = makeStyles(theme => ({
button: {
color: theme.palette.primary.light,
textDecoration: 'underline',
},
}));
const RedirectNotification = ({
message,
handleButtonClick,
autoHideDuration,
}: RedirectNotificationProps) => {
const classes = useStyles();
const [open, setOpen] = useState(true);
const handleClose = () => {
setOpen(prev => !prev);
};
return (
<Snackbar
open={open}
anchorOrigin={{ vertical: 'top', horizontal: 'right' }}
autoHideDuration={autoHideDuration}
color="primary"
onClose={handleClose}
message={message}
action={
<Button
classes={{ root: classes.button }}
size="small"
onClick={handleButtonClick}
>
Redirect now
</Button>
}
/>
);
};
import { TechDocsRedirectNotification } from '../components/TechDocsRedirectNotification';
export const handleMetaRedirects = (
navigate: (to: string) => void,
entityName: string,
): Transformer => {
const redirectAfterMs = 4000;
const redirectAfterMs = 3000;
const determineRedirectURL = (metaUrl: string) => {
const normalizedCurrentUrl = normalizeUrl(window.location.href);
// If metaUrl is relative, it will be resolved with base href. If it is absolute, it will replace the base href when creating URL object.
// When creating URL object, if the metaUrl is relative, it will be resolved with base href. If it is absolute, it will replace the base href.
const absoluteRedirectObj = new URL(metaUrl, normalizedCurrentUrl);
const isExternalRedirect =
absoluteRedirectObj.hostname !== window.location.hostname;
@@ -85,9 +40,8 @@ export const handleMetaRedirects = (
0,
indexOfSiteHome + entityName.length,
);
return siteHomePath;
return new URL(siteHomePath, normalizedCurrentUrl).href;
}
// The navigate function from dom.tsx is a wrapper around react-router navigate function that helps absolute url redirects.
return absoluteRedirectObj.href;
};
@@ -97,15 +51,23 @@ export const handleMetaRedirects = (
const metaContentParameters = elem
.getAttribute('content')
?.split('url=');
if (!metaContentParameters || metaContentParameters.length < 2) {
continue;
return dom;
}
const metaUrl = metaContentParameters[1];
const redirectURL = determineRedirectURL(metaUrl);
// If the current URL is the same as the redirect URL, do not proceed with the redirect.
if (window.location.href === redirectURL) {
return dom;
}
const container = document.createElement('div');
renderReactElement(
<RedirectNotification
<TechDocsRedirectNotification
message="This TechDocs page is no longer maintained. Will automatically redirect to the designated replacement."
handleButtonClick={() => navigate(redirectURL)}
autoHideDuration={redirectAfterMs}
@@ -27,3 +27,4 @@ export * from './simplifyMkdocsFooter';
export * from './onCssReady';
export * from './scrollIntoNavigation';
export * from './transformer';
export * from './handleMetaRedirects';