console: unify webhook handler UX for action, remote schemas and events
[GS-397]: https://hasurahq.atlassian.net/browse/GS-397?atlOrigin=eyJpIjoiNWRkNTljNzYxNjVmNDY3MDlhMDU5Y2ZhYzA5YTRkZjUiLCJwIjoiZ2l0aHViLWNvbS1KU1cifQ PR-URL: https://github.com/hasura/graphql-engine-mono/pull/7796 Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com> Co-authored-by: Sean Park-Ross <94021366+seanparkross@users.noreply.github.com> GitOrigin-RevId: 620aea50e7d1b45835a5996246f46017b2ba5904
@ -78,24 +78,24 @@ X-Hasura-Role: admin
|
|||||||
|
|
||||||
### Args syntax {#metadata-pg-create-event-trigger-syntax}
|
### Args syntax {#metadata-pg-create-event-trigger-syntax}
|
||||||
|
|
||||||
| Key | Required | Schema | Description |
|
| Key | Required | Schema | Description |
|
||||||
| ---------------------- | -------- | -------------------------------------------------------------------------------------------------------------------------------------- | --------------------------------------------------------------------------------- |
|
|------------------------|----------|----------------------------------------------------------------------------------------------------------------------------------------|------------------------------------------------------------------------------------------------------------------------|
|
||||||
| name | true | [TriggerName](/api-reference/syntax-defs.mdx#triggername) | Name of the Event Trigger |
|
| name | true | [TriggerName](/api-reference/syntax-defs.mdx#triggername) | Name of the Event Trigger |
|
||||||
| table | true | [QualifiedTable](/api-reference/syntax-defs.mdx#qualifiedtable) | Object with table name and schema |
|
| table | true | [QualifiedTable](/api-reference/syntax-defs.mdx#qualifiedtable) | Object with table name and schema |
|
||||||
| source | false | [SourceName](/api-reference/syntax-defs.mdx#sourcename) | Name of the source database of the table (default: `default`) |
|
| source | false | [SourceName](/api-reference/syntax-defs.mdx#sourcename) | Name of the source database of the table (default: `default`) |
|
||||||
| webhook | false | String | Full url of webhook (\*) |
|
| webhook | false | [WebhookURL](/api-reference/syntax-defs.mdx#webhookurl) | Event Trigger webhook URL |
|
||||||
| webhook_from_env | false | String | Environment variable name of webhook (must exist at boot time) (\*) |
|
| webhook_from_env | false | String | Environment variable name of webhook (Deprecated in favour of [WebhookURL](/api-reference/syntax-defs.mdx#webhookurl)) |
|
||||||
| insert | false | [OperationSpec](/api-reference/syntax-defs.mdx#operationspec) | Specification for insert operation |
|
| insert | false | [OperationSpec](/api-reference/syntax-defs.mdx#operationspec) | Specification for insert operation |
|
||||||
| update | false | [OperationSpec](/api-reference/syntax-defs.mdx#operationspec) | Specification for update operation |
|
| update | false | [OperationSpec](/api-reference/syntax-defs.mdx#operationspec) | Specification for update operation |
|
||||||
| delete | false | [OperationSpec](/api-reference/syntax-defs.mdx#operationspec) | Specification for delete operation |
|
| delete | false | [OperationSpec](/api-reference/syntax-defs.mdx#operationspec) | Specification for delete operation |
|
||||||
| headers | false | [ [HeaderFromValue](/api-reference/syntax-defs.mdx#headerfromvalue) \| [HeaderFromEnv](/api-reference/syntax-defs.mdx#headerfromenv) ] | List of headers to be sent with the webhook |
|
| headers | false | [ [HeaderFromValue](/api-reference/syntax-defs.mdx#headerfromvalue) \| [HeaderFromEnv](/api-reference/syntax-defs.mdx#headerfromenv) ] | List of headers to be sent with the webhook |
|
||||||
| retry_conf | false | [RetryConf](/api-reference/syntax-defs.mdx#retryconf) | Retry configuration if event delivery fails |
|
| retry_conf | false | [RetryConf](/api-reference/syntax-defs.mdx#retryconf) | Retry configuration if event delivery fails |
|
||||||
| replace | false | Boolean | If set to true, the Event Trigger is replaced with the new definition |
|
| replace | false | Boolean | If set to true, the Event Trigger is replaced with the new definition |
|
||||||
| enable_manual | false | Boolean | If set to true, the Event Trigger can be invoked manually |
|
| enable_manual | false | Boolean | If set to true, the Event Trigger can be invoked manually |
|
||||||
| request_transform | false | [RequestTransformation](/api-reference/syntax-defs.mdx#requesttransformation) | Attaches a Request Transformation to the Event Trigger. |
|
| request_transform | false | [RequestTransformation](/api-reference/syntax-defs.mdx#requesttransformation) | Attaches a Request Transformation to the Event Trigger. |
|
||||||
| response_transform | false | [ResponseTransformation](/api-reference/syntax-defs.mdx#responsetransformation) | Attaches a Request Transformation to the Event Trigger. |
|
| response_transform | false | [ResponseTransformation](/api-reference/syntax-defs.mdx#responsetransformation) | Attaches a Request Transformation to the Event Trigger. |
|
||||||
| cleanup_config | false | [AutoEventTriggerCleanupConfig](/api-reference/syntax-defs.mdx#autoeventtriggercleanupconfig) | Cleanup config for the auto cleanup process (EE/Cloud only). |
|
| cleanup_config | false | [AutoEventTriggerCleanupConfig](/api-reference/syntax-defs.mdx#autoeventtriggercleanupconfig) | Cleanup config for the auto cleanup process (EE/Cloud only). |
|
||||||
| trigger_on_replication | false | Boolean | Specification for enabling/disabling the Event Trigger during logical replication |
|
| trigger_on_replication | false | Boolean | Specification for enabling/disabling the Event Trigger during logical replication |
|
||||||
|
|
||||||
(\*) Either `webhook` or `webhook_from_env` are required.
|
(\*) Either `webhook` or `webhook_from_env` are required.
|
||||||
|
|
||||||
|
Before Width: | Height: | Size: 15 KiB After Width: | Height: | Size: 454 KiB |
Before Width: | Height: | Size: 52 KiB After Width: | Height: | Size: 290 KiB |
BIN
docs/static/img/event-triggers/one-off.png
vendored
Before Width: | Height: | Size: 51 KiB After Width: | Height: | Size: 300 KiB |
Before Width: | Height: | Size: 47 KiB After Width: | Height: | Size: 139 KiB |
@ -5,21 +5,8 @@ import {
|
|||||||
} from '../Common/stateDefaults';
|
} from '../Common/stateDefaults';
|
||||||
import { DefaultState } from './types';
|
import { DefaultState } from './types';
|
||||||
|
|
||||||
let defaultHandler = '';
|
|
||||||
if (typeof navigator !== 'undefined') {
|
|
||||||
const { appVersion } = navigator;
|
|
||||||
const isLinux =
|
|
||||||
appVersion.toLowerCase().includes('linux') ||
|
|
||||||
appVersion.toLowerCase().includes('x11');
|
|
||||||
if (isLinux) {
|
|
||||||
defaultHandler = 'http://localhost:3000';
|
|
||||||
} else {
|
|
||||||
defaultHandler = 'http://host.docker.internal:3000';
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
const state: DefaultState = {
|
const state: DefaultState = {
|
||||||
handler: defaultHandler,
|
handler: '',
|
||||||
actionDefinition: {
|
actionDefinition: {
|
||||||
sdl: defaultActionDefSdl,
|
sdl: defaultActionDefSdl,
|
||||||
error: null,
|
error: null,
|
||||||
|
@ -2,6 +2,9 @@ import React, { useEffect, useState } from 'react';
|
|||||||
import { useDebouncedEffect } from '@/hooks/useDebounceEffect';
|
import { useDebouncedEffect } from '@/hooks/useDebounceEffect';
|
||||||
import { Analytics, REDACT_EVERYTHING } from '@/features/Analytics';
|
import { Analytics, REDACT_EVERYTHING } from '@/features/Analytics';
|
||||||
import { inputStyles } from '../../constants';
|
import { inputStyles } from '../../constants';
|
||||||
|
import { FaShieldAlt } from 'react-icons/fa';
|
||||||
|
import { IconTooltip } from '@/new-components/Tooltip';
|
||||||
|
import { LearnMoreLink } from '@/new-components/LearnMoreLink';
|
||||||
|
|
||||||
const editorLabel = 'Webhook (HTTP/S) Handler';
|
const editorLabel = 'Webhook (HTTP/S) Handler';
|
||||||
|
|
||||||
@ -32,26 +35,30 @@ const HandlerEditor: React.FC<HandlerEditorProps> = ({
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<Analytics name="ActionEditor" {...REDACT_EVERYTHING}>
|
<Analytics name="ActionEditor" {...REDACT_EVERYTHING}>
|
||||||
<div className="mb-lg w-4/12">
|
<div className="mb-lg w-6/12">
|
||||||
<h2 className="text-lg font-semibold mb-xs flex items-center">
|
<h2 className="text-lg font-semibold mb-xs flex items-center">
|
||||||
{editorLabel}
|
{editorLabel}
|
||||||
<span className="text-red-700 ml-xs mr-sm">*</span>
|
<span className="text-red-700 ml-xs">*</span>
|
||||||
|
<IconTooltip
|
||||||
|
message="Environment variables and secrets are available using the {{VARIABLE}} tag. Environment variable templating is available for this field. Example: https://{{ENV_VAR}}/endpoint_url"
|
||||||
|
icon={<FaShieldAlt className="h-4 text-muted cursor-pointer" />}
|
||||||
|
/>
|
||||||
|
<LearnMoreLink href="https://hasura.io/docs/latest/api-reference/syntax-defs/#webhookurl" />
|
||||||
</h2>
|
</h2>
|
||||||
|
<p className="text-sm text-gray-600 mb-sm">
|
||||||
|
Note: Provide an URL or use an env var to template the handler URL if
|
||||||
|
you have different URLs for multiple environments.
|
||||||
|
</p>
|
||||||
<input
|
<input
|
||||||
disabled={disabled}
|
disabled={disabled}
|
||||||
type="text"
|
type="text"
|
||||||
name="handler"
|
name="handler"
|
||||||
value={localValue}
|
value={localValue}
|
||||||
onChange={e => setLocalValue(e.target.value)}
|
onChange={e => setLocalValue(e.target.value)}
|
||||||
placeholder="http://custom-logic.com/api"
|
placeholder="http://custom-logic.com/api or {{ACTION_BASE_URL}}/handler"
|
||||||
className={inputStyles}
|
className={inputStyles}
|
||||||
data-test="action-create-handler-input"
|
data-test="action-create-handler-input"
|
||||||
/>
|
/>
|
||||||
<p className="text-sm text-gray-600">
|
|
||||||
Note: You can use an env var to template the handler URL if you have
|
|
||||||
different URLs for multiple environments.
|
|
||||||
<br /> e.g. {'{{ACTION_BASE_URL}}/handler'}
|
|
||||||
</p>
|
|
||||||
</div>
|
</div>
|
||||||
</Analytics>
|
</Analytics>
|
||||||
);
|
);
|
||||||
|
@ -61,6 +61,7 @@ import {
|
|||||||
RetryConf,
|
RetryConf,
|
||||||
EventTriggerAutoCleanup,
|
EventTriggerAutoCleanup,
|
||||||
} from '../../types';
|
} from '../../types';
|
||||||
|
import { useDebouncedEffect } from '@/hooks/useDebounceEffect';
|
||||||
|
|
||||||
interface Props extends InjectedProps {}
|
interface Props extends InjectedProps {}
|
||||||
|
|
||||||
@ -229,49 +230,53 @@ const Add: React.FC<Props> = props => {
|
|||||||
transformState.sessionVars,
|
transformState.sessionVars,
|
||||||
]);
|
]);
|
||||||
|
|
||||||
useEffect(() => {
|
useDebouncedEffect(
|
||||||
requestBodyErrorOnChange('');
|
() => {
|
||||||
requestTransformedBodyOnChange('');
|
requestBodyErrorOnChange('');
|
||||||
const onResponse = (data: Record<string, any>) => {
|
requestTransformedBodyOnChange('');
|
||||||
parseValidateApiData(
|
const onResponse = (data: Record<string, any>) => {
|
||||||
data,
|
parseValidateApiData(
|
||||||
requestBodyErrorOnChange,
|
data,
|
||||||
undefined,
|
requestBodyErrorOnChange,
|
||||||
requestTransformedBodyOnChange
|
|
||||||
);
|
|
||||||
};
|
|
||||||
const options = getValidateTransformOptions({
|
|
||||||
version: transformState.version,
|
|
||||||
inputPayloadString: transformState.requestSampleInput,
|
|
||||||
webhookUrl: webhook.value,
|
|
||||||
envVarsFromContext: transformState.envVars,
|
|
||||||
sessionVarsFromContext: transformState.sessionVars,
|
|
||||||
transformerBody: transformState.requestBody,
|
|
||||||
isEnvVar: webhook.type === 'env',
|
|
||||||
});
|
|
||||||
if (!webhook.value) {
|
|
||||||
requestBodyErrorOnChange(
|
|
||||||
'Please configure your webhook handler to generate request body transform'
|
|
||||||
);
|
|
||||||
} else if (transformState.requestBody && webhook.value) {
|
|
||||||
dispatch(
|
|
||||||
requestAction(
|
|
||||||
Endpoints.metadata,
|
|
||||||
options,
|
|
||||||
undefined,
|
undefined,
|
||||||
undefined,
|
requestTransformedBodyOnChange
|
||||||
true,
|
);
|
||||||
true
|
};
|
||||||
)
|
const options = getValidateTransformOptions({
|
||||||
).then(onResponse, onResponse); // parseValidateApiData will parse both success and error
|
version: transformState.version,
|
||||||
}
|
inputPayloadString: transformState.requestSampleInput,
|
||||||
}, [
|
webhookUrl: webhook.value,
|
||||||
transformState.requestSampleInput,
|
envVarsFromContext: transformState.envVars,
|
||||||
transformState.requestBody,
|
sessionVarsFromContext: transformState.sessionVars,
|
||||||
webhook,
|
transformerBody: transformState.requestBody,
|
||||||
transformState.envVars,
|
isEnvVar: webhook.type === 'env',
|
||||||
transformState.sessionVars,
|
});
|
||||||
]);
|
if (!webhook.value) {
|
||||||
|
requestBodyErrorOnChange(
|
||||||
|
'Please configure your webhook handler to generate request body transform'
|
||||||
|
);
|
||||||
|
} else if (transformState.requestBody && webhook.value) {
|
||||||
|
dispatch(
|
||||||
|
requestAction(
|
||||||
|
Endpoints.metadata,
|
||||||
|
options,
|
||||||
|
undefined,
|
||||||
|
undefined,
|
||||||
|
true,
|
||||||
|
true
|
||||||
|
)
|
||||||
|
).then(onResponse, onResponse); // parseValidateApiData will parse both success and error
|
||||||
|
}
|
||||||
|
},
|
||||||
|
1000,
|
||||||
|
[
|
||||||
|
transformState.requestSampleInput,
|
||||||
|
transformState.requestBody,
|
||||||
|
webhook,
|
||||||
|
transformState.envVars,
|
||||||
|
transformState.sessionVars,
|
||||||
|
]
|
||||||
|
);
|
||||||
|
|
||||||
const createBtnText = 'Create Event Trigger';
|
const createBtnText = 'Create Event Trigger';
|
||||||
|
|
||||||
|
@ -18,9 +18,9 @@ import {
|
|||||||
} from '../../types';
|
} from '../../types';
|
||||||
import ColumnList from '../Common/ColumnList';
|
import ColumnList from '../Common/ColumnList';
|
||||||
import FormLabel from './FormLabel';
|
import FormLabel from './FormLabel';
|
||||||
import DebouncedDropdownInput from '../Common/DropdownWrapper';
|
|
||||||
import { inputStyles, heading } from '../../constants';
|
import { inputStyles, heading } from '../../constants';
|
||||||
import { AutoCleanupForm } from '../Common/AutoCleanupForm';
|
import { AutoCleanupForm } from '../Common/AutoCleanupForm';
|
||||||
|
import { FaShieldAlt } from 'react-icons/fa';
|
||||||
|
|
||||||
type CreateETFormProps = {
|
type CreateETFormProps = {
|
||||||
state: LocalEventTriggerState;
|
state: LocalEventTriggerState;
|
||||||
@ -62,7 +62,6 @@ const CreateETForm: React.FC<CreateETFormProps> = props => {
|
|||||||
handleDatabaseChange,
|
handleDatabaseChange,
|
||||||
handleSchemaChange,
|
handleSchemaChange,
|
||||||
handleTableChange,
|
handleTableChange,
|
||||||
handleWebhookTypeChange,
|
|
||||||
handleWebhookValueChange,
|
handleWebhookValueChange,
|
||||||
handleOperationsChange,
|
handleOperationsChange,
|
||||||
handleOperationsColumnsChange,
|
handleOperationsColumnsChange,
|
||||||
@ -166,36 +165,31 @@ const CreateETForm: React.FC<CreateETFormProps> = props => {
|
|||||||
<FormLabel
|
<FormLabel
|
||||||
title="Webhook (HTTP/S) Handler"
|
title="Webhook (HTTP/S) Handler"
|
||||||
tooltip={tooltip.webhookUrlDescription}
|
tooltip={tooltip.webhookUrlDescription}
|
||||||
|
tooltipIcon={
|
||||||
|
<FaShieldAlt className="h-4 text-muted cursor-pointer" />
|
||||||
|
}
|
||||||
|
knowMoreLink="https://hasura.io/docs/latest/api-reference/syntax-defs/#webhookurl"
|
||||||
/>
|
/>
|
||||||
<div>
|
<div>
|
||||||
<div className="w-72">
|
<div className="w-1/2">
|
||||||
<DebouncedDropdownInput
|
<p className="text-sm text-gray-600 mb-sm">
|
||||||
dropdownOptions={[
|
Note: Provide an URL or use an env var to template the handler URL
|
||||||
{ display_text: 'URL', value: 'static' },
|
if you have different URLs for multiple environments.
|
||||||
{ display_text: 'From env var', value: 'env' },
|
</p>
|
||||||
]}
|
<input
|
||||||
title={webhook.type === 'static' ? 'URL' : 'From env var'}
|
type="text"
|
||||||
dataKey={webhook.type === 'static' ? 'static' : 'env'}
|
name="handler"
|
||||||
onButtonChange={handleWebhookTypeChange}
|
onChange={e => handleWebhookValueChange(e.target.value)}
|
||||||
onHandlerValChange={handleWebhookValueChange}
|
|
||||||
required
|
required
|
||||||
bsClass="w-72"
|
value={webhook.value}
|
||||||
handlerVal={webhook.value}
|
|
||||||
id="webhook-url"
|
id="webhook-url"
|
||||||
inputPlaceHolder={
|
placeholder="http://httpbin.org/post or {{MY_WEBHOOK_URL}}/handler"
|
||||||
webhook.type === 'static'
|
data-test="webhook"
|
||||||
? 'http://httpbin.org/post'
|
className={`w-82 ${inputStyles}`}
|
||||||
: 'MY_WEBHOOK_URL'
|
|
||||||
}
|
|
||||||
testId="webhook"
|
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<br />
|
<br />
|
||||||
<small>
|
|
||||||
Note: Specifying the webhook URL via an environmental variable is
|
|
||||||
recommended if you have different URLs for multiple environments.
|
|
||||||
</small>
|
|
||||||
</div>
|
</div>
|
||||||
<hr className="my-md" />
|
<hr className="my-md" />
|
||||||
{isProConsole(window.__env) && (
|
{isProConsole(window.__env) && (
|
||||||
|
@ -1,17 +1,26 @@
|
|||||||
import React from 'react';
|
import React from 'react';
|
||||||
import { IconTooltip } from '@/new-components/Tooltip';
|
import { IconTooltip } from '@/new-components/Tooltip';
|
||||||
|
import { LearnMoreLink } from '@/new-components/LearnMoreLink';
|
||||||
|
|
||||||
type FormLabelProps = {
|
type FormLabelProps = {
|
||||||
title: string;
|
title: string;
|
||||||
tooltip: string;
|
tooltip: string;
|
||||||
|
tooltipIcon?: React.ReactElement;
|
||||||
|
knowMoreLink?: string;
|
||||||
};
|
};
|
||||||
|
|
||||||
const FormLabel: React.FC<FormLabelProps> = ({ title, tooltip }) => {
|
const FormLabel: React.FC<FormLabelProps> = ({
|
||||||
|
title,
|
||||||
|
tooltip,
|
||||||
|
tooltipIcon,
|
||||||
|
knowMoreLink,
|
||||||
|
}) => {
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<h2 className="text-lg font-semibold mb-xs flex items-center">
|
<h2 className="text-lg font-semibold mb-xs flex items-center">
|
||||||
{title}
|
{title}
|
||||||
<IconTooltip message={tooltip} />
|
<IconTooltip message={tooltip} icon={tooltipIcon} />
|
||||||
|
{knowMoreLink ? <LearnMoreLink href={knowMoreLink} /> : null}
|
||||||
</h2>
|
</h2>
|
||||||
</>
|
</>
|
||||||
);
|
);
|
||||||
|
@ -12,7 +12,7 @@ export const manualOperationsDescription =
|
|||||||
'Trigger manually from table data browser in console';
|
'Trigger manually from table data browser in console';
|
||||||
|
|
||||||
export const webhookUrlDescription =
|
export const webhookUrlDescription =
|
||||||
'POST endpoint which will be triggered with payload on configured events';
|
'Environment variables and secrets are available using the {{VARIABLE}} tag. Environment variable templating is available for this field. Example: https://{{ENV_VAR}}/endpoint_url';
|
||||||
|
|
||||||
export const advancedOperationDescription =
|
export const advancedOperationDescription =
|
||||||
'For update triggers, webhook will be triggered only when selected columns are modified';
|
'For update triggers, webhook will be triggered only when selected columns are modified';
|
||||||
|
@ -65,6 +65,7 @@ import {
|
|||||||
getEventTriggerByName,
|
getEventTriggerByName,
|
||||||
} from '../../../../../metadata/selector';
|
} from '../../../../../metadata/selector';
|
||||||
import { AutoCleanupForm } from '../Common/AutoCleanupForm';
|
import { AutoCleanupForm } from '../Common/AutoCleanupForm';
|
||||||
|
import { useDebouncedEffect } from '@/hooks/useDebounceEffect';
|
||||||
|
|
||||||
interface Props extends InjectedProps {}
|
interface Props extends InjectedProps {}
|
||||||
|
|
||||||
@ -291,25 +292,29 @@ const Modify: React.FC<Props> = props => {
|
|||||||
transformState.sessionVars,
|
transformState.sessionVars,
|
||||||
]);
|
]);
|
||||||
|
|
||||||
useEffect(() => {
|
useDebouncedEffect(
|
||||||
if (
|
() => {
|
||||||
transformState.requestBody &&
|
if (
|
||||||
state.webhook?.value &&
|
transformState.requestBody &&
|
||||||
!transformState.requestTransformedBody
|
state.webhook?.value &&
|
||||||
) {
|
!transformState.requestTransformedBody
|
||||||
requestBodyErrorOnChange('');
|
) {
|
||||||
dispatch(
|
requestBodyErrorOnChange('');
|
||||||
requestAction(
|
dispatch(
|
||||||
Endpoints.metadata,
|
requestAction(
|
||||||
reqBodyoptions,
|
Endpoints.metadata,
|
||||||
undefined,
|
reqBodyoptions,
|
||||||
undefined,
|
undefined,
|
||||||
true,
|
undefined,
|
||||||
true
|
true,
|
||||||
)
|
true
|
||||||
).then(onRequestBodyResponse, onRequestBodyResponse);
|
)
|
||||||
}
|
).then(onRequestBodyResponse, onRequestBodyResponse);
|
||||||
}, [transformState.requestTransformedBody]);
|
}
|
||||||
|
},
|
||||||
|
1000,
|
||||||
|
[transformState.requestTransformedBody]
|
||||||
|
);
|
||||||
|
|
||||||
const saveWrapper =
|
const saveWrapper =
|
||||||
(property?: EventTriggerProperty) =>
|
(property?: EventTriggerProperty) =>
|
||||||
|
@ -1,8 +1,10 @@
|
|||||||
import React from 'react';
|
import React from 'react';
|
||||||
|
import { FaShieldAlt } from 'react-icons/fa';
|
||||||
import Editor from '../../../../Common/Layout/ExpandableEditor/Editor';
|
import Editor from '../../../../Common/Layout/ExpandableEditor/Editor';
|
||||||
import { inputStyles } from '../../constants';
|
import { inputStyles } from '../../constants';
|
||||||
import { EventTrigger, URLConf, VoidCallback } from '../../types';
|
import { EventTrigger, URLConf, VoidCallback } from '../../types';
|
||||||
import { parseServerWebhook } from '../../utils';
|
import { parseServerWebhook } from '../../utils';
|
||||||
|
import FormLabel from '../Add/FormLabel';
|
||||||
import DebouncedDropdownInput from '../Common/DropdownWrapper';
|
import DebouncedDropdownInput from '../Common/DropdownWrapper';
|
||||||
|
|
||||||
type WebhookEditorProps = {
|
type WebhookEditorProps = {
|
||||||
@ -14,7 +16,6 @@ type WebhookEditorProps = {
|
|||||||
|
|
||||||
const WebhookEditor = (props: WebhookEditorProps) => {
|
const WebhookEditor = (props: WebhookEditorProps) => {
|
||||||
const { currentTrigger, webhook, setWebhook, save } = props;
|
const { currentTrigger, webhook, setWebhook, save } = props;
|
||||||
|
|
||||||
const existingWebhook = parseServerWebhook(
|
const existingWebhook = parseServerWebhook(
|
||||||
currentTrigger.configuration.webhook,
|
currentTrigger.configuration.webhook,
|
||||||
currentTrigger.configuration.webhook_from_env
|
currentTrigger.configuration.webhook_from_env
|
||||||
@ -50,37 +51,62 @@ const WebhookEditor = (props: WebhookEditorProps) => {
|
|||||||
);
|
);
|
||||||
|
|
||||||
const expanded = () => (
|
const expanded = () => (
|
||||||
<div className="pb-sm pt-sm max-w-80">
|
<div className="w-1/2">
|
||||||
<DebouncedDropdownInput
|
<p className="text-sm text-gray-600 mb-sm">
|
||||||
dropdownOptions={[
|
Note: Provide an URL or use an env var to template the handler URL if
|
||||||
{ display_text: 'URL', value: 'static' },
|
you have different URLs for multiple environments.
|
||||||
{ display_text: 'From env var', value: 'env' },
|
</p>
|
||||||
]}
|
{existingWebhook?.type === 'env' ? (
|
||||||
title={webhook.type === 'env' ? 'From env var' : 'URL'}
|
<DebouncedDropdownInput
|
||||||
dataKey={webhook.type === 'env' ? 'env' : 'static'}
|
dropdownOptions={[
|
||||||
onButtonChange={handleWebhookTypeChange}
|
{ display_text: 'URL', value: 'static' },
|
||||||
onHandlerValChange={handleWebhookValueChange}
|
{ display_text: 'From env var', value: 'env' },
|
||||||
required
|
]}
|
||||||
bsClass={`${inputStyles} w-72`}
|
title={webhook.type === 'env' ? 'From env var' : 'URL'}
|
||||||
handlerVal={webhook.value}
|
dataKey={webhook.type === 'env' ? 'env' : 'static'}
|
||||||
id="webhook-url"
|
onButtonChange={handleWebhookTypeChange}
|
||||||
inputPlaceHolder={
|
onHandlerValChange={handleWebhookValueChange}
|
||||||
webhook.type === 'env' ? 'MY_WEBHOOK_URL' : 'http://httpbin.org/post'
|
required
|
||||||
}
|
bsClass={`w-82`}
|
||||||
testId="webhook"
|
handlerVal={webhook.value}
|
||||||
/>
|
id="webhook-url"
|
||||||
|
inputPlaceHolder={
|
||||||
|
webhook.type === 'env'
|
||||||
|
? 'MY_WEBHOOK_URL'
|
||||||
|
: 'http://httpbin.org/post'
|
||||||
|
}
|
||||||
|
testId="webhook"
|
||||||
|
/>
|
||||||
|
) : (
|
||||||
|
<input
|
||||||
|
type="text"
|
||||||
|
name="handler"
|
||||||
|
onChange={e => handleWebhookValueChange(e.target.value)}
|
||||||
|
required
|
||||||
|
className={`${inputStyles} w-82`}
|
||||||
|
value={
|
||||||
|
webhook.type === 'static' ? webhook.value : `{{${webhook.value}}}`
|
||||||
|
}
|
||||||
|
id="webhook-url"
|
||||||
|
placeholder="http://httpbin.org/post or {{MY_WEBHOOK_URL}}/handler"
|
||||||
|
data-test="webhook"
|
||||||
|
/>
|
||||||
|
)}
|
||||||
<br />
|
<br />
|
||||||
<small>
|
|
||||||
Note: Specifying the webhook URL via an environmental variable is
|
|
||||||
recommended if you have different URLs for multiple environments.
|
|
||||||
</small>
|
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="w-full border-b border-solid border-gray-300 mb-md">
|
<div className="w-full border-b border-solid border-gray-300 mb-md">
|
||||||
<div className="mb-md">
|
<div className="mb-md">
|
||||||
<h4 className="text-lg font-bold mb-md">Webhook (HTTP/S) Handler</h4>
|
<FormLabel
|
||||||
|
title="Webhook (HTTP/S) Handler"
|
||||||
|
tooltip="Environment variables and secrets are available using the {{VARIABLE}} tag. Environment variable templating is available for this field. Example: https://{{ENV_VAR}}/endpoint_url"
|
||||||
|
tooltipIcon={
|
||||||
|
<FaShieldAlt className="h-4 text-muted cursor-pointer" />
|
||||||
|
}
|
||||||
|
knowMoreLink="https://hasura.io/docs/latest/api-reference/syntax-defs/#webhookurl"
|
||||||
|
/>
|
||||||
<Editor
|
<Editor
|
||||||
editorCollapsed={collapsed}
|
editorCollapsed={collapsed}
|
||||||
editorExpanded={expanded}
|
editorExpanded={expanded}
|
||||||
|
@ -12,6 +12,8 @@ import CommonHeader from '../../../Common/Layout/ReusableHeader/Header';
|
|||||||
import GraphQLCustomizationEdit from './GraphQLCustomization/GraphQLCustomizationEdit';
|
import GraphQLCustomizationEdit from './GraphQLCustomization/GraphQLCustomizationEdit';
|
||||||
|
|
||||||
import { focusYellowRing, inputStyles, subHeading } from '../constants';
|
import { focusYellowRing, inputStyles, subHeading } from '../constants';
|
||||||
|
import { FaShieldAlt } from 'react-icons/fa';
|
||||||
|
import { LearnMoreLink } from '@/new-components/LearnMoreLink';
|
||||||
|
|
||||||
class Common extends React.Component {
|
class Common extends React.Component {
|
||||||
getPlaceHolderText(valType) {
|
getPlaceHolderText(valType) {
|
||||||
@ -48,6 +50,7 @@ class Common extends React.Component {
|
|||||||
forwardClientHeaders,
|
forwardClientHeaders,
|
||||||
comment,
|
comment,
|
||||||
customization,
|
customization,
|
||||||
|
isEnvVarEnabled,
|
||||||
} = this.props;
|
} = this.props;
|
||||||
|
|
||||||
const { isModify } = this.props.editState;
|
const { isModify } = this.props.editState;
|
||||||
@ -58,8 +61,9 @@ class Common extends React.Component {
|
|||||||
const tooltips = {
|
const tooltips = {
|
||||||
graphqlurl: (
|
graphqlurl: (
|
||||||
<IconTooltip
|
<IconTooltip
|
||||||
message="Remote GraphQL server’s URL. E.g. https://my-domain/v1/graphql"
|
message="Environment variables and secrets are available using the {{VARIABLE}} tag. Environment variable templating is available for this field. Example: https://{{ENV_VAR}}/endpoint_url"
|
||||||
side="right"
|
side="right"
|
||||||
|
icon={<FaShieldAlt className="h-4 text-muted cursor-pointer" />}
|
||||||
/>
|
/>
|
||||||
),
|
),
|
||||||
clientHeaderForward: (
|
clientHeaderForward: (
|
||||||
@ -142,42 +146,59 @@ class Common extends React.Component {
|
|||||||
<hr className="my-md" />
|
<hr className="my-md" />
|
||||||
<div className={`${subHeading} flex items-center`}>
|
<div className={`${subHeading} flex items-center`}>
|
||||||
GraphQL server URL *{tooltips.graphqlurl}
|
GraphQL server URL *{tooltips.graphqlurl}
|
||||||
|
<LearnMoreLink href="https://hasura.io/docs/latest/api-reference/syntax-defs/#webhookurl" />
|
||||||
</div>
|
</div>
|
||||||
<div className={'w-80'}>
|
<p className="text-sm text-gray-600 mb-sm w-1/2">
|
||||||
<DropdownButton
|
Note: Provide an URL or use an env var to template the handler URL if
|
||||||
dropdownOptions={[
|
you have different URLs for multiple environments.
|
||||||
{ display_text: 'URL', value: 'manualUrl' },
|
</p>
|
||||||
{ display_text: 'From env var', value: 'envName' },
|
<div className={'w-1/2'}>
|
||||||
]}
|
{isEnvVarEnabled ? (
|
||||||
title={
|
<DropdownButton
|
||||||
(manualUrl !== null && 'URL') ||
|
dropdownOptions={[
|
||||||
(envName !== null && 'From env var') ||
|
{ display_text: 'URL', value: 'manualUrl' },
|
||||||
'Value'
|
{ display_text: 'From env var', value: 'envName' },
|
||||||
}
|
]}
|
||||||
dataKey={
|
title={
|
||||||
(manualUrl !== null && 'manualUrl') ||
|
(manualUrl !== null && 'URL') ||
|
||||||
(envName !== null && 'envName')
|
(envName !== null && 'From env var') ||
|
||||||
}
|
'Value'
|
||||||
onButtonChange={this.toggleUrlParam.bind(this)}
|
}
|
||||||
onInputChange={this.handleInputChange.bind(this)}
|
dataKey={
|
||||||
required={urlRequired}
|
(manualUrl !== null && 'manualUrl') ||
|
||||||
bsClass="w-80"
|
(envName !== null && 'envName')
|
||||||
inputVal={manualUrl || envName}
|
}
|
||||||
disabled={isDisabled}
|
onButtonChange={this.toggleUrlParam.bind(this)}
|
||||||
id="graphql-server-url"
|
onInputChange={this.handleInputChange.bind(this)}
|
||||||
inputPlaceHolder={
|
required={urlRequired}
|
||||||
(manualUrl !== null &&
|
bsClass="w-80"
|
||||||
'https://my-graphql-service.com/graphql') ||
|
inputVal={envName || manualUrl}
|
||||||
(envName !== null && 'MY_GRAPHQL_ENDPOINT')
|
disabled={isDisabled}
|
||||||
}
|
id="graphql-server-url"
|
||||||
testId="remote-schema-graphql-url"
|
inputPlaceHolder={
|
||||||
/>
|
(manualUrl !== null &&
|
||||||
|
'https://my-graphql-service.com/graphql') ||
|
||||||
|
(envName !== null && 'MY_GRAPHQL_ENDPOINT')
|
||||||
|
}
|
||||||
|
testId="remote-schema-graphql-url"
|
||||||
|
/>
|
||||||
|
) : (
|
||||||
|
<input
|
||||||
|
type="text"
|
||||||
|
name="handler"
|
||||||
|
onChange={this.handleInputChange.bind(this)}
|
||||||
|
required={urlRequired}
|
||||||
|
className={`w-3/4 ${inputStyles}`}
|
||||||
|
data-key={'manualUrl'}
|
||||||
|
value={manualUrl}
|
||||||
|
disabled={isDisabled}
|
||||||
|
id="graphql-server-url"
|
||||||
|
placeholder="https://my-graphql-service.com/graphql or {{MY_WEBHOOK_URL}}/graphql"
|
||||||
|
data-test="remote-schema-graphql-url"
|
||||||
|
/>
|
||||||
|
)}
|
||||||
</div>
|
</div>
|
||||||
<br />
|
<br />
|
||||||
<small>
|
|
||||||
Note: Specifying the server URL via an environmental variable is
|
|
||||||
recommended if you have different URLs for multiple environments.
|
|
||||||
</small>
|
|
||||||
<div className={`${subHeading} pt-md`}>
|
<div className={`${subHeading} pt-md`}>
|
||||||
Headers for the remote GraphQL server
|
Headers for the remote GraphQL server
|
||||||
</div>
|
</div>
|
||||||
|
@ -199,6 +199,12 @@ class Edit extends React.Component {
|
|||||||
{ redactText: true }
|
{ redactText: true }
|
||||||
);
|
);
|
||||||
|
|
||||||
|
const isEnvVarEnabled = () => {
|
||||||
|
return this.props.allRemoteSchemas.some(
|
||||||
|
rs => rs.name === remoteSchemaName && rs.definition.url_from_env
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div>
|
<div>
|
||||||
<Helmet>
|
<Helmet>
|
||||||
@ -227,7 +233,7 @@ class Edit extends React.Component {
|
|||||||
this.editClicked();
|
this.editClicked();
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<Common {...this.props} />
|
<Common {...this.props} isEnvVarEnabled={isEnvVarEnabled()} />
|
||||||
{generateMigrateBtns()}
|
{generateMigrateBtns()}
|
||||||
</form>
|
</form>
|
||||||
</Analytics>
|
</Analytics>
|
||||||
|
@ -11,6 +11,7 @@ import {
|
|||||||
ScheduleEventPayloadInput,
|
ScheduleEventPayloadInput,
|
||||||
} from './components';
|
} from './components';
|
||||||
import { ScheduledTime } from './components/ScheduledTime';
|
import { ScheduledTime } from './components/ScheduledTime';
|
||||||
|
import { FaShieldAlt } from 'react-icons/fa';
|
||||||
|
|
||||||
type Props = {
|
type Props = {
|
||||||
/**
|
/**
|
||||||
@ -51,10 +52,15 @@ const OneOffScheduledEventForm = (props: Props) => {
|
|||||||
</div>
|
</div>
|
||||||
<div className="mb-md">
|
<div className="mb-md">
|
||||||
<InputField
|
<InputField
|
||||||
|
learnMoreLink="https://hasura.io/docs/latest/api-reference/syntax-defs/#webhookurl"
|
||||||
|
tooltipIcon={
|
||||||
|
<FaShieldAlt className="h-4 text-muted cursor-pointer" />
|
||||||
|
}
|
||||||
name="webhook"
|
name="webhook"
|
||||||
label="Webhook URL"
|
label="Webhook URL"
|
||||||
placeholder="https://httpbin.com/post"
|
placeholder="https://httpbin.com/post or {{MY_WEBHOOK_URL}}/handler"
|
||||||
tooltip="The HTTP URL that should be triggered."
|
tooltip="Environment variables and secrets are available using the {{VARIABLE}} tag. Environment variable templating is available for this field. Example: https://{{ENV_VAR}}/endpoint_url"
|
||||||
|
description="Note: Provide an URL or use an env var to template the handler URL if you have different URLs for multiple environments."
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
<div className="mb-md">
|
<div className="mb-md">
|
||||||
|
@ -19,6 +19,7 @@ import {
|
|||||||
} from './utils';
|
} from './utils';
|
||||||
import { useCronMetadataMigration, useDefaultValues } from './hooks';
|
import { useCronMetadataMigration, useDefaultValues } from './hooks';
|
||||||
import { CronRequestTransformation } from './components/CronRequestTransformation';
|
import { CronRequestTransformation } from './components/CronRequestTransformation';
|
||||||
|
import { FaQuestionCircle, FaShieldAlt } from 'react-icons/fa';
|
||||||
|
|
||||||
type Props = {
|
type Props = {
|
||||||
/**
|
/**
|
||||||
@ -63,10 +64,15 @@ const FormContent = (props: FormContentProps) => {
|
|||||||
<hr className="my-md" />
|
<hr className="my-md" />
|
||||||
<div className="mb-xs w-1/2">
|
<div className="mb-xs w-1/2">
|
||||||
<InputField
|
<InputField
|
||||||
|
learnMoreLink="https://hasura.io/docs/latest/api-reference/syntax-defs/#webhookurl"
|
||||||
|
tooltipIcon={
|
||||||
|
<FaShieldAlt className="h-4 text-muted cursor-pointer" />
|
||||||
|
}
|
||||||
name="webhook"
|
name="webhook"
|
||||||
label="Webhook URL"
|
label="Webhook URL"
|
||||||
placeholder="https://httpbin.com/post"
|
placeholder="https://httpbin.com/post or {{MY_WEBHOOK_URL}}/handler"
|
||||||
tooltip="The HTTP URL that should be triggered. You can also provide the URL from environment variables, e.g. {{MY_WEBHOOK_URL}}"
|
tooltip="Environment variables and secrets are available using the {{VARIABLE}} tag. Environment variable templating is available for this field. Example: https://{{ENV_VAR}}/endpoint_url"
|
||||||
|
description="Note: Provide an URL or use an env var to template the handler URL if you have different URLs for multiple environments."
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
<div className="mb-xs w-1/2">
|
<div className="mb-xs w-1/2">
|
||||||
|
@ -16,7 +16,9 @@ export const CronScheduleSelector = () => {
|
|||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<div className="block flex items-center text-gray-600 font-semibold">
|
<div className="block flex items-center text-gray-600 font-semibold">
|
||||||
<label htmlFor="schedule">Cron Schedule</label>
|
<label htmlFor="schedule" className="font-semibold">
|
||||||
|
Cron Schedule
|
||||||
|
</label>
|
||||||
<IconTooltip message="Schedule for your cron (events are created based on the UTC timezone)" />
|
<IconTooltip message="Schedule for your cron (events are created based on the UTC timezone)" />
|
||||||
<LearnMoreLink
|
<LearnMoreLink
|
||||||
href="https://crontab.guru/#*_*_*_*_*"
|
href="https://crontab.guru/#*_*_*_*_*"
|
||||||
|
@ -1,6 +1,8 @@
|
|||||||
import React from 'react';
|
import React from 'react';
|
||||||
import { IconTooltip } from '@/new-components/Tooltip';
|
import { IconTooltip } from '@/new-components/Tooltip';
|
||||||
import { useFormContext } from 'react-hook-form';
|
import { useFormContext } from 'react-hook-form';
|
||||||
|
import { FaShieldAlt } from 'react-icons/fa';
|
||||||
|
import { LearnMoreLink } from '@/new-components/LearnMoreLink';
|
||||||
|
|
||||||
export const GraphQLServiceUrl = () => {
|
export const GraphQLServiceUrl = () => {
|
||||||
const { watch, register } = useFormContext();
|
const { watch, register } = useFormContext();
|
||||||
@ -9,28 +11,21 @@ export const GraphQLServiceUrl = () => {
|
|||||||
<div className="mb-md w-6/12">
|
<div className="mb-md w-6/12">
|
||||||
<label className="block flex items-center text-gray-600 font-semibold mb-xs">
|
<label className="block flex items-center text-gray-600 font-semibold mb-xs">
|
||||||
GraphQL Service URL
|
GraphQL Service URL
|
||||||
<IconTooltip message="Remote GraphQL server’s URL. E.g. https://my-domain/v1/graphql" />
|
<IconTooltip
|
||||||
|
message="Environment variables and secrets are available using the {{VARIABLE}} tag. Environment variable templating is available for this field. Example: https://{{ENV_VAR}}/endpoint_url"
|
||||||
|
icon={<FaShieldAlt className="h-4 text-muted cursor-pointer" />}
|
||||||
|
/>
|
||||||
|
<LearnMoreLink href="https://hasura.io/docs/latest/api-reference/syntax-defs/#webhookurl" />
|
||||||
</label>
|
</label>
|
||||||
<p className="text-sm text-gray-600 mb-sm">
|
<p className="text-sm text-gray-600 mb-sm">
|
||||||
Note: Specifying the server URL via an environmental variable is
|
Note: Provide an URL or use an env var to template the handler URL if
|
||||||
recommended if you have different URLs for multiple environments.
|
you have different URLs for multiple environments.
|
||||||
</p>
|
</p>
|
||||||
<div className="flex shadow-sm rounded">
|
<div className="flex shadow-sm rounded">
|
||||||
<select
|
|
||||||
className="inline-flex rounded-l border border-r-0 border-gray-300 bg-white hover:border-gray-400 focus:ring-2 focus:ring-yellow-200 focus:border-yellow-400"
|
|
||||||
{...register('url.type')}
|
|
||||||
>
|
|
||||||
<option value="from_url">URL</option>
|
|
||||||
<option value="from_env">Env Var</option>
|
|
||||||
</select>
|
|
||||||
<input
|
<input
|
||||||
type="text"
|
type="text"
|
||||||
className="flex-1 min-w-0 block w-full px-3 py-2 rounded-r border-gray-300 hover:border-gray-400 focus:ring-2 focus:ring-yellow-200 focus:border-yellow-400"
|
className="flex-1 min-w-0 block w-full px-3 py-2 rounded-r border-gray-300 hover:border-gray-400 focus:ring-2 focus:ring-yellow-200 focus:border-yellow-400"
|
||||||
placeholder={
|
placeholder="https://myservice.com/graphql or {{MY_WEBHOOK_URL}}/graphql"
|
||||||
url?.type === 'from_url'
|
|
||||||
? 'https://myservice.com/graphql'
|
|
||||||
: 'MY_GRAPHQL_ENDPOINT'
|
|
||||||
}
|
|
||||||
{...register('url.value')}
|
{...register('url.value')}
|
||||||
data-testid="url"
|
data-testid="url"
|
||||||
/>
|
/>
|
||||||
|
@ -39,6 +39,10 @@ export type FieldWrapperPassThroughProps = {
|
|||||||
* Render line breaks in the description
|
* Render line breaks in the description
|
||||||
*/
|
*/
|
||||||
renderDescriptionLineBreaks?: boolean;
|
renderDescriptionLineBreaks?: boolean;
|
||||||
|
/**
|
||||||
|
* tooltip icon other then ?
|
||||||
|
*/
|
||||||
|
tooltipIcon?: React.ReactElement;
|
||||||
} & DiscriminatedTypes<
|
} & DiscriminatedTypes<
|
||||||
{
|
{
|
||||||
/**
|
/**
|
||||||
@ -110,6 +114,7 @@ export const FieldWrapper = (props: FieldWrapperProps) => {
|
|||||||
className,
|
className,
|
||||||
size = 'full',
|
size = 'full',
|
||||||
error,
|
error,
|
||||||
|
tooltipIcon,
|
||||||
children,
|
children,
|
||||||
description,
|
description,
|
||||||
tooltip,
|
tooltip,
|
||||||
@ -158,7 +163,9 @@ export const FieldWrapper = (props: FieldWrapperProps) => {
|
|||||||
{label}
|
{label}
|
||||||
{loading ? <Skeleton className="absolute inset-0" /> : null}
|
{loading ? <Skeleton className="absolute inset-0" /> : null}
|
||||||
</span>
|
</span>
|
||||||
{!loading && tooltip ? <IconTooltip message={tooltip} /> : null}
|
{!loading && tooltip ? (
|
||||||
|
<IconTooltip message={tooltip} icon={tooltipIcon} />
|
||||||
|
) : null}
|
||||||
{!loading && !!learnMoreLink && (
|
{!loading && !!learnMoreLink && (
|
||||||
<LearnMoreLink href={learnMoreLink} />
|
<LearnMoreLink href={learnMoreLink} />
|
||||||
)}
|
)}
|
||||||
|
@ -18,7 +18,7 @@ export const LearnMoreLink: React.VFC<LearnMoreLinkProps> = props => {
|
|||||||
href={href}
|
href={href}
|
||||||
target="_blank"
|
target="_blank"
|
||||||
rel="noopener noreferrer"
|
rel="noopener noreferrer"
|
||||||
className={`ml-xs italic text-sm text-secondary ${className}`}
|
className={`ml-xs italic text-sm font-thin text-secondary ${className}`}
|
||||||
>
|
>
|
||||||
{text}
|
{text}
|
||||||
</a>
|
</a>
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
import React from 'react';
|
import React, { ReactElement, ReactNode } from 'react';
|
||||||
import { Tooltip, TooltipProps } from '@/new-components/Tooltip';
|
import { Tooltip, TooltipProps } from '@/new-components/Tooltip';
|
||||||
import { FaQuestionCircle } from 'react-icons/fa';
|
import { FaQuestionCircle } from 'react-icons/fa';
|
||||||
|
|
||||||
@ -11,6 +11,10 @@ export type IconTooltipProps = {
|
|||||||
* The tooltip icon classes
|
* The tooltip icon classes
|
||||||
*/
|
*/
|
||||||
className?: string;
|
className?: string;
|
||||||
|
/**
|
||||||
|
* tooltip icon other then ?
|
||||||
|
*/
|
||||||
|
icon?: ReactNode;
|
||||||
} & Pick<TooltipProps, 'side'> &
|
} & Pick<TooltipProps, 'side'> &
|
||||||
Pick<TooltipProps, 'defaultOpen'>;
|
Pick<TooltipProps, 'defaultOpen'>;
|
||||||
|
|
||||||
@ -19,6 +23,7 @@ export const IconTooltip: React.VFC<IconTooltipProps> = ({
|
|||||||
className,
|
className,
|
||||||
side = 'right',
|
side = 'right',
|
||||||
defaultOpen = false,
|
defaultOpen = false,
|
||||||
|
icon,
|
||||||
}) => (
|
}) => (
|
||||||
<Tooltip
|
<Tooltip
|
||||||
tooltipContentChildren={message}
|
tooltipContentChildren={message}
|
||||||
@ -26,8 +31,12 @@ export const IconTooltip: React.VFC<IconTooltipProps> = ({
|
|||||||
defaultOpen={defaultOpen}
|
defaultOpen={defaultOpen}
|
||||||
className="flex items-center"
|
className="flex items-center"
|
||||||
>
|
>
|
||||||
<FaQuestionCircle
|
{!icon ? (
|
||||||
className={`h-4 text-muted cursor-pointer ${className}`}
|
<FaQuestionCircle
|
||||||
/>
|
className={`h-4 text-muted cursor-pointer ${className}`}
|
||||||
|
/>
|
||||||
|
) : (
|
||||||
|
icon
|
||||||
|
)}
|
||||||
</Tooltip>
|
</Tooltip>
|
||||||
);
|
);
|
||||||
|