labelWithPowerIcon.tsx 3.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122
  1. import {ClassNames} from '@emotion/react';
  2. import styled from '@emotion/styled';
  3. import Feature from 'sentry/components/acl/feature';
  4. import {IconBusiness} from 'sentry/icons';
  5. import {space} from 'sentry/styles/space';
  6. import PowerFeatureHovercard from 'getsentry/components/powerFeatureHovercard';
  7. import withSubscription from 'getsentry/components/withSubscription';
  8. import type {Subscription} from 'getsentry/types';
  9. import {isEnterprise} from 'getsentry/utils/billing';
  10. const SSO = 'sso';
  11. const RELAY = 'relay';
  12. const ALLOCATIONS = 'allocations-upsell';
  13. const TEAM_ROLES = 'team-roles-upsell';
  14. const TABLE_ITEMS = [TEAM_ROLES];
  15. /**
  16. * This maps the IDs of the labels over to a configuration determining how the
  17. * tooltip should be represented.
  18. *
  19. * {
  20. * # partial indicates that this item does not strictly require a the
  21. * # features to function, but will work in some way *better* with the
  22. * # features. Thus "partial" support.
  23. * partial: false,
  24. *
  25. * # The list of plan features required for this functionality.
  26. * features: [...]
  27. * }
  28. */
  29. const POWER_FEATURE_CONFIG = [
  30. {
  31. id: SSO,
  32. features: ['sso-saml2'],
  33. partial: true,
  34. },
  35. {
  36. id: RELAY,
  37. features: ['relay'],
  38. partial: false,
  39. },
  40. {
  41. id: ALLOCATIONS,
  42. features: ['spend-allocations'],
  43. partial: false,
  44. },
  45. {
  46. id: TEAM_ROLES,
  47. features: ['team-roles'],
  48. partial: false,
  49. },
  50. ];
  51. type Props = {
  52. children: React.ReactNode;
  53. subscription: Subscription;
  54. id?: string;
  55. };
  56. function LabelWithPowerIcon({children, id, subscription}: Props) {
  57. // Must return React Elements
  58. // @see https://github.com/DefinitelyTyped/DefinitelyTyped/issues/20544
  59. if (id === undefined) {
  60. return children as React.ReactElement;
  61. }
  62. const config = POWER_FEATURE_CONFIG.find(c => c.id === id)!;
  63. if (config === undefined) {
  64. return children as React.ReactElement;
  65. }
  66. if (!subscription || isEnterprise(subscription)) {
  67. return children as React.ReactElement;
  68. }
  69. const {partial, features} = config;
  70. const leftAligned = TABLE_ITEMS.includes(id);
  71. const renderDisabled = () => (
  72. <StyledLabel leftAligned={leftAligned}>
  73. {children}
  74. <ClassNames>
  75. {({css}) => (
  76. <PowerFeatureHovercard
  77. {...{id, partial, features}}
  78. containerClassName={css`
  79. display: flex;
  80. align-items: center;
  81. `}
  82. containerDisplayMode="inline-flex"
  83. >
  84. <IconBusiness data-test-id="power-icon" />
  85. </PowerFeatureHovercard>
  86. )}
  87. </ClassNames>
  88. </StyledLabel>
  89. );
  90. return (
  91. <Feature features={features} renderDisabled={renderDisabled}>
  92. {children}
  93. </Feature>
  94. );
  95. }
  96. const StyledLabel = styled('div')<{leftAligned?: boolean}>`
  97. display: flex;
  98. align-items: center;
  99. justify-content: ${p => (p.leftAligned ? 'flex-start' : 'space-between')};
  100. width: 100%;
  101. > :last-child {
  102. margin-left: ${space(1)};
  103. }
  104. `;
  105. export default withSubscription(LabelWithPowerIcon, {noLoader: true});