From 1c6e110363964e2347d15050af81ae406c933df1 Mon Sep 17 00:00:00 2001 From: ecarrill Date: Mon, 17 Jul 2023 07:53:25 -0700 Subject: [PATCH 01/14] refactor: [M3-6268] - Apply previous work --- .../components/PrimaryNav/PrimaryNav.test.tsx | 11 +- .../src/components/PrimaryNav/PrimaryNav.tsx | 1 + .../src/features/Help/HelpLanding.test.tsx | 4 +- .../manager/src/features/Help/HelpLanding.tsx | 4 +- .../features/Help/Panels/AlgoliaSearchBar.tsx | 256 ++++++++---------- .../src/features/Help/Panels/OtherWays.tsx | 124 ++++----- .../src/features/Help/Panels/PopularPosts.tsx | 109 +++----- .../src/features/Help/Panels/SearchItem.tsx | 5 +- .../manager/src/features/Help/SearchHOC.tsx | 2 +- .../src/features/Help/StatusBanners.test.tsx | 2 +- .../src/features/Help/StatusBanners.tsx | 16 +- .../DocumentationResults.tsx | 14 +- .../SupportSearchLanding/HelpResources.tsx | 191 +++++-------- .../SupportSearchLanding.test.tsx | 10 +- .../SupportSearchLanding.tsx | 2 +- .../Help/SupportSearchLanding/index.tsx | 4 +- packages/manager/src/features/Help/index.tsx | 11 +- 17 files changed, 321 insertions(+), 445 deletions(-) diff --git a/packages/manager/src/components/PrimaryNav/PrimaryNav.test.tsx b/packages/manager/src/components/PrimaryNav/PrimaryNav.test.tsx index e6dde1e5626..7f592d177ee 100644 --- a/packages/manager/src/components/PrimaryNav/PrimaryNav.test.tsx +++ b/packages/manager/src/components/PrimaryNav/PrimaryNav.test.tsx @@ -14,6 +14,7 @@ const props = { }; const queryClient = queryClientFactory(); +const queryString = 'menu-item-Managed'; describe('PrimaryNav', () => { it('only contains a "Managed" menu link if the user has Managed services.', async () => { @@ -29,7 +30,7 @@ describe('PrimaryNav', () => { queryByTestId, rerender, } = renderWithTheme(, { queryClient }); - expect(queryByTestId('menu-item-Managed')).not.toBeInTheDocument(); + expect(queryByTestId(queryString)).not.toBeInTheDocument(); server.use( rest.get('*/account/maintenance', (req, res, ctx) => { @@ -39,9 +40,9 @@ describe('PrimaryNav', () => { rerender(wrapWithTheme(, { queryClient })); - await findByTestId('menu-item-Managed'); + await findByTestId(queryString); - getByTestId('menu-item-Managed'); + getByTestId(queryString); }); it('should have aria-current attribute for accessible links', () => { @@ -49,8 +50,6 @@ describe('PrimaryNav', () => { queryClient, }); - expect(getByTestId('menu-item-Managed').getAttribute('aria-current')).toBe( - 'false' - ); + expect(getByTestId(queryString).getAttribute('aria-current')).toBe('false'); }); }); diff --git a/packages/manager/src/components/PrimaryNav/PrimaryNav.tsx b/packages/manager/src/components/PrimaryNav/PrimaryNav.tsx index ad7c2e04238..bca1bdb6454 100644 --- a/packages/manager/src/components/PrimaryNav/PrimaryNav.tsx +++ b/packages/manager/src/components/PrimaryNav/PrimaryNav.tsx @@ -248,6 +248,7 @@ export const PrimaryNav = (props: Props) => { }, ], ], + // eslint-disable-next-line react-hooks/exhaustive-deps [ showDatabases, _isManagedAccount, diff --git a/packages/manager/src/features/Help/HelpLanding.test.tsx b/packages/manager/src/features/Help/HelpLanding.test.tsx index d433dbece7d..fad4cd0a56d 100644 --- a/packages/manager/src/features/Help/HelpLanding.test.tsx +++ b/packages/manager/src/features/Help/HelpLanding.test.tsx @@ -6,7 +6,7 @@ import { HelpLanding } from './HelpLanding'; describe('Help Landing', () => { const component = shallow(); xit('should render search panel', () => { - expect(component.find('WithStyles(SearchPanel)')).toHaveLength(1); + expect(component.find('SearchPanel')).toHaveLength(1); }); it('should render popular posts panel', () => { @@ -14,6 +14,6 @@ describe('Help Landing', () => { }); it('should render other ways panel', () => { - expect(component.find('WithStyles(OtherWays)')).toHaveLength(1); + expect(component.find('OtherWays')).toHaveLength(1); }); }); diff --git a/packages/manager/src/features/Help/HelpLanding.tsx b/packages/manager/src/features/Help/HelpLanding.tsx index 3cf65544653..20428f2130a 100644 --- a/packages/manager/src/features/Help/HelpLanding.tsx +++ b/packages/manager/src/features/Help/HelpLanding.tsx @@ -3,8 +3,8 @@ import * as React from 'react'; import { DocumentTitleSegment } from 'src/components/DocumentTitle'; -import OtherWays from './Panels/OtherWays'; -import PopularPosts from './Panels/PopularPosts'; +import { OtherWays } from './Panels/OtherWays'; +import { PopularPosts } from './Panels/PopularPosts'; import { SearchPanel } from './Panels/SearchPanel'; export const HelpLanding = () => { diff --git a/packages/manager/src/features/Help/Panels/AlgoliaSearchBar.tsx b/packages/manager/src/features/Help/Panels/AlgoliaSearchBar.tsx index 7a4b16e9571..70883ccbe43 100644 --- a/packages/manager/src/features/Help/Panels/AlgoliaSearchBar.tsx +++ b/packages/manager/src/features/Help/Panels/AlgoliaSearchBar.tsx @@ -1,10 +1,10 @@ import Search from '@mui/icons-material/Search'; -import { Theme } from '@mui/material/styles'; -import { WithStyles, WithTheme, createStyles, withStyles } from '@mui/styles'; +import { Theme, useTheme } from '@mui/material/styles'; import { pathOr } from 'ramda'; import * as React from 'react'; import { RouteComponentProps, withRouter } from 'react-router-dom'; import { compose } from 'recompose'; +import { makeStyles } from 'tss-react/mui'; import EnhancedSelect, { Item } from 'src/components/EnhancedSelect'; import { Notice } from 'src/components/Notice/Notice'; @@ -12,184 +12,142 @@ import { selectStyles } from 'src/features/TopMenu/SearchBar'; import windowIsNarrowerThan from 'src/utilities/breakpoints'; import withSearch, { AlgoliaState as AlgoliaProps } from '../SearchHOC'; -import SearchItem from './SearchItem'; - -type ClassNames = - | 'enhancedSelectWrapper' - | 'notice' - | 'root' - | 'searchIcon' - | 'searchItem'; - -const styles = (theme: Theme) => - createStyles({ - enhancedSelectWrapper: { - '& .input': { - '& > div': { - marginRight: 0, - }, - '& p': { - color: theme.color.grey1, - paddingLeft: theme.spacing(3), - }, - maxWidth: '100%', - }, - '& .react-select__value-container': { - paddingLeft: theme.spacing(4), - }, - margin: '0 auto', - maxHeight: 500, - [theme.breakpoints.up('md')]: { - width: 500, +import { SearchItem } from './SearchItem'; + +const useStyles = makeStyles()((theme: Theme) => ({ + enhancedSelectWrapper: { + '& .input': { + '& > div': { + marginRight: 0, }, - width: 300, - }, - notice: { '& p': { - color: theme.color.white, - fontFamily: 'LatoWeb', + color: theme.color.grey1, + paddingLeft: theme.spacing(3), }, + maxWidth: '100%', }, - root: { - position: 'relative', + '& .react-select__value-container': { + paddingLeft: theme.spacing(4), }, - searchIcon: { - color: theme.color.grey1, - left: 5, - position: 'absolute', - top: 4, - zIndex: 3, + margin: '0 auto', + maxHeight: 500, + [theme.breakpoints.up('md')]: { + width: 500, }, - searchItem: { - '& em': { - color: theme.palette.primary.main, - fontStyle: 'normal', - }, + width: 300, + }, + notice: { + '& p': { + color: theme.color.white, + fontFamily: 'LatoWeb', }, - }); - -interface State { - inputValue: string; -} - -type CombinedProps = AlgoliaProps & - WithStyles & - WithTheme & - RouteComponentProps<{}>; -class AlgoliaSearchBar extends React.Component { - componentDidMount() { - const { theme } = this.props; - this.mounted = true; + }, + root: { + position: 'relative', + }, + searchIcon: { + color: theme.color.grey1, + left: 5, + position: 'absolute', + top: 4, + zIndex: 3, + }, +})); + +type CombinedProps = AlgoliaProps & RouteComponentProps<{}>; + +const AlgoliaSearchBar = (props: CombinedProps) => { + const { classes } = useStyles(); + const theme = useTheme(); + const [inputValue, setInputValue] = React.useState(''); + const [mounted, setMounted] = React.useState(false); + const [isMobile, setIsMobile] = React.useState(false); + const { searchEnabled, searchError } = props; + + React.useEffect(() => { + setMounted(true); if (theme) { - this.isMobile = windowIsNarrowerThan(theme.breakpoints.values.sm); + setIsMobile(windowIsNarrowerThan(theme.breakpoints.values.sm)); } - } - componentWillUnmount() { - this.mounted = false; - } - render() { - const { classes, searchEnabled, searchError } = this.props; - const { inputValue } = this.state; - const options = this.getOptionsFromResults(); - - return ( - - {searchError && ( - - {searchError} - - )} -
- - null, Option: SearchItem } as any - } - className={classes.enhancedSelectWrapper} - disabled={!searchEnabled} - hideLabel - inputValue={inputValue} - isClearable={false} - isMulti={false} - label="Search for answers" - onChange={this.handleSelect} - onInputChange={this.onInputValueChange} - options={options} - placeholder="Search for answers..." - styles={selectStyles} - /> -
-
- ); - } - getLinkTarget = (inputValue: string) => { - return inputValue - ? `/support/search/?query=${inputValue}` - : '/support/search/'; - }; + return () => setMounted(false); + }, [isMobile, theme]); - getOptionsFromResults = () => { - const [docs, community] = this.props.searchResults; - const { inputValue } = this.state; + const getOptionsFromResults = () => { + const [docs, community] = props.searchResults; const options = [...docs, ...community]; + return [ { data: { source: 'finalLink' }, label: inputValue, value: 'search' }, ...options, ]; }; - handleSelect = (selected: Item) => { + const onInputValueChange = (inputValue: string) => { + if (!mounted) { + return; + } + setInputValue(inputValue); + props.searchAlgolia(inputValue); + }; + + const getLinkTarget = (inputValue: string) => { + return inputValue + ? `/support/search/?query=${inputValue}` + : '/support/search/'; + }; + + const handleSelect = (selected: Item) => { if (!selected) { return; } - const { history } = this.props; - const { inputValue } = this.state; + const { history } = props; if (!inputValue) { return; } + const href = pathOr('', ['data', 'href'], selected); + if (selected.value === 'search') { - const link = this.getLinkTarget(inputValue); + const link = getLinkTarget(inputValue); history.push(link); } else { window.open(href, '_blank', 'noopener'); } }; - handleSubmit = () => { - const { inputValue } = this.state; - if (!inputValue) { - return; - } - const { history } = this.props; - const link = this.getLinkTarget(inputValue); - history.push(link); - }; - - isMobile: boolean = false; - - mounted: boolean = false; - - onInputValueChange = (inputValue: string) => { - if (!this.mounted) { - return; - } - this.setState({ inputValue }); - this.props.searchAlgolia(inputValue); - }; - - searchIndex: any = null; - - state: State = { - inputValue: '', - }; -} + const options = getOptionsFromResults(); + + return ( + + {searchError && ( + + {searchError} + + )} +
+ + null, Option: SearchItem } as any + } + className={classes.enhancedSelectWrapper} + disabled={!searchEnabled} + hideLabel + inputValue={inputValue} + isClearable={false} + isMulti={false} + label="Search for answers" + onChange={handleSelect} + onInputChange={onInputValueChange} + options={options} + placeholder="Search for answers..." + styles={selectStyles} + /> +
+
+ ); +}; -const styled = withStyles(styles, { withTheme: true }); const search = withSearch({ highlight: true, hitsPerPage: 10 }); -export default compose( - styled, - search, - withRouter -)(AlgoliaSearchBar); +export default compose(search, withRouter)(AlgoliaSearchBar); diff --git a/packages/manager/src/features/Help/Panels/OtherWays.tsx b/packages/manager/src/features/Help/Panels/OtherWays.tsx index c8fa673a6e9..bf31b4c0dd2 100644 --- a/packages/manager/src/features/Help/Panels/OtherWays.tsx +++ b/packages/manager/src/features/Help/Panels/OtherWays.tsx @@ -1,7 +1,7 @@ import Grid from '@mui/material/Unstable_Grid2'; import { Theme } from '@mui/material/styles'; -import { WithStyles, createStyles, withStyles } from '@mui/styles'; import * as React from 'react'; +import { makeStyles } from 'tss-react/mui'; import Community from 'src/assets/icons/community.svg'; import Documentation from 'src/assets/icons/document.svg'; @@ -10,77 +10,59 @@ import Support from 'src/assets/icons/support.svg'; import { Tile } from 'src/components/Tile/Tile'; import { Typography } from 'src/components/Typography'; -type ClassNames = 'heading' | 'root' | 'wrapper'; +const useStyles = makeStyles()((theme: Theme) => ({ + heading: { + marginBottom: theme.spacing(4), + textAlign: 'center', + }, + wrapper: { + marginTop: theme.spacing(2), + }, +})); -const styles = (theme: Theme) => - createStyles({ - heading: { - marginBottom: theme.spacing(4), - textAlign: 'center', - }, - root: {}, - wrapper: { - marginTop: theme.spacing(2), - }, - }); +export const OtherWays = () => { + const { classes } = useStyles(); -interface State { - error?: string; -} - -type CombinedProps = WithStyles; - -export class OtherWays extends React.Component { - render() { - const { classes } = this.props; - - return ( - - - Other Ways to Get Help - - - - } - link="https://linode.com/docs/" - title="Guides and Tutorials" - /> - - - + + Other Ways to Get Help + + + + } + link="https://linode.com/docs/" + title="Guides and Tutorials" + /> + + + } - link="https://linode.com/community/questions" - title="Community Q&A" - /> - - - } - link="https://status.linode.com" - title="Linode Status Page" - /> - - - } - link="/support/tickets" - title="Customer Support" - /> - + icon={} + link="https://linode.com/community/questions" + title="Community Q&A" + /> - - ); - } - - state: State = {}; -} - -const styled = withStyles(styles); - -export default styled(OtherWays); + + } + link="https://status.linode.com" + title="Linode Status Page" + /> + + + } + link="/support/tickets" + title="Customer Support" + /> + + + + ); +}; diff --git a/packages/manager/src/features/Help/Panels/PopularPosts.tsx b/packages/manager/src/features/Help/Panels/PopularPosts.tsx index c3d98fb18b5..f44fd8fc838 100644 --- a/packages/manager/src/features/Help/Panels/PopularPosts.tsx +++ b/packages/manager/src/features/Help/Panels/PopularPosts.tsx @@ -1,31 +1,15 @@ import Grid from '@mui/material/Unstable_Grid2'; -import { Theme } from '@mui/material/styles'; -import { makeStyles } from '@mui/styles'; +import { styled, useTheme } from '@mui/material/styles'; import * as React from 'react'; import ExternalLink from 'src/components/ExternalLink'; import { Typography } from 'src/components/Typography'; import Paper from 'src/components/core/Paper'; -const useStyles = makeStyles((theme: Theme) => ({ - post: { - marginBottom: theme.spacing(0.5), - ...theme.typography.body1, - }, - postLink: { - '&:hover': { - color: theme.palette.primary.main, - textDecoration: 'underline', - }, - color: theme.textColors.linkActiveLight, - }, - postTitle: { - marginBottom: theme.spacing(2), - }, - root: { - margin: `${theme.spacing(6)} 0`, - }, - withSeparator: { +export const PopularPosts = () => { + const theme = useTheme(); + + const withSeparator = { borderLeft: `1px solid ${theme.palette.divider}`, paddingLeft: theme.spacing(4), [theme.breakpoints.down('sm')]: { @@ -33,39 +17,32 @@ const useStyles = makeStyles((theme: Theme) => ({ marginTop: theme.spacing(4), paddingLeft: 0, }, - }, -})); - -const PopularPosts: React.FC = () => { - const classes = useStyles(); + }; const renderPopularDocs = () => { return ( -
- + -
-
- + + -
-
- + + -
+
); }; @@ -73,57 +50,61 @@ const PopularPosts: React.FC = () => { const renderPopularForumPosts = () => { return ( -
- + -
-
- + + -
-
- + + -
+
); }; return ( - + - + Most Popular Documentation: {renderPopularDocs()} - - - Most Popular Community Posts: - - {renderPopularForumPosts()} - + + + Most Popular Community Posts: + + {renderPopularForumPosts()} ); }; -export default PopularPosts; +const StyledPostDiv = styled('div', { + label: 'StyledPostItem', +})(({ theme }) => ({ + marginBottom: theme.spacing(0.5), + ...theme.typography.body1, +})); + +const StyledExternalLink = styled(ExternalLink)(({ theme }) => ({ + '&:hover': { + color: theme.palette.primary.main, + textDecoration: 'underline', + }, + color: theme.textColors.linkActiveLight, +})); diff --git a/packages/manager/src/features/Help/Panels/SearchItem.tsx b/packages/manager/src/features/Help/Panels/SearchItem.tsx index ee7f011b98c..13c7cae60c6 100644 --- a/packages/manager/src/features/Help/Panels/SearchItem.tsx +++ b/packages/manager/src/features/Help/Panels/SearchItem.tsx @@ -15,7 +15,8 @@ interface Props extends OptionProps { searchText: string; } -const SearchItem: React.FC = (props) => { +// const SearchItem: React.FC = (props) => { +export const SearchItem = (props: Props) => { const getLabel = () => { if (isFinal) { return props.label ? `Search for "${props.label}"` : 'Search'; @@ -63,4 +64,4 @@ const SearchItem: React.FC = (props) => { ); }; -export default SearchItem; +// export default SearchItem; diff --git a/packages/manager/src/features/Help/SearchHOC.tsx b/packages/manager/src/features/Help/SearchHOC.tsx index dfb06255df4..ec2bfffcc56 100644 --- a/packages/manager/src/features/Help/SearchHOC.tsx +++ b/packages/manager/src/features/Help/SearchHOC.tsx @@ -37,7 +37,6 @@ interface AlgoliaContent { } // Functional helper methods - export const convertDocsToItems = ( highlight: boolean, hits: SearchHit[] = [] @@ -221,5 +220,6 @@ export default (options: SearchOptions) => ( searchResults: [[], []], }; } + return WrappedComponent; }; diff --git a/packages/manager/src/features/Help/StatusBanners.test.tsx b/packages/manager/src/features/Help/StatusBanners.test.tsx index 6d442663c8f..54768bf999b 100644 --- a/packages/manager/src/features/Help/StatusBanners.test.tsx +++ b/packages/manager/src/features/Help/StatusBanners.test.tsx @@ -4,7 +4,7 @@ import * as React from 'react'; import { renderWithTheme } from 'src/utilities/testHelpers'; -import StatusBanners, { IncidentBanner, IncidentProps } from './StatusBanners'; +import { IncidentBanner, IncidentProps, StatusBanners } from './StatusBanners'; const props: IncidentProps = { href: 'https://www.example.com', diff --git a/packages/manager/src/features/Help/StatusBanners.tsx b/packages/manager/src/features/Help/StatusBanners.tsx index f3a70eaf524..80c0cd253a5 100644 --- a/packages/manager/src/features/Help/StatusBanners.tsx +++ b/packages/manager/src/features/Help/StatusBanners.tsx @@ -1,8 +1,8 @@ import Box from '@mui/material/Box'; import { Theme } from '@mui/material/styles'; -import { makeStyles } from '@mui/styles'; import { DateTime } from 'luxon'; import * as React from 'react'; +import { makeStyles } from 'tss-react/mui'; import DismissibleBanner from 'src/components/DismissibleBanner'; import { Link } from 'src/components/Link'; @@ -16,11 +16,7 @@ import { capitalize } from 'src/utilities/capitalize'; import { sanitizeHTML } from 'src/utilities/sanitize-html'; import { truncateEnd } from 'src/utilities/truncate'; -const useStyles = makeStyles((theme: Theme) => ({ - button: { - ...theme.applyLinkStyles, - display: 'flex', - }, +const useStyles = makeStyles()((theme: Theme) => ({ header: { fontSize: '1rem', marginBottom: theme.spacing(), @@ -34,7 +30,7 @@ const useStyles = makeStyles((theme: Theme) => ({ }, })); -export const StatusBanners: React.FC<{}> = (_) => { +export const StatusBanners = () => { const { data: incidentsData } = useIncidentQuery(); const incidents = incidentsData?.incidents ?? []; @@ -73,10 +69,10 @@ export interface IncidentProps { title: string; } -export const IncidentBanner: React.FC = React.memo((props) => { +export const IncidentBanner = React.memo((props: IncidentProps) => { const { href, impact, message, status: _status, title } = props; const status = _status ?? ''; - const classes = useStyles(); + const { classes } = useStyles(); const preferenceKey = `${href}-${status}`; @@ -116,5 +112,3 @@ export const IncidentBanner: React.FC = React.memo((props) => { ); }); - -export default React.memo(StatusBanners); diff --git a/packages/manager/src/features/Help/SupportSearchLanding/DocumentationResults.tsx b/packages/manager/src/features/Help/SupportSearchLanding/DocumentationResults.tsx index d6968a21e18..1fb0ccf90fb 100644 --- a/packages/manager/src/features/Help/SupportSearchLanding/DocumentationResults.tsx +++ b/packages/manager/src/features/Help/SupportSearchLanding/DocumentationResults.tsx @@ -1,14 +1,14 @@ import OpenInNew from '@mui/icons-material/OpenInNew'; import { Theme } from '@mui/material/styles'; -import { makeStyles } from '@mui/styles'; import * as React from 'react'; +import { makeStyles } from 'tss-react/mui'; import ExternalLink from 'src/components/ExternalLink'; import { Typography } from 'src/components/Typography'; import ListItem from 'src/components/core/ListItem'; import Paper from 'src/components/core/Paper'; -const useStyles = makeStyles((theme: Theme) => ({ +const useStyles = makeStyles()((theme: Theme) => ({ header: { marginBottom: theme.spacing(2), marginTop: theme.spacing(3), @@ -52,10 +52,8 @@ interface Props { target: string; } -type CombinedProps = Props; - -const DocumentationResults: React.FC = (props) => { - const classes = useStyles(); +export const DocumentationResults = (props: Props) => { + const { classes } = useStyles(); const { results, sectionTitle, target } = props; const renderResults = () => { @@ -80,7 +78,7 @@ const DocumentationResults: React.FC = (props) => { )); }; - const renderEmptyState = () => { + const renderEmptyState = (): JSX.Element => { return ( No results @@ -109,5 +107,3 @@ const DocumentationResults: React.FC = (props) => { ); }; - -export default DocumentationResults; diff --git a/packages/manager/src/features/Help/SupportSearchLanding/HelpResources.tsx b/packages/manager/src/features/Help/SupportSearchLanding/HelpResources.tsx index ecfd73f3f85..95045c82275 100644 --- a/packages/manager/src/features/Help/SupportSearchLanding/HelpResources.tsx +++ b/packages/manager/src/features/Help/SupportSearchLanding/HelpResources.tsx @@ -1,9 +1,9 @@ import Grid from '@mui/material/Unstable_Grid2'; import { Theme } from '@mui/material/styles'; -import { WithStyles, createStyles, withStyles } from '@mui/styles'; import { compose } from 'ramda'; import * as React from 'react'; import { RouteComponentProps, withRouter } from 'react-router-dom'; +import { makeStyles } from 'tss-react/mui'; import Community from 'src/assets/icons/community.svg'; import Support from 'src/assets/icons/support.svg'; @@ -12,132 +12,89 @@ import { Typography } from 'src/components/Typography'; import { AttachmentError } from 'src/features/Support/SupportTicketDetail/SupportTicketDetail'; import { SupportTicketDialog } from 'src/features/Support/SupportTickets/SupportTicketDialog'; -type ClassNames = - | 'card' - | 'heading' - | 'icon' - | 'root' - | 'tileTitle' - | 'wrapper'; +const useStyles = makeStyles()((theme: Theme) => ({ + heading: { + marginBottom: theme.spacing(1), + textAlign: 'center', + }, + icon: { + border: `2px solid ${theme.palette.divider}`, + borderRadius: '50%', + color: theme.palette.primary.main, + display: 'block', + height: 66, + margin: '0 auto 16px', + padding: 16, + width: 66, + }, + wrapper: { + marginTop: theme.spacing(4), + }, +})); -const styles = (theme: Theme) => - createStyles({ - card: { - alignItems: 'center', - backgroundColor: theme.color.white, - border: `1px solid ${theme.color.grey2}`, - display: 'flex', - flexDirection: 'column', - height: '100%', - padding: theme.spacing(4), - }, - heading: { - marginBottom: theme.spacing(1), - textAlign: 'center', - }, - icon: { - border: `2px solid ${theme.palette.divider}`, - borderRadius: '50%', - color: theme.palette.primary.main, - display: 'block', - height: 66, - margin: '0 auto 16px', - padding: 16, - width: 66, - }, - root: {}, - tileTitle: { - fontSize: '1.2rem', - marginBottom: theme.spacing(1), - marginTop: theme.spacing(1), - }, - wrapper: { - marginTop: theme.spacing(4), - }, - }); - -interface State { - drawerOpen: boolean; - error?: string; -} - -type CombinedProps = RouteComponentProps<{}> & WithStyles; - -export class OtherWays extends React.Component { - render() { - const { classes } = this.props; - const { drawerOpen } = this.state; - - return ( - <> - - - - Didn’t find what you need? Get help. - - - - - } - link="https://linode.com/community/" - title="Create a Community Post" - /> - - - } - link={this.openTicketDrawer} - title="Open a ticket" - /> - - - - - - ); - } +export const HelpResources = (props: RouteComponentProps) => { + const { classes } = useStyles(); + const [drawerOpen, setDrawerOpen] = React.useState(false); + const openTicketDrawer = () => { + setDrawerOpen(true); + }; - closeTicketDrawer = () => { - this.setState({ drawerOpen: false }); + const closeTicketDrawer = () => { + setDrawerOpen(false); }; - onTicketCreated = ( + const onTicketCreated = ( ticketId: number, attachmentErrors: AttachmentError[] = [] ) => { - const { history } = this.props; + const { history } = props; history.push({ pathname: `/support/tickets/${ticketId}`, state: { attachmentErrors }, }); - this.setState({ - drawerOpen: false, - }); + setDrawerOpen(false); }; - openTicketDrawer = () => { - this.setState({ drawerOpen: true }); - }; - - state: State = { - drawerOpen: false, - }; -} - -const styled = withStyles(styles); - -export default compose(styled, withRouter)(OtherWays); + return ( + <> + + + + Didn’t find what you need? Get help. + + + + + } + link="https://linode.com/community/" + title="Create a Community Post" + /> + + + } + link={openTicketDrawer} + title="Open a ticket" + /> + + + + + + ); +}; +export default compose(withRouter)(HelpResources); diff --git a/packages/manager/src/features/Help/SupportSearchLanding/SupportSearchLanding.test.tsx b/packages/manager/src/features/Help/SupportSearchLanding/SupportSearchLanding.test.tsx index 5a54526de33..277997be6e5 100644 --- a/packages/manager/src/features/Help/SupportSearchLanding/SupportSearchLanding.test.tsx +++ b/packages/manager/src/features/Help/SupportSearchLanding/SupportSearchLanding.test.tsx @@ -30,11 +30,13 @@ const propsWithMultiWordURLQuery = assocPath( '?query=search%20two%20words', props ); -const component = shallow( + +const component = shallow( ); + // Query is read on mount so we have to mount twice. -const component2 = shallow( +const component2 = shallow( ); @@ -42,12 +44,15 @@ describe('Component', () => { it('should render', () => { expect(component).toBeDefined(); }); + it('should set the query from the URL param to state', () => { expect(component.state().query).toMatch('search'); }); + it('should read multi-word queries correctly', () => { expect(component2.state().query).toMatch('search two words'); }); + it('should display the query text in the header', () => { expect( component.containsMatchingElement( @@ -58,6 +63,7 @@ describe('Component', () => { ) ).toBeTruthy(); }); + it('should display generic text if no query is provided', () => { component.setState({ query: '' }); expect( diff --git a/packages/manager/src/features/Help/SupportSearchLanding/SupportSearchLanding.tsx b/packages/manager/src/features/Help/SupportSearchLanding/SupportSearchLanding.tsx index 046aae3b952..4d1741ff098 100644 --- a/packages/manager/src/features/Help/SupportSearchLanding/SupportSearchLanding.tsx +++ b/packages/manager/src/features/Help/SupportSearchLanding/SupportSearchLanding.tsx @@ -15,7 +15,7 @@ import { COMMUNITY_SEARCH_URL, DOCS_SEARCH_URL } from 'src/constants'; import { getQueryParamFromQueryString } from 'src/utilities/queryParams'; import withSearch, { AlgoliaState as AlgoliaProps } from '../SearchHOC'; -import DocumentationResults, { SearchResult } from './DocumentationResults'; +import { DocumentationResults, SearchResult } from './DocumentationResults'; import HelpResources from './HelpResources'; type ClassNames = diff --git a/packages/manager/src/features/Help/SupportSearchLanding/index.tsx b/packages/manager/src/features/Help/SupportSearchLanding/index.tsx index cb15cb5f8f3..e52dfa443b0 100644 --- a/packages/manager/src/features/Help/SupportSearchLanding/index.tsx +++ b/packages/manager/src/features/Help/SupportSearchLanding/index.tsx @@ -1,2 +1,2 @@ -import SupportSearchLanding from './SupportSearchLanding'; -export default SupportSearchLanding; +// import SupportSearchLanding from './SupportSearchLanding'; +// export default SupportSearchLanding; diff --git a/packages/manager/src/features/Help/index.tsx b/packages/manager/src/features/Help/index.tsx index d6a1c40d5bf..20651a58d27 100644 --- a/packages/manager/src/features/Help/index.tsx +++ b/packages/manager/src/features/Help/index.tsx @@ -1,19 +1,20 @@ import * as React from 'react'; import { Redirect, Route, Switch } from 'react-router-dom'; -import StatusBanners from './StatusBanners'; +import { StatusBanners } from './StatusBanners'; const HelpLanding = React.lazy(() => - import('./HelpLanding').then((module) => ({ - default: module.HelpLanding, - })) + import('./HelpLanding').then((module) => ({ default: module.HelpLanding })) ); + const SupportSearchLanding = React.lazy( - () => import('src/features/Help/SupportSearchLanding') + () => import('src/features/Help/SupportSearchLanding/SupportSearchLanding') ); + const SupportTickets = React.lazy( () => import('src/features/Support/SupportTickets') ); + const SupportTicketDetail = React.lazy( () => import('src/features/Support/SupportTicketDetail') ); From a74655263087d4b71db7a1cecd3b42829007abda Mon Sep 17 00:00:00 2001 From: ecarrill Date: Mon, 17 Jul 2023 08:18:43 -0700 Subject: [PATCH 02/14] Remove redundant index file --- .../manager/src/features/Help/SupportSearchLanding/index.tsx | 2 -- 1 file changed, 2 deletions(-) delete mode 100644 packages/manager/src/features/Help/SupportSearchLanding/index.tsx diff --git a/packages/manager/src/features/Help/SupportSearchLanding/index.tsx b/packages/manager/src/features/Help/SupportSearchLanding/index.tsx deleted file mode 100644 index e52dfa443b0..00000000000 --- a/packages/manager/src/features/Help/SupportSearchLanding/index.tsx +++ /dev/null @@ -1,2 +0,0 @@ -// import SupportSearchLanding from './SupportSearchLanding'; -// export default SupportSearchLanding; From 45ff7fa2817ca83d716e3e5906a89ab366ca6482 Mon Sep 17 00:00:00 2001 From: ecarrill Date: Mon, 17 Jul 2023 08:53:56 -0700 Subject: [PATCH 03/14] Add changeset --- packages/manager/.changeset/pr-9408-changed-1689609217598.md | 5 +++++ 1 file changed, 5 insertions(+) create mode 100644 packages/manager/.changeset/pr-9408-changed-1689609217598.md diff --git a/packages/manager/.changeset/pr-9408-changed-1689609217598.md b/packages/manager/.changeset/pr-9408-changed-1689609217598.md new file mode 100644 index 00000000000..c0f1a8d62ed --- /dev/null +++ b/packages/manager/.changeset/pr-9408-changed-1689609217598.md @@ -0,0 +1,5 @@ +--- +"@linode/manager": Changed +--- + +MUI v5 migration Src > Features > Help ([#9408](https://github.com/linode/manager/pull/9408)) From cebb95185a6a4025ff64b5b96ff8a3337ac33465 Mon Sep 17 00:00:00 2001 From: ecarrill Date: Tue, 18 Jul 2023 07:23:15 -0700 Subject: [PATCH 04/14] Fix issues from PR feedback --- ...8.md => pr-9408-tech-stories-1689609217598.md} | 2 +- .../src/features/Help/Panels/OtherWays.tsx | 3 +-- .../src/features/Help/Panels/PopularPosts.tsx | 15 +++++++++------ .../src/features/Help/Panels/SearchItem.tsx | 3 --- .../src/features/Help/Panels/SearchPanel.tsx | 2 +- .../SupportSearchLanding/SupportSearchLanding.tsx | 2 +- 6 files changed, 13 insertions(+), 14 deletions(-) rename packages/manager/.changeset/{pr-9408-changed-1689609217598.md => pr-9408-tech-stories-1689609217598.md} (76%) diff --git a/packages/manager/.changeset/pr-9408-changed-1689609217598.md b/packages/manager/.changeset/pr-9408-tech-stories-1689609217598.md similarity index 76% rename from packages/manager/.changeset/pr-9408-changed-1689609217598.md rename to packages/manager/.changeset/pr-9408-tech-stories-1689609217598.md index c0f1a8d62ed..918d03e40ad 100644 --- a/packages/manager/.changeset/pr-9408-changed-1689609217598.md +++ b/packages/manager/.changeset/pr-9408-tech-stories-1689609217598.md @@ -1,5 +1,5 @@ --- -"@linode/manager": Changed +'@linode/manager': Tech Stories --- MUI v5 migration Src > Features > Help ([#9408](https://github.com/linode/manager/pull/9408)) diff --git a/packages/manager/src/features/Help/Panels/OtherWays.tsx b/packages/manager/src/features/Help/Panels/OtherWays.tsx index bf31b4c0dd2..4df69ed3682 100644 --- a/packages/manager/src/features/Help/Panels/OtherWays.tsx +++ b/packages/manager/src/features/Help/Panels/OtherWays.tsx @@ -39,8 +39,7 @@ export const OtherWays = () => { } link="https://linode.com/community/questions" title="Community Q&A" diff --git a/packages/manager/src/features/Help/Panels/PopularPosts.tsx b/packages/manager/src/features/Help/Panels/PopularPosts.tsx index f44fd8fc838..5066499d148 100644 --- a/packages/manager/src/features/Help/Panels/PopularPosts.tsx +++ b/packages/manager/src/features/Help/Panels/PopularPosts.tsx @@ -84,11 +84,12 @@ export const PopularPosts = () => { {renderPopularDocs()} - - - Most Popular Community Posts: - - {renderPopularForumPosts()} + + + Most Popular Community Posts: + + {renderPopularForumPosts()} + ); @@ -101,7 +102,9 @@ const StyledPostDiv = styled('div', { ...theme.typography.body1, })); -const StyledExternalLink = styled(ExternalLink)(({ theme }) => ({ +const StyledExternalLink = styled(ExternalLink, { + label: 'StyledExternalLink', +})(({ theme }) => ({ '&:hover': { color: theme.palette.primary.main, textDecoration: 'underline', diff --git a/packages/manager/src/features/Help/Panels/SearchItem.tsx b/packages/manager/src/features/Help/Panels/SearchItem.tsx index 13c7cae60c6..cd31958388c 100644 --- a/packages/manager/src/features/Help/Panels/SearchItem.tsx +++ b/packages/manager/src/features/Help/Panels/SearchItem.tsx @@ -15,7 +15,6 @@ interface Props extends OptionProps { searchText: string; } -// const SearchItem: React.FC = (props) => { export const SearchItem = (props: Props) => { const getLabel = () => { if (isFinal) { @@ -63,5 +62,3 @@ export const SearchItem = (props: Props) => { ); }; - -// export default SearchItem; diff --git a/packages/manager/src/features/Help/Panels/SearchPanel.tsx b/packages/manager/src/features/Help/Panels/SearchPanel.tsx index 0c8b3965785..f0afc594e79 100644 --- a/packages/manager/src/features/Help/Panels/SearchPanel.tsx +++ b/packages/manager/src/features/Help/Panels/SearchPanel.tsx @@ -36,7 +36,7 @@ const StyledRootContainer = styled(Paper, { const StyledH1Header = styled(H1Header, { label: 'StyledH1Header', })(({ theme }) => ({ - color: theme.color.white, + color: 'white', marginBottom: theme.spacing(), position: 'relative', textAlign: 'center', diff --git a/packages/manager/src/features/Help/SupportSearchLanding/SupportSearchLanding.tsx b/packages/manager/src/features/Help/SupportSearchLanding/SupportSearchLanding.tsx index 4d1741ff098..8709d7986c6 100644 --- a/packages/manager/src/features/Help/SupportSearchLanding/SupportSearchLanding.tsx +++ b/packages/manager/src/features/Help/SupportSearchLanding/SupportSearchLanding.tsx @@ -90,7 +90,7 @@ export class SupportSearchLanding extends React.Component< const [docs, community] = searchResults; return ( - + Date: Fri, 21 Jul 2023 13:14:55 -0700 Subject: [PATCH 05/14] Implement new styling api and remove unused css --- .../src/features/Help/Panels/PopularPosts.tsx | 2 +- .../DocumentationResults.tsx | 2 +- .../SupportSearchLanding.test.tsx | 5 +- .../SupportSearchLanding.tsx | 88 +++++++------------ .../src/features/Users/UserPermissions.tsx | 4 +- 5 files changed, 36 insertions(+), 65 deletions(-) diff --git a/packages/manager/src/features/Help/Panels/PopularPosts.tsx b/packages/manager/src/features/Help/Panels/PopularPosts.tsx index 67db6e295e5..50e8a0d0b67 100644 --- a/packages/manager/src/features/Help/Panels/PopularPosts.tsx +++ b/packages/manager/src/features/Help/Panels/PopularPosts.tsx @@ -3,8 +3,8 @@ import { styled, useTheme } from '@mui/material/styles'; import * as React from 'react'; import ExternalLink from 'src/components/ExternalLink'; -import { Typography } from 'src/components/Typography'; import { Paper } from 'src/components/Paper'; +import { Typography } from 'src/components/Typography'; export const PopularPosts = () => { const theme = useTheme(); diff --git a/packages/manager/src/features/Help/SupportSearchLanding/DocumentationResults.tsx b/packages/manager/src/features/Help/SupportSearchLanding/DocumentationResults.tsx index 20353d13f07..a1720ee21f0 100644 --- a/packages/manager/src/features/Help/SupportSearchLanding/DocumentationResults.tsx +++ b/packages/manager/src/features/Help/SupportSearchLanding/DocumentationResults.tsx @@ -4,9 +4,9 @@ import * as React from 'react'; import { makeStyles } from 'tss-react/mui'; import ExternalLink from 'src/components/ExternalLink'; +import { Paper } from 'src/components/Paper'; import { Typography } from 'src/components/Typography'; import ListItem from 'src/components/core/ListItem'; -import { Paper } from 'src/components/Paper'; const useStyles = makeStyles()((theme: Theme) => ({ header: { diff --git a/packages/manager/src/features/Help/SupportSearchLanding/SupportSearchLanding.test.tsx b/packages/manager/src/features/Help/SupportSearchLanding/SupportSearchLanding.test.tsx index 277997be6e5..05ccdc8aef0 100644 --- a/packages/manager/src/features/Help/SupportSearchLanding/SupportSearchLanding.test.tsx +++ b/packages/manager/src/features/Help/SupportSearchLanding/SupportSearchLanding.test.tsx @@ -5,15 +5,12 @@ import * as React from 'react'; import { reactRouterProps } from 'src/__data__/reactRouterProps'; import { H1Header } from 'src/components/H1Header/H1Header'; -import { CombinedProps, SupportSearchLanding } from './SupportSearchLanding'; +import SupportSearchLanding, { CombinedProps } from './SupportSearchLanding'; const classes = { backButton: '', - root: '', searchBar: '', searchBoxInner: '', - searchField: '', - searchHeading: '', searchIcon: '', }; diff --git a/packages/manager/src/features/Help/SupportSearchLanding/SupportSearchLanding.tsx b/packages/manager/src/features/Help/SupportSearchLanding/SupportSearchLanding.tsx index 8709d7986c6..9d18a5cb7cb 100644 --- a/packages/manager/src/features/Help/SupportSearchLanding/SupportSearchLanding.tsx +++ b/packages/manager/src/features/Help/SupportSearchLanding/SupportSearchLanding.tsx @@ -2,10 +2,10 @@ import Search from '@mui/icons-material/Search'; import Box from '@mui/material/Box'; import Grid from '@mui/material/Unstable_Grid2'; import { Theme } from '@mui/material/styles'; -import { WithStyles, createStyles, withStyles } from '@mui/styles'; -import { compose } from 'ramda'; import * as React from 'react'; import { RouteComponentProps, withRouter } from 'react-router-dom'; +import { compose } from 'recompose'; +import { withStyles } from 'tss-react/mui'; import { H1Header } from 'src/components/H1Header/H1Header'; import { Notice } from 'src/components/Notice/Notice'; @@ -18,62 +18,39 @@ import withSearch, { AlgoliaState as AlgoliaProps } from '../SearchHOC'; import { DocumentationResults, SearchResult } from './DocumentationResults'; import HelpResources from './HelpResources'; -type ClassNames = - | 'root' - | 'searchBar' - | 'searchBoxInner' - | 'searchField' - | 'searchHeading' - | 'searchIcon'; - -const styles = (theme: Theme) => - createStyles({ - root: { - alignItems: 'center', - display: 'flex', - justifyContent: 'flex-start', - maxWidth: '100%', - position: 'relative', - }, - searchBar: { +type ClassNames = 'searchBar' | 'searchBoxInner' | 'searchIcon'; + +const styles = (theme: Theme) => ({ + searchBar: { + maxWidth: '100%', + }, + searchBoxInner: { + '& > div': { maxWidth: '100%', }, - searchBoxInner: { - '& > div': { - maxWidth: '100%', - }, - backgroundColor: theme.color.grey2, - marginTop: 0, - padding: theme.spacing(3), - }, - searchField: { - padding: theme.spacing(3), - }, - searchHeading: { - color: theme.color.black, - fontSize: '175%', - marginBottom: theme.spacing(2), - }, - searchIcon: { - '& svg': { - color: theme.palette.text.primary, - }, - marginRight: 0, + backgroundColor: theme.color.grey2, + marginTop: 0, + padding: theme.spacing(3), + }, + searchIcon: { + '& svg': { + color: theme.palette.text.primary, }, - }); + marginRight: 0, + }, +}); interface State { query: string; } -export type CombinedProps = AlgoliaProps & - WithStyles & - RouteComponentProps<{}>; +interface Props { + classes?: Partial>; +} + +export type CombinedProps = AlgoliaProps & Props & RouteComponentProps<{}>; -export class SupportSearchLanding extends React.Component< - CombinedProps, - State -> { +class SupportSearchLanding extends React.Component { componentDidMount() { this.searchFromParams(); } @@ -84,9 +61,9 @@ export class SupportSearchLanding extends React.Component< } render() { - const { classes, searchEnabled, searchError, searchResults } = this.props; + const { searchEnabled, searchError, searchResults } = this.props; + const classes = withStyles.getClasses(this.props); const { query } = this.state; - const [docs, community] = searchResults; return ( @@ -166,12 +143,9 @@ export class SupportSearchLanding extends React.Component< }; } -const styled = withStyles(styles); const searchable = withSearch({ highlight: false, hitsPerPage: 5 }); -const enhanced: any = compose( - styled, + +export default compose( searchable, withRouter -)(SupportSearchLanding); - -export default enhanced; +)(withStyles(SupportSearchLanding, styles)); diff --git a/packages/manager/src/features/Users/UserPermissions.tsx b/packages/manager/src/features/Users/UserPermissions.tsx index 363963426b6..181e4c60dde 100644 --- a/packages/manager/src/features/Users/UserPermissions.tsx +++ b/packages/manager/src/features/Users/UserPermissions.tsx @@ -23,12 +23,12 @@ import { Divider } from 'src/components/Divider'; import { DocumentTitleSegment } from 'src/components/DocumentTitle'; import Select, { Item } from 'src/components/EnhancedSelect/Select'; import { Notice } from 'src/components/Notice/Notice'; +import { Paper } from 'src/components/Paper'; import { SafeTabPanel } from 'src/components/SafeTabPanel/SafeTabPanel'; import { SelectionCard } from 'src/components/SelectionCard/SelectionCard'; import { Toggle } from 'src/components/Toggle'; import { Typography } from 'src/components/Typography'; import FormControlLabel from 'src/components/core/FormControlLabel'; -import { Paper } from 'src/components/Paper'; import { Tab } from 'src/components/core/ReachTab'; import { TabList } from 'src/components/core/ReachTabList'; import TabPanels from 'src/components/core/ReachTabPanels'; @@ -179,7 +179,7 @@ class UserPermissions extends React.Component { if (updateFns.length) { this.setState((compose as any)(...updateFns)); } - return; + // return; } }; From 2be31b9d58d160d09705d0b24a328cf577e21995 Mon Sep 17 00:00:00 2001 From: ecarrill Date: Tue, 25 Jul 2023 08:27:21 -0700 Subject: [PATCH 06/14] Styled components implementation --- .../SupportSearchLanding.test.tsx | 14 +-- .../SupportSearchLanding.tsx | 115 ++++++++++++------ 2 files changed, 82 insertions(+), 47 deletions(-) diff --git a/packages/manager/src/features/Help/SupportSearchLanding/SupportSearchLanding.test.tsx b/packages/manager/src/features/Help/SupportSearchLanding/SupportSearchLanding.test.tsx index 05ccdc8aef0..3389b96616e 100644 --- a/packages/manager/src/features/Help/SupportSearchLanding/SupportSearchLanding.test.tsx +++ b/packages/manager/src/features/Help/SupportSearchLanding/SupportSearchLanding.test.tsx @@ -7,15 +7,15 @@ import { H1Header } from 'src/components/H1Header/H1Header'; import SupportSearchLanding, { CombinedProps } from './SupportSearchLanding'; -const classes = { - backButton: '', - searchBar: '', - searchBoxInner: '', - searchIcon: '', -}; +// const classes = { +// backButton: '', +// searchBar: '', +// searchBoxInner: '', +// searchIcon: '', +// }; const props: CombinedProps = { - classes, + // classes, searchAlgolia: jest.fn(), searchEnabled: true, searchResults: [[], []], diff --git a/packages/manager/src/features/Help/SupportSearchLanding/SupportSearchLanding.tsx b/packages/manager/src/features/Help/SupportSearchLanding/SupportSearchLanding.tsx index 9d18a5cb7cb..76939e417bf 100644 --- a/packages/manager/src/features/Help/SupportSearchLanding/SupportSearchLanding.tsx +++ b/packages/manager/src/features/Help/SupportSearchLanding/SupportSearchLanding.tsx @@ -1,11 +1,12 @@ import Search from '@mui/icons-material/Search'; import Box from '@mui/material/Box'; import Grid from '@mui/material/Unstable_Grid2'; -import { Theme } from '@mui/material/styles'; +// import { Theme } from '@mui/material/styles'; import * as React from 'react'; import { RouteComponentProps, withRouter } from 'react-router-dom'; -import { compose } from 'recompose'; -import { withStyles } from 'tss-react/mui'; +// import { compose } from 'recompose'; +// import { withStyles } from 'tss-react/mui'; +import { styled } from '@mui/material/styles'; import { H1Header } from 'src/components/H1Header/H1Header'; import { Notice } from 'src/components/Notice/Notice'; @@ -18,37 +19,42 @@ import withSearch, { AlgoliaState as AlgoliaProps } from '../SearchHOC'; import { DocumentationResults, SearchResult } from './DocumentationResults'; import HelpResources from './HelpResources'; -type ClassNames = 'searchBar' | 'searchBoxInner' | 'searchIcon'; - -const styles = (theme: Theme) => ({ - searchBar: { - maxWidth: '100%', - }, - searchBoxInner: { - '& > div': { - maxWidth: '100%', - }, - backgroundColor: theme.color.grey2, - marginTop: 0, - padding: theme.spacing(3), - }, - searchIcon: { - '& svg': { - color: theme.palette.text.primary, - }, - marginRight: 0, - }, -}); +// type ClassNames = +// 'searchBar' | +// 'searchBoxInner' | +// 'searchIcon'; + +// const styles = (theme: Theme) => ({ +// searchBar: { +// maxWidth: '100%', +// }, +// searchBoxInner: { +// '& > div': { +// maxWidth: '100%', +// }, +// backgroundColor: theme.color.grey2, +// marginTop: 0, +// padding: theme.spacing(3), +// }, +// searchIcon: { +// '& svg': { +// color: theme.palette.text.primary, +// }, +// marginRight: 0, +// }, +// }); interface State { query: string; } -interface Props { - classes?: Partial>; -} +// interface Props { +// classes?: Partial>; +// } -export type CombinedProps = AlgoliaProps & Props & RouteComponentProps<{}>; +export type CombinedProps = AlgoliaProps & + // Props & + RouteComponentProps<{}>; class SupportSearchLanding extends React.Component { componentDidMount() { @@ -62,7 +68,7 @@ class SupportSearchLanding extends React.Component { render() { const { searchEnabled, searchError, searchResults } = this.props; - const classes = withStyles.getClasses(this.props); + // const classes = withStyles.getClasses(this.props); const { query } = this.state; const [docs, community] = searchResults; @@ -82,16 +88,22 @@ class SupportSearchLanding extends React.Component { {searchError && {searchError}} - + - + ), + sx: { + maxWidth: '100%', + }, }} - className={classes.searchBoxInner} + // className={classes.searchBoxInner} data-qa-search-landing-input disabled={!Boolean(searchEnabled)} hideLabel @@ -143,9 +155,32 @@ class SupportSearchLanding extends React.Component { }; } -const searchable = withSearch({ highlight: false, hitsPerPage: 5 }); - -export default compose( - searchable, - withRouter -)(withStyles(SupportSearchLanding, styles)); +const StyledTextFieldComponent = styled(TextField, { + label: 'StyledTextFieldComponent', +})(({ theme }) => ({ + '&& > div': { + maxWidth: '100%', + }, + backgroundColor: theme.color.grey2, + marginTop: 0, + padding: theme.spacing(3), +})); + +const StyledInputAdornment = styled(InputAdornment, { + label: 'StyledInputAdornment', +})(({ theme }) => ({ + '&& svg': { + color: theme.palette.text.primary, + }, + marginRight: 0, +})); + +// const searchable = withSearch({ highlight: false, hitsPerPage: 5 }); +// export default compose( +// searchable, +// withRouter +// )(withStyles(SupportSearchLanding, styles)); + +export default withSearch({ highlight: true, hitsPerPage: 10 })( + withRouter(SupportSearchLanding) +); From 6ca41133edfdb82e89001c4dfadbd31039bddb83 Mon Sep 17 00:00:00 2001 From: ecarrill Date: Thu, 27 Jul 2023 06:38:15 -0700 Subject: [PATCH 07/14] Convert class comp to funct comp and rewrite tests --- .../src/components/Breadcrumb/FinalCrumb.tsx | 2 +- .../src/components/H1Header/H1Header.tsx | 11 +- .../DocumentationResults.tsx | 7 +- .../SupportSearchLanding.test.tsx | 73 ++--- .../SupportSearchLanding.tsx | 267 +++++++----------- 5 files changed, 141 insertions(+), 219 deletions(-) diff --git a/packages/manager/src/components/Breadcrumb/FinalCrumb.tsx b/packages/manager/src/components/Breadcrumb/FinalCrumb.tsx index ec9242f5d91..bc6ebbf5f88 100644 --- a/packages/manager/src/components/Breadcrumb/FinalCrumb.tsx +++ b/packages/manager/src/components/Breadcrumb/FinalCrumb.tsx @@ -62,7 +62,7 @@ export const FinalCrumb = React.memo((props: Props) => { [classes.crumb]: true, [classes.noCap]: labelOptions && labelOptions.noCap, })} - dataQaEl={crumb} + data-testid={crumb} title={crumb} /> {labelOptions && labelOptions.subtitle && ( diff --git a/packages/manager/src/components/H1Header/H1Header.tsx b/packages/manager/src/components/H1Header/H1Header.tsx index be2a9c248db..f2916eb8321 100644 --- a/packages/manager/src/components/H1Header/H1Header.tsx +++ b/packages/manager/src/components/H1Header/H1Header.tsx @@ -5,7 +5,6 @@ import { Typography } from 'src/components/Typography'; interface H1HeaderProps { className?: string; - dataQaEl?: string; renderAsSecondary?: boolean; sx?: SxProps; title: string; @@ -16,13 +15,7 @@ interface H1HeaderProps { // It should serve as the only source for all H1s export const H1Header = (props: H1HeaderProps) => { const h1Header = React.useRef(null); - const { className, dataQaEl, renderAsSecondary, sx, title } = props; - - // React.useEffect(() => { - // if (h1Header.current !== null) { - // h1Header.current.focus(); - // } - // }, []); + const { className, renderAsSecondary, sx, title } = props; return ( { }} className={className} component={renderAsSecondary ? 'h2' : 'h1'} - data-qa-header={dataQaEl ? dataQaEl : ''} + data-testid="data-qa-support-search-landing-title" ref={renderAsSecondary ? null : h1Header} // If we're rendering as an h2, we want to remove the autofocus functionality tabIndex={0} variant="h1" diff --git a/packages/manager/src/features/Help/SupportSearchLanding/DocumentationResults.tsx b/packages/manager/src/features/Help/SupportSearchLanding/DocumentationResults.tsx index ee9f43630b1..4f57694aeca 100644 --- a/packages/manager/src/features/Help/SupportSearchLanding/DocumentationResults.tsx +++ b/packages/manager/src/features/Help/SupportSearchLanding/DocumentationResults.tsx @@ -73,7 +73,12 @@ export const DocumentationResults = (props: Props) => { const renderEmptyState = (): JSX.Element => { return ( - No results + + No results + ); }; diff --git a/packages/manager/src/features/Help/SupportSearchLanding/SupportSearchLanding.test.tsx b/packages/manager/src/features/Help/SupportSearchLanding/SupportSearchLanding.test.tsx index 3389b96616e..de460bfe13f 100644 --- a/packages/manager/src/features/Help/SupportSearchLanding/SupportSearchLanding.test.tsx +++ b/packages/manager/src/features/Help/SupportSearchLanding/SupportSearchLanding.test.tsx @@ -1,21 +1,14 @@ -import { shallow } from 'enzyme'; +import { screen } from '@testing-library/react'; import { assocPath } from 'ramda'; import * as React from 'react'; import { reactRouterProps } from 'src/__data__/reactRouterProps'; import { H1Header } from 'src/components/H1Header/H1Header'; +import { renderWithTheme } from 'src/utilities/testHelpers'; import SupportSearchLanding, { CombinedProps } from './SupportSearchLanding'; -// const classes = { -// backButton: '', -// searchBar: '', -// searchBoxInner: '', -// searchIcon: '', -// }; - const props: CombinedProps = { - // classes, searchAlgolia: jest.fn(), searchEnabled: true, searchResults: [[], []], @@ -24,57 +17,43 @@ const props: CombinedProps = { const propsWithMultiWordURLQuery = assocPath( ['location', 'search'], - '?query=search%20two%20words', + '?query=two%20words', props ); -const component = shallow( - -); - -// Query is read on mount so we have to mount twice. -const component2 = shallow( - -); - -describe('Component', () => { +describe('SupportSearchLanding Component', () => { it('should render', () => { - expect(component).toBeDefined(); + renderWithTheme(); + expect( + screen.getByTestId('data-qa-support-search-landing-title') + ).toBeInTheDocument(); }); - it('should set the query from the URL param to state', () => { - expect(component.state().query).toMatch('search'); + it('should display generic text if no query string is provided', () => { + renderWithTheme(); + expect(screen.getByText('Search')).toBeInTheDocument(); }); - it('should read multi-word queries correctly', () => { - expect(component2.state().query).toMatch('search two words'); + it('should display query string in the header', () => { + renderWithTheme(); + expect(screen.getByText(props.location.search)).toBeInTheDocument(); }); - it('should display the query text in the header', () => { + it('should display multi-word query string in the header', () => { + renderWithTheme( + + ); expect( - component.containsMatchingElement( - - ) - ).toBeTruthy(); + screen.getByText(propsWithMultiWordURLQuery.location.search) + ).toBeInTheDocument(); }); - it('should display generic text if no query is provided', () => { - component.setState({ query: '' }); - expect( - component.containsMatchingElement( - - ) - ).toBeTruthy(); + it('should display empty DocumentationResults components with empty query string', () => { + const newProps = assocPath(['location', 'search'], '?query=', props); + + renderWithTheme(); expect( - component.containsMatchingElement( - - ) - ).toBeFalsy(); + screen.getAllByTestId('data-qa-documentation-no-results') + ).toHaveLength(2); }); }); diff --git a/packages/manager/src/features/Help/SupportSearchLanding/SupportSearchLanding.tsx b/packages/manager/src/features/Help/SupportSearchLanding/SupportSearchLanding.tsx index 76939e417bf..120658b6806 100644 --- a/packages/manager/src/features/Help/SupportSearchLanding/SupportSearchLanding.tsx +++ b/packages/manager/src/features/Help/SupportSearchLanding/SupportSearchLanding.tsx @@ -1,12 +1,10 @@ import Search from '@mui/icons-material/Search'; import Box from '@mui/material/Box'; import Grid from '@mui/material/Unstable_Grid2'; -// import { Theme } from '@mui/material/styles'; +import { Theme } from '@mui/material/styles'; import * as React from 'react'; import { RouteComponentProps, withRouter } from 'react-router-dom'; -// import { compose } from 'recompose'; -// import { withStyles } from 'tss-react/mui'; -import { styled } from '@mui/material/styles'; +import { makeStyles } from 'tss-react/mui'; import { H1Header } from 'src/components/H1Header/H1Header'; import { Notice } from 'src/components/Notice/Notice'; @@ -19,168 +17,115 @@ import withSearch, { AlgoliaState as AlgoliaProps } from '../SearchHOC'; import { DocumentationResults, SearchResult } from './DocumentationResults'; import HelpResources from './HelpResources'; -// type ClassNames = -// 'searchBar' | -// 'searchBoxInner' | -// 'searchIcon'; - -// const styles = (theme: Theme) => ({ -// searchBar: { -// maxWidth: '100%', -// }, -// searchBoxInner: { -// '& > div': { -// maxWidth: '100%', -// }, -// backgroundColor: theme.color.grey2, -// marginTop: 0, -// padding: theme.spacing(3), -// }, -// searchIcon: { -// '& svg': { -// color: theme.palette.text.primary, -// }, -// marginRight: 0, -// }, -// }); - -interface State { - query: string; -} - -// interface Props { -// classes?: Partial>; -// } - -export type CombinedProps = AlgoliaProps & - // Props & - RouteComponentProps<{}>; - -class SupportSearchLanding extends React.Component { - componentDidMount() { - this.searchFromParams(); - } - componentDidUpdate(prevProps: CombinedProps) { - if (!prevProps.searchEnabled && this.props.searchEnabled) { - this.searchFromParams(); - } - } - - render() { - const { searchEnabled, searchError, searchResults } = this.props; - // const classes = withStyles.getClasses(this.props); - const { query } = this.state; - const [docs, community] = searchResults; - - return ( - - - 1 ? `Search results for "${query}"` : 'Search' - } - data-qa-support-search-landing-title - /> - - - {searchError && {searchError}} - - - - ), - sx: { - maxWidth: '100%', - }, - }} - // className={classes.searchBoxInner} - data-qa-search-landing-input - disabled={!Boolean(searchEnabled)} - hideLabel - label="Search Linode documentation and community questions" - onChange={this.onInputChange} - placeholder="Search Linode documentation and community questions" - value={query} - /> - - - - - - - - - - ); - } - - searchFromParams() { - const query = getQueryParamFromQueryString( - this.props.location.search, - 'query' - ); - this.setState({ query }); - this.props.searchAlgolia(query); - } - - onInputChange = (e: React.ChangeEvent) => { - const newQuery = e.target.value ?? ''; - this.setState({ query: newQuery }); - this.props.history.replace({ search: `?query=${newQuery}` }); - this.props.searchAlgolia(newQuery); - }; - - searchIndex: any = null; - - state: State = { - query: '', - }; -} - -const StyledTextFieldComponent = styled(TextField, { - label: 'StyledTextFieldComponent', -})(({ theme }) => ({ - '&& > div': { +const useStyles = makeStyles()((theme: Theme) => ({ + searchBar: { maxWidth: '100%', }, - backgroundColor: theme.color.grey2, - marginTop: 0, - padding: theme.spacing(3), -})); - -const StyledInputAdornment = styled(InputAdornment, { - label: 'StyledInputAdornment', -})(({ theme }) => ({ - '&& svg': { - color: theme.palette.text.primary, + searchBoxInner: { + '& > div': { + maxWidth: '100%', + }, + backgroundColor: theme.color.grey2, + marginTop: 0, + padding: theme.spacing(3), + }, + searchIcon: { + '& svg': { + color: theme.palette.text.primary, + }, + marginRight: 0, }, - marginRight: 0, })); -// const searchable = withSearch({ highlight: false, hitsPerPage: 5 }); -// export default compose( -// searchable, -// withRouter -// )(withStyles(SupportSearchLanding, styles)); +export type CombinedProps = AlgoliaProps & RouteComponentProps<{}>; + +const SupportSearchLanding = (props: CombinedProps) => { + const { + history, + searchAlgolia, + searchEnabled, + searchError, + searchResults, + } = props; + const [docs, community] = searchResults; + const { classes } = useStyles(); + + const [queryString, setQueryString] = React.useState(''); + + React.useEffect(() => { + searchFromParams(); + }, []); + + const searchFromParams = () => { + const query = getQueryParamFromQueryString(location.search, 'query'); + setQueryString(query); + searchAlgolia(query); + }; + + const onInputChange = (e: React.ChangeEvent) => { + const newQuery = e.target.value ?? ''; + setQueryString(newQuery); + history.replace({ search: `?query=${newQuery}` }); + searchAlgolia(newQuery); + }; -export default withSearch({ highlight: true, hitsPerPage: 10 })( + return ( + + + 1 + ? `Search results for "${queryString}"` + : 'Search' + } + data-qa-support-search-landing-title + /> + + + {searchError && {searchError}} + + + + ), + }} + className={classes.searchBoxInner} + // data-qa-search-landing-input + data-testid="search-landing-input" + disabled={!Boolean(searchEnabled)} + hideLabel + label="Search Linode documentation and community questions" + onChange={onInputChange} + placeholder="Search Linode documentation and community questions" + value={queryString} + /> + + + + + + + + + + ); +}; + +export default withSearch({ highlight: false, hitsPerPage: 5 })( withRouter(SupportSearchLanding) ); From b95a172aaca09dff3a366453ddef0be948fb0aaa Mon Sep 17 00:00:00 2001 From: ecarrill Date: Thu, 27 Jul 2023 07:21:18 -0700 Subject: [PATCH 08/14] Remove dead code from PR feedback --- .../features/Help/Panels/AlgoliaSearchBar.tsx | 42 ++++++------------- 1 file changed, 13 insertions(+), 29 deletions(-) diff --git a/packages/manager/src/features/Help/Panels/AlgoliaSearchBar.tsx b/packages/manager/src/features/Help/Panels/AlgoliaSearchBar.tsx index 70883ccbe43..5bd2094b645 100644 --- a/packages/manager/src/features/Help/Panels/AlgoliaSearchBar.tsx +++ b/packages/manager/src/features/Help/Panels/AlgoliaSearchBar.tsx @@ -1,15 +1,13 @@ import Search from '@mui/icons-material/Search'; -import { Theme, useTheme } from '@mui/material/styles'; +import { Theme } from '@mui/material/styles'; import { pathOr } from 'ramda'; import * as React from 'react'; import { RouteComponentProps, withRouter } from 'react-router-dom'; -import { compose } from 'recompose'; import { makeStyles } from 'tss-react/mui'; import EnhancedSelect, { Item } from 'src/components/EnhancedSelect'; import { Notice } from 'src/components/Notice/Notice'; import { selectStyles } from 'src/features/TopMenu/SearchBar'; -import windowIsNarrowerThan from 'src/utilities/breakpoints'; import withSearch, { AlgoliaState as AlgoliaProps } from '../SearchHOC'; import { SearchItem } from './SearchItem'; @@ -58,19 +56,15 @@ type CombinedProps = AlgoliaProps & RouteComponentProps<{}>; const AlgoliaSearchBar = (props: CombinedProps) => { const { classes } = useStyles(); - const theme = useTheme(); const [inputValue, setInputValue] = React.useState(''); - const [mounted, setMounted] = React.useState(false); - const [isMobile, setIsMobile] = React.useState(false); - const { searchEnabled, searchError } = props; + const { history, searchAlgolia, searchEnabled, searchError } = props; + + const [options, setOptions] = React.useState([]); React.useEffect(() => { - setMounted(true); - if (theme) { - setIsMobile(windowIsNarrowerThan(theme.breakpoints.values.sm)); - } - return () => setMounted(false); - }, [isMobile, theme]); + const options = getOptionsFromResults(); + setOptions(options); + }, []); const getOptionsFromResults = () => { const [docs, community] = props.searchResults; @@ -83,11 +77,8 @@ const AlgoliaSearchBar = (props: CombinedProps) => { }; const onInputValueChange = (inputValue: string) => { - if (!mounted) { - return; - } setInputValue(inputValue); - props.searchAlgolia(inputValue); + searchAlgolia(inputValue); }; const getLinkTarget = (inputValue: string) => { @@ -97,26 +88,19 @@ const AlgoliaSearchBar = (props: CombinedProps) => { }; const handleSelect = (selected: Item) => { - if (!selected) { + if (!selected || !inputValue) { return; } - const { history } = props; - if (!inputValue) { - return; - } - - const href = pathOr('', ['data', 'href'], selected); if (selected.value === 'search') { const link = getLinkTarget(inputValue); history.push(link); } else { + const href = pathOr('', ['data', 'href'], selected); window.open(href, '_blank', 'noopener'); } }; - const options = getOptionsFromResults(); - return ( {searchError && ( @@ -148,6 +132,6 @@ const AlgoliaSearchBar = (props: CombinedProps) => { ); }; -const search = withSearch({ highlight: true, hitsPerPage: 10 }); - -export default compose(search, withRouter)(AlgoliaSearchBar); +export default withSearch({ highlight: true, hitsPerPage: 10 })( + withRouter(AlgoliaSearchBar) +); From 34e1f0068bed72209c241f8a80adba966b010c29 Mon Sep 17 00:00:00 2001 From: ecarrill Date: Sun, 30 Jul 2023 13:27:28 -0700 Subject: [PATCH 09/14] Fix error with search results --- .../src/components/Breadcrumb/FinalCrumb.tsx | 2 +- .../src/components/H1Header/H1Header.tsx | 5 +++-- .../features/Help/Panels/AlgoliaSearchBar.tsx | 21 +++++++++---------- .../SupportSearchLanding.test.tsx | 4 +--- .../SupportSearchLanding.tsx | 6 +++--- 5 files changed, 18 insertions(+), 20 deletions(-) diff --git a/packages/manager/src/components/Breadcrumb/FinalCrumb.tsx b/packages/manager/src/components/Breadcrumb/FinalCrumb.tsx index bc6ebbf5f88..ec9242f5d91 100644 --- a/packages/manager/src/components/Breadcrumb/FinalCrumb.tsx +++ b/packages/manager/src/components/Breadcrumb/FinalCrumb.tsx @@ -62,7 +62,7 @@ export const FinalCrumb = React.memo((props: Props) => { [classes.crumb]: true, [classes.noCap]: labelOptions && labelOptions.noCap, })} - data-testid={crumb} + dataQaEl={crumb} title={crumb} /> {labelOptions && labelOptions.subtitle && ( diff --git a/packages/manager/src/components/H1Header/H1Header.tsx b/packages/manager/src/components/H1Header/H1Header.tsx index f2916eb8321..990de603a58 100644 --- a/packages/manager/src/components/H1Header/H1Header.tsx +++ b/packages/manager/src/components/H1Header/H1Header.tsx @@ -5,6 +5,7 @@ import { Typography } from 'src/components/Typography'; interface H1HeaderProps { className?: string; + dataQaEl?: string; renderAsSecondary?: boolean; sx?: SxProps; title: string; @@ -15,7 +16,7 @@ interface H1HeaderProps { // It should serve as the only source for all H1s export const H1Header = (props: H1HeaderProps) => { const h1Header = React.useRef(null); - const { className, renderAsSecondary, sx, title } = props; + const { className, dataQaEl, renderAsSecondary, sx, title } = props; return ( { }} className={className} component={renderAsSecondary ? 'h2' : 'h1'} - data-testid="data-qa-support-search-landing-title" + data-qa-header={dataQaEl ? dataQaEl : ''} ref={renderAsSecondary ? null : h1Header} // If we're rendering as an h2, we want to remove the autofocus functionality tabIndex={0} variant="h1" diff --git a/packages/manager/src/features/Help/Panels/AlgoliaSearchBar.tsx b/packages/manager/src/features/Help/Panels/AlgoliaSearchBar.tsx index 5bd2094b645..a61667199c6 100644 --- a/packages/manager/src/features/Help/Panels/AlgoliaSearchBar.tsx +++ b/packages/manager/src/features/Help/Panels/AlgoliaSearchBar.tsx @@ -62,19 +62,18 @@ const AlgoliaSearchBar = (props: CombinedProps) => { const [options, setOptions] = React.useState([]); React.useEffect(() => { - const options = getOptionsFromResults(); - setOptions(options); - }, []); + const getOptionsFromResults = () => { + const [docs, community] = props.searchResults; + const options = [...docs, ...community]; - const getOptionsFromResults = () => { - const [docs, community] = props.searchResults; - const options = [...docs, ...community]; + return [ + { data: { source: 'finalLink' }, label: inputValue, value: 'search' }, + ...options, + ]; + }; - return [ - { data: { source: 'finalLink' }, label: inputValue, value: 'search' }, - ...options, - ]; - }; + setOptions(getOptionsFromResults()); + }, [inputValue, props.searchResults]); const onInputValueChange = (inputValue: string) => { setInputValue(inputValue); diff --git a/packages/manager/src/features/Help/SupportSearchLanding/SupportSearchLanding.test.tsx b/packages/manager/src/features/Help/SupportSearchLanding/SupportSearchLanding.test.tsx index de460bfe13f..f0938bad6b3 100644 --- a/packages/manager/src/features/Help/SupportSearchLanding/SupportSearchLanding.test.tsx +++ b/packages/manager/src/features/Help/SupportSearchLanding/SupportSearchLanding.test.tsx @@ -24,9 +24,7 @@ const propsWithMultiWordURLQuery = assocPath( describe('SupportSearchLanding Component', () => { it('should render', () => { renderWithTheme(); - expect( - screen.getByTestId('data-qa-support-search-landing-title') - ).toBeInTheDocument(); + expect(screen.getByTestId('support-search-landing')).toBeInTheDocument(); }); it('should display generic text if no query string is provided', () => { diff --git a/packages/manager/src/features/Help/SupportSearchLanding/SupportSearchLanding.tsx b/packages/manager/src/features/Help/SupportSearchLanding/SupportSearchLanding.tsx index 120658b6806..f9b1ad7052b 100644 --- a/packages/manager/src/features/Help/SupportSearchLanding/SupportSearchLanding.tsx +++ b/packages/manager/src/features/Help/SupportSearchLanding/SupportSearchLanding.tsx @@ -70,7 +70,7 @@ const SupportSearchLanding = (props: CombinedProps) => { }; return ( - + { : 'Search' } data-qa-support-search-landing-title + dataQaEl={queryString} /> @@ -97,8 +98,7 @@ const SupportSearchLanding = (props: CombinedProps) => { ), }} className={classes.searchBoxInner} - // data-qa-search-landing-input - data-testid="search-landing-input" + data-qa-search-landing-input disabled={!Boolean(searchEnabled)} hideLabel label="Search Linode documentation and community questions" From 6cec5365a2047455a898d387cac4c2624da6c6b7 Mon Sep 17 00:00:00 2001 From: ecarrill Date: Tue, 1 Aug 2023 11:42:37 -0700 Subject: [PATCH 10/14] Debounce searchAlgolia func and minor clean up --- .../features/Help/Panels/AlgoliaSearchBar.tsx | 41 +++++++++++-------- .../src/features/Help/Panels/PopularPosts.tsx | 2 +- .../src/features/Help/Panels/SearchPanel.tsx | 2 +- .../src/features/Users/UserPermissions.tsx | 1 - 4 files changed, 26 insertions(+), 20 deletions(-) diff --git a/packages/manager/src/features/Help/Panels/AlgoliaSearchBar.tsx b/packages/manager/src/features/Help/Panels/AlgoliaSearchBar.tsx index a61667199c6..0294e16c5fc 100644 --- a/packages/manager/src/features/Help/Panels/AlgoliaSearchBar.tsx +++ b/packages/manager/src/features/Help/Panels/AlgoliaSearchBar.tsx @@ -3,6 +3,7 @@ import { Theme } from '@mui/material/styles'; import { pathOr } from 'ramda'; import * as React from 'react'; import { RouteComponentProps, withRouter } from 'react-router-dom'; +import { debounce } from 'throttle-debounce'; import { makeStyles } from 'tss-react/mui'; import EnhancedSelect, { Item } from 'src/components/EnhancedSelect'; @@ -57,29 +58,35 @@ type CombinedProps = AlgoliaProps & RouteComponentProps<{}>; const AlgoliaSearchBar = (props: CombinedProps) => { const { classes } = useStyles(); const [inputValue, setInputValue] = React.useState(''); - const { history, searchAlgolia, searchEnabled, searchError } = props; + const { + history, + searchAlgolia, + searchEnabled, + searchError, + searchResults, + } = props; - const [options, setOptions] = React.useState([]); + const options = React.useMemo(() => { + const [docs, community] = searchResults; + const mergedOptions = [...docs, ...community]; - React.useEffect(() => { - const getOptionsFromResults = () => { - const [docs, community] = props.searchResults; - const options = [...docs, ...community]; - - return [ - { data: { source: 'finalLink' }, label: inputValue, value: 'search' }, - ...options, - ]; - }; - - setOptions(getOptionsFromResults()); - }, [inputValue, props.searchResults]); + return [ + { data: { source: 'finalLink' }, label: inputValue, value: 'search' }, + ...mergedOptions, + ]; + }, [inputValue, searchResults]); const onInputValueChange = (inputValue: string) => { - setInputValue(inputValue); - searchAlgolia(inputValue); + debouncedSearchAlgolia(inputValue); }; + const debouncedSearchAlgolia = React.useRef( + debounce(100, false, (inputValue) => { + setInputValue(inputValue); + searchAlgolia(inputValue); + }) + ).current; + const getLinkTarget = (inputValue: string) => { return inputValue ? `/support/search/?query=${inputValue}` diff --git a/packages/manager/src/features/Help/Panels/PopularPosts.tsx b/packages/manager/src/features/Help/Panels/PopularPosts.tsx index e56250095c7..fcd469d1c70 100644 --- a/packages/manager/src/features/Help/Panels/PopularPosts.tsx +++ b/packages/manager/src/features/Help/Panels/PopularPosts.tsx @@ -30,7 +30,7 @@ const useStyles = makeStyles((theme: Theme) => ({ }, })); -export const PopularPosts: React.FC = () => { +export const PopularPosts = () => { const classes = useStyles(); const renderPopularDocs = () => { diff --git a/packages/manager/src/features/Help/Panels/SearchPanel.tsx b/packages/manager/src/features/Help/Panels/SearchPanel.tsx index e85f40cbc3f..3a2150403b3 100644 --- a/packages/manager/src/features/Help/Panels/SearchPanel.tsx +++ b/packages/manager/src/features/Help/Panels/SearchPanel.tsx @@ -36,7 +36,7 @@ const StyledRootContainer = styled(Paper, { const StyledH1Header = styled(H1Header, { label: 'StyledH1Header', })(({ theme }) => ({ - color: 'white', + color: theme.name === 'dark' ? theme.color.black : theme.color.white, marginBottom: theme.spacing(), position: 'relative', textAlign: 'center', diff --git a/packages/manager/src/features/Users/UserPermissions.tsx b/packages/manager/src/features/Users/UserPermissions.tsx index 8c13da5fa16..ab6d25b9b60 100644 --- a/packages/manager/src/features/Users/UserPermissions.tsx +++ b/packages/manager/src/features/Users/UserPermissions.tsx @@ -178,7 +178,6 @@ class UserPermissions extends React.Component { if (updateFns.length) { this.setState((compose as any)(...updateFns)); } - // return; } }; From 6ed75f9c4da33ded7c55abaa4e181a49a5375382 Mon Sep 17 00:00:00 2001 From: ecarrill Date: Tue, 1 Aug 2023 13:54:31 -0700 Subject: [PATCH 11/14] Replace useMemo with useCallback --- .../src/features/Help/Panels/AlgoliaSearchBar.tsx | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/packages/manager/src/features/Help/Panels/AlgoliaSearchBar.tsx b/packages/manager/src/features/Help/Panels/AlgoliaSearchBar.tsx index 0294e16c5fc..4cd7956c246 100644 --- a/packages/manager/src/features/Help/Panels/AlgoliaSearchBar.tsx +++ b/packages/manager/src/features/Help/Panels/AlgoliaSearchBar.tsx @@ -77,15 +77,16 @@ const AlgoliaSearchBar = (props: CombinedProps) => { }, [inputValue, searchResults]); const onInputValueChange = (inputValue: string) => { + setInputValue(inputValue); debouncedSearchAlgolia(inputValue); }; - const debouncedSearchAlgolia = React.useRef( - debounce(100, false, (inputValue) => { - setInputValue(inputValue); + const debouncedSearchAlgolia = React.useCallback( + debounce(200, false, (inputValue: string) => { searchAlgolia(inputValue); - }) - ).current; + }), + [searchAlgolia] + ); const getLinkTarget = (inputValue: string) => { return inputValue From 8923bf5ddfadf6dff7f78276af3a40b305725f29 Mon Sep 17 00:00:00 2001 From: ecarrill Date: Tue, 1 Aug 2023 14:27:03 -0700 Subject: [PATCH 12/14] Remove old styles in favor of using sx prop --- .../src/features/Help/Panels/OtherWays.tsx | 25 ++++++++----------- 1 file changed, 10 insertions(+), 15 deletions(-) diff --git a/packages/manager/src/features/Help/Panels/OtherWays.tsx b/packages/manager/src/features/Help/Panels/OtherWays.tsx index 4df69ed3682..17b7bf7c433 100644 --- a/packages/manager/src/features/Help/Panels/OtherWays.tsx +++ b/packages/manager/src/features/Help/Panels/OtherWays.tsx @@ -1,7 +1,6 @@ import Grid from '@mui/material/Unstable_Grid2'; -import { Theme } from '@mui/material/styles'; +import { useTheme } from '@mui/material/styles'; import * as React from 'react'; -import { makeStyles } from 'tss-react/mui'; import Community from 'src/assets/icons/community.svg'; import Documentation from 'src/assets/icons/document.svg'; @@ -10,25 +9,21 @@ import Support from 'src/assets/icons/support.svg'; import { Tile } from 'src/components/Tile/Tile'; import { Typography } from 'src/components/Typography'; -const useStyles = makeStyles()((theme: Theme) => ({ - heading: { - marginBottom: theme.spacing(4), - textAlign: 'center', - }, - wrapper: { - marginTop: theme.spacing(2), - }, -})); - export const OtherWays = () => { - const { classes } = useStyles(); + const theme = useTheme(); return ( - + Other Ways to Get Help - + Date: Tue, 1 Aug 2023 14:41:19 -0700 Subject: [PATCH 13/14] Replace old styles in favor of sx prop --- .../src/features/Help/StatusBanners.tsx | 52 ++++++++++++------- 1 file changed, 33 insertions(+), 19 deletions(-) diff --git a/packages/manager/src/features/Help/StatusBanners.tsx b/packages/manager/src/features/Help/StatusBanners.tsx index 80c0cd253a5..bf9af56286f 100644 --- a/packages/manager/src/features/Help/StatusBanners.tsx +++ b/packages/manager/src/features/Help/StatusBanners.tsx @@ -1,8 +1,9 @@ import Box from '@mui/material/Box'; -import { Theme } from '@mui/material/styles'; +import { useTheme } from '@mui/material/styles'; +// import { Theme } from '@mui/material/styles'; import { DateTime } from 'luxon'; import * as React from 'react'; -import { makeStyles } from 'tss-react/mui'; +// import { makeStyles } from 'tss-react/mui'; import DismissibleBanner from 'src/components/DismissibleBanner'; import { Link } from 'src/components/Link'; @@ -16,19 +17,19 @@ import { capitalize } from 'src/utilities/capitalize'; import { sanitizeHTML } from 'src/utilities/sanitize-html'; import { truncateEnd } from 'src/utilities/truncate'; -const useStyles = makeStyles()((theme: Theme) => ({ - header: { - fontSize: '1rem', - marginBottom: theme.spacing(), - }, - root: { - marginBottom: theme.spacing(), - }, - text: { - fontSize: '0.875rem', - lineHeight: '1.25rem', - }, -})); +// const useStyles = makeStyles()((theme: Theme) => ({ +// header: { +// fontSize: '1rem', +// marginBottom: theme.spacing(), +// }, +// root: { +// marginBottom: theme.spacing(), +// }, +// text: { +// fontSize: '0.875rem', +// lineHeight: '1.25rem', +// }, +// })); export const StatusBanners = () => { const { data: incidentsData } = useIncidentQuery(); @@ -72,7 +73,8 @@ export interface IncidentProps { export const IncidentBanner = React.memo((props: IncidentProps) => { const { href, impact, message, status: _status, title } = props; const status = _status ?? ''; - const { classes } = useStyles(); + // const { classes } = useStyles(); + const theme = useTheme(); const preferenceKey = `${href}-${status}`; @@ -89,12 +91,20 @@ export const IncidentBanner = React.memo((props: IncidentProps) => { ['major', 'minor', 'none'].includes(impact) || ['monitoring', 'resolved'].includes(status) } - className={classes.root} important preferenceKey={preferenceKey} + // className={classes.root} + sx={{ marginBottom: theme.spacing() }} > - + {title} @@ -106,7 +116,11 @@ export const IncidentBanner = React.memo((props: IncidentProps) => { dangerouslySetInnerHTML={{ __html: sanitizeHTML(truncateEnd(message, 500)), }} - className={classes.text} + // className={classes.text} + sx={{ + fontSize: '0.875rem', + lineHeight: '1.25rem', + }} /> From 67703a9583a152dea6ef07e7b1ecddec0bc1f41b Mon Sep 17 00:00:00 2001 From: ecarrill Date: Tue, 1 Aug 2023 18:26:33 -0700 Subject: [PATCH 14/14] Remove comments and default export --- packages/manager/src/MainContent.tsx | 7 +++++-- .../src/features/Help/StatusBanners.tsx | 20 ------------------- packages/manager/src/features/Help/index.tsx | 4 +--- 3 files changed, 6 insertions(+), 25 deletions(-) diff --git a/packages/manager/src/MainContent.tsx b/packages/manager/src/MainContent.tsx index 18b2b49dfd0..0d4b56d4737 100644 --- a/packages/manager/src/MainContent.tsx +++ b/packages/manager/src/MainContent.tsx @@ -162,8 +162,11 @@ const SupportTicketDetail = React.lazy( ); const Longview = React.lazy(() => import('src/features/Longview')); const Managed = React.lazy(() => import('src/features/Managed')); -const Help = React.lazy(() => import('src/features/Help')); - +const Help = React.lazy(() => + import('./features/Help/index').then((module) => ({ + default: module.HelpAndSupport, + })) +); const SearchLanding = React.lazy(() => import('src/features/Search')); const EventsLanding = React.lazy( () => import('src/features/Events/EventsLanding') diff --git a/packages/manager/src/features/Help/StatusBanners.tsx b/packages/manager/src/features/Help/StatusBanners.tsx index bf9af56286f..302740f9e09 100644 --- a/packages/manager/src/features/Help/StatusBanners.tsx +++ b/packages/manager/src/features/Help/StatusBanners.tsx @@ -1,9 +1,7 @@ import Box from '@mui/material/Box'; import { useTheme } from '@mui/material/styles'; -// import { Theme } from '@mui/material/styles'; import { DateTime } from 'luxon'; import * as React from 'react'; -// import { makeStyles } from 'tss-react/mui'; import DismissibleBanner from 'src/components/DismissibleBanner'; import { Link } from 'src/components/Link'; @@ -17,20 +15,6 @@ import { capitalize } from 'src/utilities/capitalize'; import { sanitizeHTML } from 'src/utilities/sanitize-html'; import { truncateEnd } from 'src/utilities/truncate'; -// const useStyles = makeStyles()((theme: Theme) => ({ -// header: { -// fontSize: '1rem', -// marginBottom: theme.spacing(), -// }, -// root: { -// marginBottom: theme.spacing(), -// }, -// text: { -// fontSize: '0.875rem', -// lineHeight: '1.25rem', -// }, -// })); - export const StatusBanners = () => { const { data: incidentsData } = useIncidentQuery(); const incidents = incidentsData?.incidents ?? []; @@ -73,7 +57,6 @@ export interface IncidentProps { export const IncidentBanner = React.memo((props: IncidentProps) => { const { href, impact, message, status: _status, title } = props; const status = _status ?? ''; - // const { classes } = useStyles(); const theme = useTheme(); const preferenceKey = `${href}-${status}`; @@ -93,7 +76,6 @@ export const IncidentBanner = React.memo((props: IncidentProps) => { } important preferenceKey={preferenceKey} - // className={classes.root} sx={{ marginBottom: theme.spacing() }} > @@ -102,7 +84,6 @@ export const IncidentBanner = React.memo((props: IncidentProps) => { fontSize: '1rem', marginBottom: theme.spacing(), }} - // className={classes.header} data-testid="status-banner" > @@ -116,7 +97,6 @@ export const IncidentBanner = React.memo((props: IncidentProps) => { dangerouslySetInnerHTML={{ __html: sanitizeHTML(truncateEnd(message, 500)), }} - // className={classes.text} sx={{ fontSize: '0.875rem', lineHeight: '1.25rem', diff --git a/packages/manager/src/features/Help/index.tsx b/packages/manager/src/features/Help/index.tsx index 20651a58d27..a8d600f140f 100644 --- a/packages/manager/src/features/Help/index.tsx +++ b/packages/manager/src/features/Help/index.tsx @@ -19,7 +19,7 @@ const SupportTicketDetail = React.lazy( () => import('src/features/Support/SupportTicketDetail') ); -const HelpAndSupport = () => { +export const HelpAndSupport = () => { return ( <> @@ -46,5 +46,3 @@ const HelpAndSupport = () => { ); }; - -export default HelpAndSupport;