settingsNavigation.tsx 3.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120
  1. import {cloneElement, Component} from 'react';
  2. import styled from '@emotion/styled';
  3. import * as Sentry from '@sentry/react';
  4. import {SecondaryNav} from 'sentry/components/nav/secondary';
  5. import {space} from 'sentry/styles/space';
  6. import SettingsNavigationGroup from 'sentry/views/settings/components/settingsNavigationGroup';
  7. import SettingsNavigationGroupDeprecated from 'sentry/views/settings/components/settingsNavigationGroupDeprecated';
  8. import type {NavigationProps, NavigationSection} from 'sentry/views/settings/types';
  9. type DefaultProps = {
  10. /**
  11. * Additional navigation configuration driven by hooks
  12. */
  13. hookConfigs: NavigationSection[];
  14. /**
  15. * Additional navigation elements driven from hooks
  16. */
  17. hooks: React.ReactElement[];
  18. /**
  19. * How far from the top of the page should the navigation be when stickied.
  20. */
  21. stickyTop: string;
  22. };
  23. type Props = DefaultProps &
  24. NavigationProps & {
  25. /**
  26. * The configuration for this navigation panel
  27. */
  28. navigationObjects: NavigationSection[];
  29. };
  30. function SettingsSecondaryNavigation({
  31. navigationObjects,
  32. hookConfigs,
  33. hooks,
  34. ...otherProps
  35. }: Props) {
  36. const navWithHooks = navigationObjects.concat(hookConfigs);
  37. return (
  38. <SecondaryNav>
  39. <SecondaryNav.Body>
  40. {navWithHooks.map(config => (
  41. <SettingsNavigationGroup key={config.name} {...otherProps} {...config} />
  42. ))}
  43. {hooks.map((Hook, i) => cloneElement(Hook, {key: `hook-${i}`}))}
  44. </SecondaryNav.Body>
  45. </SecondaryNav>
  46. );
  47. }
  48. class SettingsNavigation extends Component<Props> {
  49. static defaultProps: DefaultProps = {
  50. hooks: [],
  51. hookConfigs: [],
  52. stickyTop: '69px',
  53. };
  54. componentDidCatch(error: Error, errorInfo: React.ErrorInfo) {
  55. Sentry.withScope(scope => {
  56. Object.keys(errorInfo).forEach(key => {
  57. // @ts-expect-error TS(7053): Element implicitly has an 'any' type because expre... Remove this comment to see the full error message
  58. scope.setExtra(key, errorInfo[key]);
  59. });
  60. scope.setExtra('url', window.location.href);
  61. Sentry.captureException(error);
  62. });
  63. }
  64. render() {
  65. const {navigationObjects, hooks, hookConfigs, stickyTop, ...otherProps} = this.props;
  66. const navWithHooks = navigationObjects.concat(hookConfigs);
  67. if (this.props.organization?.features.includes('navigation-sidebar-v2')) {
  68. return (
  69. <SettingsSecondaryNavigation
  70. navigationObjects={navigationObjects}
  71. hooks={hooks}
  72. hookConfigs={hookConfigs}
  73. stickyTop={stickyTop}
  74. {...otherProps}
  75. />
  76. );
  77. }
  78. return (
  79. <PositionStickyWrapper stickyTop={stickyTop}>
  80. {navWithHooks.map(config => (
  81. <SettingsNavigationGroupDeprecated
  82. key={config.name}
  83. {...otherProps}
  84. {...config}
  85. />
  86. ))}
  87. {hooks.map((Hook, i) => cloneElement(Hook, {key: `hook-${i}`}))}
  88. </PositionStickyWrapper>
  89. );
  90. }
  91. }
  92. const PositionStickyWrapper = styled('div')<{stickyTop: string}>`
  93. padding: ${space(4)};
  94. padding-right: ${space(2)};
  95. @media (min-width: ${p => p.theme.breakpoints.small}) {
  96. position: sticky;
  97. top: ${p => p.stickyTop};
  98. overflow: scroll;
  99. -ms-overflow-style: none;
  100. scrollbar-width: none;
  101. &::-webkit-scrollbar {
  102. display: none;
  103. }
  104. }
  105. `;
  106. export default SettingsNavigation;