fieldControl.tsx 3.0 KB

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