Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

refactor: [M3-6268] - MUI v5 Migration SRC > Features > Help #9408

Merged
merged 25 commits into from
Aug 2, 2023
Merged
Show file tree
Hide file tree
Changes from 5 commits
Commits
Show all changes
25 commits
Select commit Hold shift + click to select a range
1c6e110
refactor: [M3-6268] - Apply previous work
carrillo-erik Jul 17, 2023
a746552
Remove redundant index file
carrillo-erik Jul 17, 2023
45ff7fa
Add changeset
carrillo-erik Jul 17, 2023
e41e6bd
Merge branch 'develop' of https://github.com/linode/manager into refa…
carrillo-erik Jul 18, 2023
cebb951
Fix issues from PR feedback
carrillo-erik Jul 18, 2023
3371393
Merge branch 'develop' of https://github.com/linode/manager into refa…
carrillo-erik Jul 20, 2023
b986a80
Merge branch 'develop' of https://github.com/linode/manager into refa…
carrillo-erik Jul 20, 2023
d12b709
Merge branch 'develop' of https://github.com/linode/manager into refa…
carrillo-erik Jul 21, 2023
9e5fd27
Implement new styling api and remove unused css
carrillo-erik Jul 21, 2023
9eee76d
Merge branch 'develop' of https://github.com/linode/manager into refa…
carrillo-erik Jul 25, 2023
2be31b9
Styled components implementation
carrillo-erik Jul 25, 2023
6657b0d
Merge branch 'develop' of https://github.com/linode/manager into refa…
carrillo-erik Jul 27, 2023
6ca4113
Convert class comp to funct comp and rewrite tests
carrillo-erik Jul 27, 2023
b95a172
Remove dead code from PR feedback
carrillo-erik Jul 27, 2023
3488eb7
Merge branch 'develop' of https://github.com/linode/manager into refa…
carrillo-erik Jul 30, 2023
34e1f00
Fix error with search results
carrillo-erik Jul 30, 2023
0ba31b2
Merge branch 'develop' of https://github.com/linode/manager into refa…
carrillo-erik Aug 1, 2023
6cec536
Debounce searchAlgolia func and minor clean up
carrillo-erik Aug 1, 2023
c45fa17
Merge branch 'develop' of https://github.com/linode/manager into refa…
carrillo-erik Aug 1, 2023
9c44b3e
Merge branch 'develop' of https://github.com/linode/manager into refa…
carrillo-erik Aug 1, 2023
6ed75f9
Replace useMemo with useCallback
carrillo-erik Aug 1, 2023
8923bf5
Remove old styles in favor of using sx prop
carrillo-erik Aug 1, 2023
9c089e3
Replace old styles in favor of sx prop
carrillo-erik Aug 1, 2023
2a3c9bf
Merge branch 'develop' of https://github.com/linode/manager into refa…
carrillo-erik Aug 2, 2023
67703a9
Remove comments and default export
carrillo-erik Aug 2, 2023
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
'@linode/manager': Tech Stories
---

MUI v5 migration Src > Features > Help ([#9408](https://github.com/linode/manager/pull/9408))
11 changes: 5 additions & 6 deletions packages/manager/src/components/PrimaryNav/PrimaryNav.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -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 () => {
Expand All @@ -29,7 +30,7 @@ describe('PrimaryNav', () => {
queryByTestId,
rerender,
} = renderWithTheme(<PrimaryNav {...props} />, { queryClient });
expect(queryByTestId('menu-item-Managed')).not.toBeInTheDocument();
expect(queryByTestId(queryString)).not.toBeInTheDocument();

server.use(
rest.get('*/account/maintenance', (req, res, ctx) => {
Expand All @@ -39,18 +40,16 @@ describe('PrimaryNav', () => {

rerender(wrapWithTheme(<PrimaryNav {...props} />, { queryClient }));

await findByTestId('menu-item-Managed');
await findByTestId(queryString);

getByTestId('menu-item-Managed');
getByTestId(queryString);
});

it('should have aria-current attribute for accessible links', () => {
const { getByTestId } = renderWithTheme(<PrimaryNav {...props} />, {
queryClient,
});

expect(getByTestId('menu-item-Managed').getAttribute('aria-current')).toBe(
'false'
);
expect(getByTestId(queryString).getAttribute('aria-current')).toBe('false');
});
});
Original file line number Diff line number Diff line change
Expand Up @@ -248,6 +248,7 @@ export const PrimaryNav = (props: Props) => {
},
],
],
// eslint-disable-next-line react-hooks/exhaustive-deps
[
showDatabases,
_isManagedAccount,
Expand Down
4 changes: 2 additions & 2 deletions packages/manager/src/features/Help/HelpLanding.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -6,14 +6,14 @@ import { HelpLanding } from './HelpLanding';
describe('Help Landing', () => {
const component = shallow(<HelpLanding />);
xit('should render search panel', () => {
expect(component.find('WithStyles(SearchPanel)')).toHaveLength(1);
expect(component.find('SearchPanel')).toHaveLength(1);
});

it('should render popular posts panel', () => {
expect(component.find('PopularPosts')).toHaveLength(1);
});

it('should render other ways panel', () => {
expect(component.find('WithStyles(OtherWays)')).toHaveLength(1);
expect(component.find('OtherWays')).toHaveLength(1);
});
});
4 changes: 2 additions & 2 deletions packages/manager/src/features/Help/HelpLanding.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -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 = () => {
Expand Down
256 changes: 107 additions & 149 deletions packages/manager/src/features/Help/Panels/AlgoliaSearchBar.tsx
Original file line number Diff line number Diff line change
@@ -1,195 +1,153 @@
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';
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<ClassNames> &
WithTheme &
RouteComponentProps<{}>;
class AlgoliaSearchBar extends React.Component<CombinedProps, State> {
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 (
<React.Fragment>
{searchError && (
<Notice className={classes.notice} error spacingTop={8}>
{searchError}
</Notice>
)}
<div className={classes.root}>
<Search className={classes.searchIcon} data-qa-search-icon />
<EnhancedSelect
components={
{ DropdownIndicator: () => 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}
/>
</div>
</React.Fragment>
);
}
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<string>) => {
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<string>) => {
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 (
<React.Fragment>
{searchError && (
<Notice className={classes.notice} error spacingTop={8}>
{searchError}
</Notice>
)}
<div className={classes.root}>
<Search className={classes.searchIcon} data-qa-search-icon />
<EnhancedSelect
components={
{ DropdownIndicator: () => 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}
/>
</div>
</React.Fragment>
);
};

const styled = withStyles(styles, { withTheme: true });
const search = withSearch({ highlight: true, hitsPerPage: 10 });

export default compose<CombinedProps, {}>(
styled,
search,
withRouter
)(AlgoliaSearchBar);
export default compose<CombinedProps, {}>(search, withRouter)(AlgoliaSearchBar);
Loading