DataLink.tsx 4.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157
  1. import { css } from '@emotion/css';
  2. import React, { Dispatch, SetStateAction, useEffect, useState } from 'react';
  3. import { usePrevious } from 'react-use';
  4. import { VariableSuggestion } from '@grafana/data';
  5. import { DataSourcePicker } from '@grafana/runtime';
  6. import { Button, LegacyForms, DataLinkInput, stylesFactory } from '@grafana/ui';
  7. import { DataLinkConfig } from '../types';
  8. const { FormField, Switch } = LegacyForms;
  9. const getStyles = stylesFactory(() => ({
  10. firstRow: css`
  11. display: flex;
  12. `,
  13. nameField: css`
  14. flex: 2;
  15. `,
  16. regexField: css`
  17. flex: 3;
  18. `,
  19. row: css`
  20. display: flex;
  21. align-items: baseline;
  22. `,
  23. urlField: css`
  24. flex: 1;
  25. `,
  26. urlDisplayLabelField: css`
  27. flex: 1;
  28. `,
  29. }));
  30. type Props = {
  31. value: DataLinkConfig;
  32. onChange: (value: DataLinkConfig) => void;
  33. onDelete: () => void;
  34. suggestions: VariableSuggestion[];
  35. className?: string;
  36. };
  37. export const DataLink = (props: Props) => {
  38. const { value, onChange, onDelete, suggestions, className } = props;
  39. const styles = getStyles();
  40. const [showInternalLink, setShowInternalLink] = useInternalLink(value.datasourceUid);
  41. const handleChange = (field: keyof typeof value) => (event: React.ChangeEvent<HTMLInputElement>) => {
  42. onChange({
  43. ...value,
  44. [field]: event.currentTarget.value,
  45. });
  46. };
  47. return (
  48. <div className={className}>
  49. <div className={styles.firstRow + ' gf-form'}>
  50. <FormField
  51. className={styles.nameField}
  52. labelWidth={6}
  53. // A bit of a hack to prevent using default value for the width from FormField
  54. inputWidth={null}
  55. label="Field"
  56. type="text"
  57. value={value.field}
  58. tooltip={'Can be exact field name or a regex pattern that will match on the field name.'}
  59. onChange={handleChange('field')}
  60. />
  61. <Button
  62. variant={'destructive'}
  63. title="Remove field"
  64. icon="times"
  65. onClick={(event) => {
  66. event.preventDefault();
  67. onDelete();
  68. }}
  69. />
  70. </div>
  71. <div className="gf-form">
  72. <FormField
  73. label={showInternalLink ? 'Query' : 'URL'}
  74. labelWidth={6}
  75. inputEl={
  76. <DataLinkInput
  77. placeholder={showInternalLink ? '${__value.raw}' : 'http://example.com/${__value.raw}'}
  78. value={value.url || ''}
  79. onChange={(newValue) =>
  80. onChange({
  81. ...value,
  82. url: newValue,
  83. })
  84. }
  85. suggestions={suggestions}
  86. />
  87. }
  88. className={styles.urlField}
  89. />
  90. <FormField
  91. className={styles.urlDisplayLabelField}
  92. inputWidth={null}
  93. label="URL Label"
  94. type="text"
  95. value={value.urlDisplayLabel}
  96. onChange={handleChange('urlDisplayLabel')}
  97. tooltip={'Use to override the button label.'}
  98. />
  99. </div>
  100. <div className={styles.row}>
  101. <Switch
  102. labelClass={'width-6'}
  103. label="Internal link"
  104. checked={showInternalLink}
  105. onChange={() => {
  106. if (showInternalLink) {
  107. onChange({
  108. ...value,
  109. datasourceUid: undefined,
  110. });
  111. }
  112. setShowInternalLink(!showInternalLink);
  113. }}
  114. />
  115. {showInternalLink && (
  116. <DataSourcePicker
  117. tracing={true}
  118. // Uid and value should be always set in the db and so in the items.
  119. onChange={(ds) => {
  120. onChange({
  121. ...value,
  122. datasourceUid: ds.uid,
  123. });
  124. }}
  125. current={value.datasourceUid}
  126. />
  127. )}
  128. </div>
  129. </div>
  130. );
  131. };
  132. function useInternalLink(datasourceUid?: string): [boolean, Dispatch<SetStateAction<boolean>>] {
  133. const [showInternalLink, setShowInternalLink] = useState<boolean>(!!datasourceUid);
  134. const previousUid = usePrevious(datasourceUid);
  135. // Force internal link visibility change if uid changed outside of this component.
  136. useEffect(() => {
  137. if (!previousUid && datasourceUid && !showInternalLink) {
  138. setShowInternalLink(true);
  139. }
  140. if (previousUid && !datasourceUid && showInternalLink) {
  141. setShowInternalLink(false);
  142. }
  143. }, [previousUid, datasourceUid, showInternalLink]);
  144. return [showInternalLink, setShowInternalLink];
  145. }