fieldControl.tsx 3.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117
  1. import * as React from 'react';
  2. import styled from '@emotion/styled';
  3. import FieldControlState from 'sentry/components/forms/field/fieldControlState';
  4. import QuestionTooltip from 'sentry/components/questionTooltip';
  5. import space from 'sentry/styles/space';
  6. const defaultProps = {
  7. flexibleControlStateSize: false,
  8. };
  9. type Props = Partial<typeof defaultProps> & {
  10. children: React.ReactNode;
  11. /**
  12. * Align the control towards the right
  13. */
  14. alignRight?: boolean;
  15. /**
  16. * Loading / Saving / Error states of the form. See the ControlState
  17. */
  18. controlState?: React.ReactNode;
  19. /**
  20. * Disable the field
  21. */
  22. disabled?: boolean;
  23. /**
  24. * Produces a question tooltip on the field, explaining why it is disabled
  25. */
  26. disabledReason?: React.ReactNode;
  27. /**
  28. * The error state. Will not be rendered if hideControlState is true
  29. */
  30. errorState?: React.ReactNode;
  31. /**
  32. * Allow the control state to flex based on its content. When enabled the
  33. * control state element will NOT take up space unless it has some state to
  34. * show (such as an error).
  35. */
  36. flexibleControlStateSize?: boolean;
  37. /**
  38. * Hide the fields control state
  39. */
  40. hideControlState?: boolean;
  41. /**
  42. * Display the field control container in "inline" fashion. The label and
  43. * description will be aligned to the left, while the control itself will be
  44. * aligned to the right.
  45. */
  46. inline?: boolean;
  47. };
  48. const FieldControl = ({
  49. inline,
  50. alignRight,
  51. disabled,
  52. disabledReason,
  53. errorState,
  54. controlState,
  55. children,
  56. hideControlState,
  57. flexibleControlStateSize = false,
  58. }: Props) => (
  59. <FieldControlErrorWrapper inline={inline}>
  60. <FieldControlWrapper>
  61. <FieldControlStyled alignRight={alignRight}>{children}</FieldControlStyled>
  62. {disabled && disabledReason && (
  63. <DisabledIndicator className="disabled-indicator">
  64. <StyledQuestionTooltip title={disabledReason} size="sm" position="top" />
  65. </DisabledIndicator>
  66. )}
  67. {!hideControlState && (
  68. <FieldControlState flexibleControlStateSize={!!flexibleControlStateSize}>
  69. {controlState}
  70. </FieldControlState>
  71. )}
  72. </FieldControlWrapper>
  73. {!hideControlState && errorState}
  74. </FieldControlErrorWrapper>
  75. );
  76. export default FieldControl;
  77. // This wraps Control + ControlError message
  78. // * can NOT be a flex box here because of `position: absolute` on "control error message"
  79. // * can NOT have overflow hidden because "control error message" overflows
  80. const FieldControlErrorWrapper = styled('div')<{inline?: boolean}>`
  81. ${p => (p.inline ? 'width: 50%; padding-left: 10px;' : '')};
  82. position: relative;
  83. `;
  84. const FieldControlStyled = styled('div')<{alignRight?: boolean}>`
  85. display: flex;
  86. flex: 1;
  87. flex-direction: column;
  88. position: relative;
  89. max-width: 100%;
  90. ${p => (p.alignRight ? 'align-items: flex-end;' : '')};
  91. `;
  92. const FieldControlWrapper = styled('div')`
  93. display: flex;
  94. flex-shrink: 0;
  95. `;
  96. const StyledQuestionTooltip = styled(QuestionTooltip)`
  97. display: block;
  98. margin: 0 auto;
  99. `;
  100. const DisabledIndicator = styled('div')`
  101. display: flex;
  102. align-items: center;
  103. margin-left: ${space(1)};
  104. `;