console: React bootstrap tooltip migration

PR-URL: https://github.com/hasura/graphql-engine-mono/pull/4959
GitOrigin-RevId: b8c8a620baeaa3228e92d058a917dbf3350d48ec
This commit is contained in:
Nicolas Inchauspe 2022-08-03 14:38:18 +02:00 committed by hasura-bot
parent b77005c320
commit a1fc55ec89
46 changed files with 647 additions and 580 deletions

View File

@ -12,6 +12,7 @@ export { fetchConsoleNotifications } from '../src/components/Main/Actions';
export { default as NotificationSection } from '../src/components/Main/NotificationSection';
export { default as Onboarding } from '../src/components/Common/Onboarding';
export { default as PageNotFound } from '../src/components/Error/PageNotFound';
export * from '../src/new-components/Tooltip/';
export { CONSOLE_ADMIN_SECRET } from '../src/components/AppState';
export { default as dataHeaders } from '../src/components/Services/Data/Common/Headers';
export { handleMigrationErrors } from '../src/components/Services/Data/TableModify/ModifyActions';

View File

@ -75,14 +75,10 @@ class CollapsibleToggle extends React.Component<
}}
open={isOpen}
>
<summary
className="cursor-pointer inline-block items-center"
data-test={testId}
>
<span className="inline-block text-xs mr-sm">
<summary className="cursor-pointer flex items-start" data-test={testId}>
<span className="inline-block text-xs mr-sm mt-0.5">
<FaChevronRight className={`${isOpen && 'rotate-90'}`} />
</span>
<span className="inline-block">{getTitle()}</span>
</summary>
<div className="mt-sm">{children}</div>

View File

@ -1,5 +1,5 @@
import React, { InputHTMLAttributes } from 'react';
import { ToolTip } from '@/new-components/Tooltip';
import { IconTooltip } from '@/new-components/Tooltip';
interface LabeledInputProps extends InputHTMLAttributes<HTMLInputElement> {
label: string;
@ -16,7 +16,7 @@ export const LabeledInput: React.FC<LabeledInputProps> = props => (
}`}
>
{props?.boldlabel ? <b>{props.label}</b> : props.label}
{props.tooltipText && <ToolTip message={props.tooltipText} />}
{props.tooltipText && <IconTooltip message={props.tooltipText} />}
</label>
<input
type={props?.type ?? 'text'}

View File

@ -1,11 +1,10 @@
import React from 'react';
import OverlayTrigger from 'react-bootstrap/lib/OverlayTrigger';
import Tooltip from 'react-bootstrap/lib/Tooltip';
import { Tooltip, TooltipProps } from '@/new-components/Tooltip';
type Props = {
children: React.ReactElement;
message: string;
placement?: string;
placement?: TooltipProps['side'];
};
const OverlayMessage: React.FC<Props> = ({
@ -14,12 +13,9 @@ const OverlayMessage: React.FC<Props> = ({
placement = 'left',
}) =>
message ? (
<OverlayTrigger
placement={placement}
overlay={<Tooltip id={message}>{message}</Tooltip>}
>
<Tooltip side={placement} tooltipContentChildren={message}>
{children}
</OverlayTrigger>
</Tooltip>
) : (
children
);

View File

@ -1,12 +1,8 @@
import React from 'react';
import { FaInfoCircle } from 'react-icons/fa';
import OverlayTrigger from 'react-bootstrap/lib/OverlayTrigger';
import Tooltip from 'react-bootstrap/lib/Tooltip';
import { Tooltip } from '@/new-components/Tooltip';
import styles from './Tooltip.scss';
const tooltipGen = (message: string) => {
return <Tooltip id={message}>{message}</Tooltip>;
};
export interface TooltipProps extends React.ComponentProps<'i'> {
message: string;
placement?: 'right' | 'left' | 'top' | 'bottom';
@ -20,14 +16,14 @@ const ToolTip: React.FC<TooltipProps> = ({
tooltipStyle = '',
children,
}) => (
<OverlayTrigger placement={placement} overlay={tooltipGen(message)}>
<Tooltip side={placement} tooltipContentChildren={message}>
{children || (
<FaInfoCircle
className={`cursor-pointer ${styles.tooltipIcon} ${tooltipStyle} text-sm`}
aria-hidden="true"
/>
)}
</OverlayTrigger>
</Tooltip>
);
export default ToolTip;

View File

@ -1,9 +1,7 @@
import React from 'react';
import Tooltip from 'react-bootstrap/lib/Tooltip';
import { Tooltip } from '@/new-components/Tooltip';
import { FaExclamationTriangle } from 'react-icons/fa';
import OverlayTrigger from 'react-bootstrap/lib/OverlayTrigger';
import styles from './WarningSymbol.scss';
export interface WarningSymbolProps {
@ -17,15 +15,13 @@ const WarningSymbol: React.FC<WarningSymbolProps> = ({
tooltipPlacement = 'right',
customStyle = '',
}) => {
const tooltip = <Tooltip id={tooltipText}>{tooltipText}</Tooltip>;
return (
<div className={styles.display_inline}>
<OverlayTrigger placement={tooltipPlacement} overlay={tooltip}>
<Tooltip tooltipContentChildren={tooltipText} side={tooltipPlacement}>
<span>
<WarningIcon customStyle={customStyle} />
</span>
</OverlayTrigger>
</Tooltip>
</div>
);
};

View File

@ -1,7 +1,6 @@
import React from 'react';
import OverlayTrigger from 'react-bootstrap/lib/OverlayTrigger';
import Tooltip from 'react-bootstrap/lib/Tooltip';
import { Tooltip } from '@/new-components/Tooltip';
import {
FaCloud,
FaCog,
@ -111,8 +110,8 @@ const getSidebarItem = (
const className = isCurrentBlockActive ? styles.navSideBarActive : '';
return (
<OverlayTrigger placement="right" overlay={itemTooltip}>
<li>
<li>
<Tooltip side="right" tooltipContentChildren={itemTooltip}>
<Link
className={className}
to={appPrefix + path}
@ -123,10 +122,10 @@ const getSidebarItem = (
'aria-hidden': true,
})}
</div>
<p>{title}</p>
<p className="uppercase">{title}</p>
</Link>
</li>
</OverlayTrigger>
</Tooltip>
</li>
);
};

View File

@ -2,7 +2,7 @@ import React, { useEffect, useReducer } from 'react';
import { GraphQLError } from 'graphql';
import { connect, ConnectedProps } from 'react-redux';
import Helmet from 'react-helmet';
import { Tooltip } from 'react-bootstrap';
import { IconTooltip } from '@/new-components/Tooltip';
import requestAction from '@/utils/requestAction';
import {
parseValidateApiData,
@ -382,9 +382,7 @@ const AddAction: React.FC<AddActionProps> = ({
Create Action
</Button>
{readOnlyMode && (
<Tooltip id="tooltip-actions-add-readonlymode">
Adding new action is not allowed in Read only mode!
</Tooltip>
<IconTooltip message="Adding new action is not allowed in Read only mode!" />
)}
</div>
</div>

View File

@ -1,6 +1,6 @@
import React from 'react';
import AceEditor from '../../../Common/AceEditor/BaseEditor';
import Tooltip from '../Common/components/Tooltip';
import { IconTooltip } from '@/new-components/Tooltip';
const DerivedFrom = ({ shouldDerive, parentMutation, toggleDerivation }) => {
if (!parentMutation) return null;
@ -9,17 +9,26 @@ const DerivedFrom = ({ shouldDerive, parentMutation, toggleDerivation }) => {
'This code is generated based on the assumption that operation was derived from another operation. If the assumption is wrong, you can disable the derivation.';
return (
<div>
<h2 className="text-sm font-bold mb-md pb-5 mt-0">
<h2 className="text-sm font-bold pb-5 mt-0">
Derived operation
<Tooltip id="action-name" text={tooltip} className="ml-5" />
<IconTooltip message={tooltip} />
</h2>
<div className="mb-5">
<label className="cursor-pointer" onClick={toggleDerivation}>
<input
type="checkbox"
checked={shouldDerive}
className="cursor-pointer mr-md legacy-input-fix"
/>
<input
id="derivedFromInputId"
type="checkbox"
checked={shouldDerive}
className="cursor-pointer mr-md legacy-input-fix"
// Force margin to mitigate bug introduced by a too much restrictive reset
style={{ margin: '0' }}
/>
<label
htmlFor="derivedFromInputId"
className="cursor-pointer"
onClick={toggleDerivation}
// Force margin to mitigate bug introduced by a too much restrictive reset
style={{ margin: '0', marginLeft: '0.5rem' }}
>
Generate code with delegation to the derived mutation
</label>
</div>

View File

@ -1,7 +1,6 @@
import React from 'react';
import { GraphQLError } from 'graphql';
import { Tooltip, OverlayTrigger } from 'react-bootstrap';
import { FaQuestionCircle } from 'react-icons/fa';
import { IconTooltip } from '@/new-components/Tooltip';
import HandlerEditor from './HandlerEditor';
import ExecutionEditor from './ExecutionEditor';
import HeaderConfEditor from './HeaderConfEditor';
@ -189,16 +188,7 @@ const ActionEditor: React.FC<ActionEditorProps> = ({
<div className="mb-lg w-8/12">
<h2 className="text-lg font-semibold mb-xs flex items-center">
Action custom timeout
<OverlayTrigger
placement="right"
overlay={
<Tooltip id="tooltip-cascade">
Configure timeout for Action. Defaults to 30 seconds.
</Tooltip>
}
>
<FaQuestionCircle className="ml-xs" aria-hidden="true" />
</OverlayTrigger>
<IconTooltip message="Configure timeout for Action. Defaults to 30 seconds." />
</h2>
<div className="mb-lg w-4/12">
<input

View File

@ -1,102 +0,0 @@
import React from 'react';
import Spinner from '../../../../Common/Spinner/Spinner';
import { connect } from 'react-redux';
import { isInputObjectType, isObjectType, isEnumType } from 'graphql';
import { deriveExistingType } from '../utils';
import Tooltip from './Tooltip';
import { useIntrospectionSchema } from '../../../../Common/utils/graphqlUtils';
import { inputStyles } from '../../constants';
const CloneType = ({ headers, toggleModal, handleClonedTypes, dispatch }) => {
const [prefix, setPrefix] = React.useState('_');
const prefixOnChange = e => setPrefix(e.target.value);
const { schema, loading, error, introspect } = useIntrospectionSchema(
headers,
dispatch
);
if (loading) return <Spinner />;
if (error) {
return (
<div>
Error introspecting schema.&nbsp;
<a onClick={introspect}>Try again</a>
</div>
);
}
const cloneableTypes = Object.keys(schema._typeMap)
.filter(t => {
if (t.startsWith('__')) return false;
return (
isInputObjectType(schema._typeMap[t]) ||
isObjectType(schema._typeMap[t]) ||
isEnumType(schema._typeMap[t])
);
})
.sort((t1, t2) => {
const _t1 = t1.toLowerCase();
const _t2 = t2.toLowerCase();
if (_t1 > _t2) return 1;
if (_t1 < _t2) return -1;
return 0;
});
const onSelection = e => {
const selectedType = e.target.value;
if (selectedType === '') return;
const newTypes = deriveExistingType(selectedType, schema._typeMap, prefix);
handleClonedTypes(newTypes);
toggleModal();
};
const dropdownTitle = prefix ? null : 'Please provide a prefix first.';
const prefixTooltipText =
'Prefix is required so that the type you are cloning does not collide with the existing type in Hasura.';
return (
<div>
<div className="flex-row mb-2.5 flex">
<div className={`w-1/4 ${inputStyles}`}>
Prefix <Tooltip text={prefixTooltipText} id="clone-prefix" />
</div>
<input
type="text"
value={prefix}
onChange={prefixOnChange}
className={`w-1/4 ${inputStyles}`}
/>
</div>
<div className="flex-row mb-2.5 flex">
<div className="col-md-3"> Type to clone</div>
<select
value=""
className={`w-1/4 input`}
onChange={onSelection}
disabled={prefix === ''}
title={dropdownTitle}
>
<option value="">---select an existing type---</option>
{cloneableTypes.map(t => {
return (
<option value={t} key={t}>
{t}
</option>
);
})}
</select>
</div>
</div>
);
};
const mapStateToprops = state => {
return {
headers: state.tables.dataHeaders,
};
};
export default connect(mapStateToprops)(CloneType);

View File

@ -1,7 +1,7 @@
import React from 'react';
import { parse as sdlParser } from 'graphql/language/parser';
import { GraphQLError } from 'graphql';
import Tooltip from './Tooltip';
import { IconTooltip } from '@/new-components/Tooltip';
import CrossIcon from '../../../../Common/Icons/Cross';
import AceEditor from '../../../../Common/AceEditor/BaseEditor';
import { Nullable } from '../../../../Common/utils/tsUtils';
@ -78,11 +78,7 @@ const GraphQLEditor: React.FC<GraphQLEditorProps> = ({
{label ? (
<h2 className="text-lg font-bold pb-5 mb-1.5">
{label}
{tooltip ? (
<Tooltip id="action-name" text={tooltip} className="ml-2.5" />
) : (
<></>
)}
{tooltip ? <IconTooltip message={tooltip} /> : <></>}
</h2>
) : null}
<div className="flex mb-1.5">

View File

@ -1,27 +0,0 @@
import React from 'react';
import { inputStyles } from '../../constants';
import Tooltip from './Tooltip';
const editorLabel = 'Name';
const editorTooltip =
'Set a name for your action. This will be a root field in your GraphQL schema';
const NameEditor = ({ value, onChange, className, placeholder }) => {
return (
<div className={className || ''}>
<h2 className="text-sm font-bold pb-5 mb-1.5">
{editorLabel}
<Tooltip id="action-name" text={editorTooltip} className="ml-2.5" />
</h2>
<input
type="text"
value={value}
onChange={onChange}
placeholder={placeholder}
className={`${inputStyles} w-52`}
/>
</div>
);
};
export default NameEditor;

View File

@ -1,15 +0,0 @@
import React from 'react';
import OverlayTrigger from 'react-bootstrap/lib/OverlayTrigger';
import TooltipElement from 'react-bootstrap/lib/Tooltip';
import { FaQuestionCircle } from 'react-icons/fa';
const Tooltip = ({ text, id, className }) => {
const tooltip = <TooltipElement id={id}>{text}</TooltipElement>;
return (
<OverlayTrigger placement="right" overlay={tooltip}>
<FaQuestionCircle className={className} aria-hidden="true" />
</OverlayTrigger>
);
};
export default Tooltip;

View File

@ -413,7 +413,7 @@ const ModifyAction: React.FC<ModifyProps> = ({
requestPayloadTransformOnChange={requestPayloadTransformOnChange}
/>
<div className="flex items-center mb-lg">
<div className="flex items-start mb-lg">
{!readOnlyMode && (
<>
<Button

View File

@ -1,6 +1,6 @@
import React, { ChangeEvent, Dispatch } from 'react';
import { FaCheckCircle, FaInfoCircle } from 'react-icons/fa';
import { ToolTip } from '@/new-components/Tooltip';
import { IconTooltip } from '@/new-components/Tooltip';
import { ConnectDBActions, ConnectDBState, connectionTypes } from './state';
import { LabeledInput } from '../../../Common/LabeledInput';
@ -186,7 +186,7 @@ const ConnectDatabaseForm: React.FC<ConnectDatabaseFormProps> = ({
className={`${styles.remove_pad_bottom} mb-md flex items-center gap-1 ${styles.connect_db_header}`}
>
{title ?? defaultTitle}
<ToolTip message="Environment variable recommended" />
<IconTooltip message="Environment variable recommended" />
<a
href="https://hasura.io/docs/latest/graphql/cloud/projects/create.html#existing-database"
target="_blank"
@ -308,7 +308,7 @@ const ConnectDatabaseForm: React.FC<ConnectDatabaseFormProps> = ({
className={`${styles.add_mar_bottom_mid} flex items-center gap-1`}
>
<b>Service Account Key:</b>
<ToolTip message="Service account key for BigQuery data source" />
<IconTooltip message="Service account key for BigQuery data source" />
</div>
<JSONEditor
minLines={5}
@ -442,7 +442,7 @@ const ConnectDatabaseForm: React.FC<ConnectDatabaseFormProps> = ({
connectionDBState={connectionDBState}
connectionDBStateDispatch={connectionDBStateDispatch}
/>
{/*
{/*
TODO: remove the edit state condition when the BE issue is solved
https://github.com/hasura/graphql-engine-mono/issues/4700
*/}

View File

@ -1,6 +1,6 @@
import React, { Dispatch, useState } from 'react';
import { Collapse } from '@/new-components/Collapse';
import { ToolTip } from '@/new-components/Tooltip';
import { IconTooltip } from '@/new-components/Tooltip';
import { FaCaretDown, FaCaretRight } from 'react-icons/fa';
@ -166,7 +166,7 @@ const ConnectionSettingsForm: React.FC<ConnectionSettingsFormProps> = ({
<div className={styles.connection_settings_input_layout}>
<label className="flex items-center gap-1">
<b>Isolation Level</b>
<ToolTip message="The transaction isolation level in which the queries made to the source will be run" />
<IconTooltip message="The transaction isolation level in which the queries made to the source will be run" />
</label>
<select
className={`form-control ${styles.connnection_settings_form_input} cursor-pointer`}
@ -204,7 +204,7 @@ const ConnectionSettingsForm: React.FC<ConnectionSettingsFormProps> = ({
/>{' '}
&nbsp;
<b>Use Prepared Statements</b>
<ToolTip message="Prepared statements are disabled by default" />
<IconTooltip message="Prepared statements are disabled by default" />
</label>
</div>
) : null}
@ -237,7 +237,7 @@ const ConnectionSettingsForm: React.FC<ConnectionSettingsFormProps> = ({
<div className="mb-xs">
<label className="flex items-center gap-1">
<b>SSL Mode</b>
<ToolTip message="SSL certificate verification mode" />
<IconTooltip message="SSL certificate verification mode" />
</label>
<select
className="form-control"

View File

@ -1,5 +1,5 @@
import { Collapse } from '@/new-components/Collapse';
import { ToolTip } from '@/new-components/Tooltip';
import { IconTooltip } from '@/new-components/Tooltip';
import React from 'react';
import { FormRow } from './FormRow';
import { NamingConvention } from './NamingConvention';
@ -32,7 +32,7 @@ export const GraphQLFieldCustomization = ({
<div>
<div className="flex items-center p-sm text-gray-600 font-semibold">
Root Fields
<ToolTip message="Set a namespace or add a prefix / suffix to the root fields for the database's objects in the GraphQL API" />
<IconTooltip message="Set a namespace or add a prefix / suffix to the root fields for the database's objects in the GraphQL API" />
</div>
</div>
<form
@ -56,7 +56,7 @@ export const GraphQLFieldCustomization = ({
<div>
<div className="flex items-center p-sm text-gray-600 font-semibold">
Type Names
<ToolTip message="Add a prefix / suffix to the types for the database's objects in the GraphQL API" />
<IconTooltip message="Add a prefix / suffix to the types for the database's objects in the GraphQL API" />
</div>
</div>
<form

View File

@ -1,6 +1,6 @@
import React from 'react';
import { NamingConventionOptions } from '@/metadata/types';
import { ToolTip } from '@/new-components/Tooltip';
import { IconTooltip } from '@/new-components/Tooltip';
import { useServerConfig } from '@/hooks';
import { getSupportedDrivers } from '@/dataSources';
import { CardRadioGroup } from '@/new-components/CardRadioGroup';
@ -68,7 +68,7 @@ export const NamingConvention: React.FC<GraphQLFieldCustomizationProps> = ({
<div className="p-sm box-border">
<p className="flex items-center text-gray-600 font-semibold">
Naming Convention
<ToolTip message="Choose a default naming convention for your auto-generated GraphQL schema objects (fields, types, arguments, etc.)" />
<IconTooltip message="Choose a default naming convention for your auto-generated GraphQL schema objects (fields, types, arguments, etc.)" />
<a
href="https://hasura.io/docs/latest/graphql/core/databases/postgres/schema/naming-convention/#set-naming-convention-for-a-particular-source"
target="_blank"

View File

@ -1,9 +1,8 @@
import PropTypes from 'prop-types';
import React, { Component } from 'react';
import JSONEditor from './JSONEditor';
import Tooltip from 'react-bootstrap/lib/Tooltip';
import { IconTooltip } from '@/new-components/Tooltip';
import InputGroup from 'react-bootstrap/lib/InputGroup';
import OverlayTrigger from 'react-bootstrap/es/OverlayTrigger';
import 'brace/mode/json';
import 'brace/theme/github';
@ -87,12 +86,7 @@ import PermButtonSection from './PermButtonsSection';
import { rolesSelector } from '../../../../metadata/selector';
import { RightContainer } from '../../../Common/Layout/RightContainer';
import FeatureDisabled from '../FeatureDisabled';
import {
FaInfoCircle,
FaPencilAlt,
FaQuestionCircle,
FaTimes,
} from 'react-icons/fa';
import { FaInfoCircle, FaPencilAlt, FaTimes } from 'react-icons/fa';
class Permissions extends Component {
constructor() {
@ -256,9 +250,7 @@ class Permissions extends Component {
return (
<span>
<span className={styles.add_mar_right_small}>{text}</span>
<OverlayTrigger placement="right" overlay={tooltip}>
<FaQuestionCircle aria-hidden="true" />
</OverlayTrigger>
{tooltip}
</span>
);
};
@ -789,18 +781,10 @@ class Permissions extends Component {
const isSelected =
permissionsState.custom_checked[filterType] || isUniqueFilter;
const customCheckToolTip = (
<Tooltip id="tooltip-custom-check">
Create custom check using permissions builder
</Tooltip>
);
const customChecklabel = (
<span data-test="custom-check">
<span className={styles.add_mar_right}>With custom check:</span>
<OverlayTrigger placement="right" overlay={customCheckToolTip}>
<FaQuestionCircle aria-hidden="true" />
</OverlayTrigger>
<span>With custom check:</span>
<IconTooltip message="Create custom check using permissions builder" />
</span>
);
@ -849,9 +833,7 @@ class Permissions extends Component {
let _limitSection;
const rowLimitTooltip = (
<Tooltip id="tooltip-row-permissions">
Set limit on number of rows fetched per request
</Tooltip>
<IconTooltip message="Set limit on number of rows fetched per request" />
);
if (query === 'select') {
@ -883,9 +865,11 @@ class Permissions extends Component {
};
const rowPermissionTooltip = (
<Tooltip id="tooltip-row-permissions">
Set permission rule for {getIngForm(permissionsState.query)} rows
</Tooltip>
<IconTooltip
message={`Set permission rule for ${getIngForm(
permissionsState.query
)} rows`}
/>
);
const getUpdateFilterOptions = (filterType, disabled = false) => {
@ -1086,9 +1070,11 @@ class Permissions extends Component {
};
const colPermissionTooltip = (
<Tooltip id="tooltip-row-permissions">
Choose columns allowed to be {getEdForm(permissionsState.query)}
</Tooltip>
<IconTooltip
message={`Choose columns allowed to be ${getEdForm(
permissionsState.query
)}`}
/>
);
const colSectionTitle = 'Column ' + query + ' permissions';
@ -1139,60 +1125,6 @@ class Permissions extends Component {
return _columnSection;
};
// const getUpsertSection = () => {
// if (query !== 'insert') {
// return;
// }
//
// const dispatchToggleAllowUpsert = checked => {
// dispatch(permToggleAllowUpsert(checked));
// };
//
// const upsertAllowed = permissionsState.insert
// ? permissionsState.insert.allow_upsert
// : false;
//
// const upsertToolTip = (
// <Tooltip id="tooltip-upsert">
// Allow upsert queries. Upsert lets you update a row if it already
// exists, otherwise insert it
// </Tooltip>
// );
//
// const upsertStatus = upsertAllowed ? 'enabled' : 'disabled';
//
// return (
// <CollapsibleToggle
// title={getSectionHeader(
// 'Upsert queries permissions',
// upsertToolTip,
// upsertStatus
// )}
// useDefaultTitleStyle
// testId={'toggle-upsert-permission'}
// >
// <div
// className={sectionClasses}
// title={noPermissions ? noPermissionsMsg : ''}
// >
// <div className="checkbox">
// <label>
// <input
// type="checkbox"
// checked={upsertAllowed}
// value="toggle_upsert"
// onChange={e => dispatchToggleAllowUpsert(e.target.checked)}
// disabled={noPermissions}
// />
// Allow role <b>{permissionsState.role}</b> to make upsert
// queries
// </label>
// </div>
// </div>
// </CollapsibleToggle>
// );
// };
const getPresetsSection = action => {
if (query !== action) {
return;
@ -1489,10 +1421,11 @@ class Permissions extends Component {
};
const presetTooltip = (
<Tooltip id="tooltip-insert-set-operations">
Set static values or session variables as pre-determined values for
columns while {getIngForm(query)}
</Tooltip>
<IconTooltip
message={`Set static values or session variables as pre-determined values for columns while ${getIngForm(
query
)}`}
/>
);
const presetStatus = !isEmpty(presets)
@ -1541,10 +1474,7 @@ class Permissions extends Component {
: false;
const aggregationToolTip = (
<Tooltip id="tooltip-aggregation">
Allow queries with aggregate functions like sum, count, avg, max,
min, etc
</Tooltip>
<IconTooltip message="Allow queries with aggregate functions like sum, count, avg, max, min, etc" />
);
const aggregationStatus = aggregationAllowed ? 'enabled' : 'disabled';
@ -1640,7 +1570,7 @@ class Permissions extends Component {
styles.fkInEdit +
' ' +
styles.add_mar_right +
' input-sm form-control'
' form-control'
}
value={applyTo[type] || value || ''}
onChange={setApplyTo}
@ -1707,9 +1637,7 @@ class Permissions extends Component {
let clonePermissionsHtml = null;
if (applyToListHtml.length) {
const cloneToolTip = (
<Tooltip id="tooltip-clone">
Apply same permissions to other tables/roles/actions
</Tooltip>
<IconTooltip message="Apply same permissions to other tables/roles/actions" />
);
const validApplyToList = permissionsState.applySamePermissions.filter(
@ -1759,10 +1687,11 @@ class Permissions extends Component {
return null;
}
const tooltip = (
<Tooltip id="tooltip-backend-only">
When enabled, this {permissionsState.query} mutation is accessible
only via "trusted backends"
</Tooltip>
<IconTooltip
message={
'When enabled, this {permissionsState.query} mutation is accessible only via "trusted backends"'
}
/>
);
const isBackendOnly = !!(
permissionsState[permissionsState.query] &&

View File

@ -1,5 +1,5 @@
import React from 'react';
import Tooltip from 'react-bootstrap/lib/Tooltip';
import { IconTooltip } from '@/new-components/Tooltip';
import { getPermissionFilterString } from '../PermissionsSummary/utils';
import { getLegacyOperator, allOperators } from './PermissionBuilder/utils';
@ -44,9 +44,7 @@ const tooltipMsg: Record<FilterType, string> = {
};
export const getUpdateTooltip = (filterType: FilterType) => (
<Tooltip id={`tooltip-update-${filterType}`}>
{tooltipMsg[filterType]}
</Tooltip>
<IconTooltip message={tooltipMsg[filterType]} />
);
const getOptionsForUpdate = (

View File

@ -1,7 +1,6 @@
import React from 'react';
import { ToolTip } from '@/new-components/Tooltip';
import { IconTooltip } from '@/new-components/Tooltip';
import { RetryConf } from '../../types';
import Tooltip from '../../../../Common/Tooltip/Tooltip';
import { inputStyles } from '../../constants';
type Props = {
@ -14,7 +13,6 @@ type RetryInputRowType = {
label: string;
tooltipProps?:
| {
id: string;
message: string;
}
| undefined;
@ -38,9 +36,9 @@ const RetryInputRow = ({
<label className="flex items-center">
{label}
{tooltipProps ? (
<Tooltip {...tooltipProps} />
<IconTooltip {...tooltipProps} />
) : (
<ToolTip message="Number of retries that Hasura makes to the webhook in case of failure" />
<IconTooltip message="Number of retries that Hasura makes to the webhook in case of failure" />
)}
</label>
</div>
@ -57,7 +55,7 @@ const RetryInputRow = ({
};
const RetryConfEditor: React.FC<Props> = props => {
const { retryConf, setRetryConf, legacyTooltip = true } = props;
const { retryConf, setRetryConf } = props;
const handleRetryConfChange = (e: React.ChangeEvent<HTMLInputElement>) => {
const label = e.target.name;
const value = e.target.value;
@ -78,15 +76,10 @@ const RetryConfEditor: React.FC<Props> = props => {
placeholder: 'number of retries (default: 0)',
onChange: handleRetryConfChange,
}}
tooltipProps={
legacyTooltip
? {
id: 'retry-conf-num-retries',
message:
'Number of retries that Hasura makes to the webhook in case of failure',
}
: undefined
}
tooltipProps={{
message:
'Number of retries that Hasura makes to the webhook in case of failure',
}}
/>
<RetryInputRow
label="Retry interval in seconds"
@ -97,14 +90,9 @@ const RetryConfEditor: React.FC<Props> = props => {
placeholder: 'interval time in seconds (default: 10)',
onChange: handleRetryConfChange,
}}
tooltipProps={
legacyTooltip
? {
id: 'retry-conf-interval-sec',
message: 'Interval (in seconds) between each retry',
}
: undefined
}
tooltipProps={{
message: 'Interval (in seconds) between each retry"',
}}
/>
<RetryInputRow
@ -116,14 +104,9 @@ const RetryConfEditor: React.FC<Props> = props => {
placeholder: 'timeout in seconds (default: 60)',
onChange: handleRetryConfChange,
}}
tooltipProps={
legacyTooltip
? {
id: 'retry-conf-timeout-sec',
message: 'Request timeout for the webhook',
}
: undefined
}
tooltipProps={{
message: 'Request timeout for the webhook',
}}
/>
{/* exists(retryConf.tolerance_sec) ? (
@ -133,7 +116,7 @@ const RetryConfEditor: React.FC<Props> = props => {
<div className={`col-md-3 ${styles.padd_left_remove}`}>
<label className={`${styles.add_mar_right} ${styles.retryLabel}`}>
Tolerance in seconds
<Tooltip
<IconTooltip
id="retry-conf-interval-sec"
message="If scheduled time for an event is in the past, it gets dropped if it is older than the tolerance limit"
/>

View File

@ -1,5 +1,5 @@
import React from 'react';
import { ToolTip } from '@/new-components/Tooltip';
import { IconTooltip } from '@/new-components/Tooltip';
type FormLabelProps = {
title: string;
@ -11,7 +11,7 @@ const FormLabel: React.FC<FormLabelProps> = ({ title, tooltip }) => {
<>
<h2 className="text-lg font-semibold mb-xs flex items-center">
{title}
<ToolTip message={tooltip} />
<IconTooltip message={tooltip} />
</h2>
</>
);

View File

@ -1,5 +1,5 @@
import React from 'react';
import { ToolTip } from '@/new-components/Tooltip';
import { IconTooltip } from '@/new-components/Tooltip';
import * as tooltip from './Tooltips';
import KnowMoreLink from '../../../../Common/KnowMoreLink/KnowMoreLink';
import { capitalize } from '../../../../Common/utils/jsUtils';
@ -37,7 +37,7 @@ export const Operations: React.FC<OperationProps> = ({
o === 'enable_manual' ? (
<span className="flex items-center">
Via console
<ToolTip message={tooltip.manualOperationsDescription} />
<IconTooltip message={tooltip.manualOperationsDescription} />
<KnowMoreLink href="https://hasura.io/docs/latest/graphql/core/event-triggers/invoke-trigger-console.html" />
</span>
) : (

View File

@ -1,8 +1,6 @@
import React from 'react';
import PropTypes from 'prop-types';
import OverlayTrigger from 'react-bootstrap/lib/OverlayTrigger';
import Tooltip from 'react-bootstrap/lib/Tooltip';
import { FaQuestionCircle } from 'react-icons/fa';
import { IconTooltip } from '@/new-components/Tooltip';
import DropdownButton from '../../../Common/DropdownButton/DropdownButton';
import {
@ -60,47 +58,49 @@ class Common extends React.Component {
const tooltips = {
graphqlurl: (
<Tooltip id="tooltip-cascade">
Remote GraphQL servers URL. E.g. https://my-domain/v1/graphql
</Tooltip>
<IconTooltip
message="Remote GraphQL servers URL. E.g. https://my-domain/v1/graphql"
side="right"
/>
),
clientHeaderForward: (
<Tooltip id="tooltip-cascade">
Toggle forwarding headers sent by the client app in the request to
your remote GraphQL server
</Tooltip>
<IconTooltip
message="Toggle forwarding headers sent by the client app in the request to your remote GraphQL server"
side="right"
/>
),
additionalHeaders: (
<Tooltip id="tooltip-cascade">
Custom headers to be sent to the remote GraphQL server
</Tooltip>
<IconTooltip
message="Custom headers to be sent to the remote GraphQL server"
side="right"
/>
),
schema: (
<Tooltip id="tooltip-cascade">
Give this GraphQL schema a friendly name.
</Tooltip>
<IconTooltip
message="Give this GraphQL schema a friendly name."
side="right"
/>
),
timeoutConf: (
<Tooltip id="tooltip-cascade">
Configure timeout for your remote GraphQL server. Defaults to 60
seconds.
</Tooltip>
<IconTooltip
message="Configure timeout for your remote GraphQL server. Defaults to 60 seconds."
side="right"
/>
),
comment: (
<Tooltip id="tooltip-cascade">
A statement to help describe the remote schema in brief
</Tooltip>
<IconTooltip
message="A statement to help describe the remote schema in brief"
side="right"
/>
),
};
const getTimeoutSection = () => {
return (
<React.Fragment>
<div className={subHeading}>
<div className={`${subHeading} flex items-center`}>
GraphQL server timeout
<OverlayTrigger placement="right" overlay={tooltips.timeoutConf}>
<FaQuestionCircle aria-hidden="true" className="mb-1 ml-xs" />
</OverlayTrigger>
{tooltips.timeoutConf}
</div>
<label className="ml-0 pl-0">
<input
@ -122,11 +122,8 @@ class Common extends React.Component {
return (
<div>
<div className={`${subHeading} pt-md`}>
Remote Schema name *
<OverlayTrigger placement="right" overlay={tooltips.schema}>
<FaQuestionCircle aria-hidden="true" className="mb-1 ml-xs" />
</OverlayTrigger>
<div className={`${subHeading} pt-md flex items-center`}>
Remote Schema name *{tooltips.schema}
</div>
<label className="ml-0 pl-0 w-80">
<input
@ -144,11 +141,8 @@ class Common extends React.Component {
/>
</label>
<hr className="my-md" />
<div className={subHeading}>
GraphQL server URL *
<OverlayTrigger placement="right" overlay={tooltips.graphqlurl}>
<FaQuestionCircle aria-hidden="true" className="mb-1 ml-xs" />
</OverlayTrigger>
<div className={`${subHeading} flex items-center`}>
GraphQL server URL *{tooltips.graphqlurl}
</div>
<div className={'w-80'}>
<DropdownButton
@ -188,8 +182,8 @@ class Common extends React.Component {
<div className={`${subHeading} pt-md`}>
Headers for the remote GraphQL server
</div>
<div>
<label className="mb-md justify-center mr-sm">
<div className={`${subHeading} flex items-center mb-md`}>
<label className="flex justify-center mr-sm">
<input
onChange={this.toggleForwardHeaders.bind(this)}
className={`${focusYellowRing} m-0`}
@ -200,22 +194,12 @@ class Common extends React.Component {
disabled={isDisabled}
/>
<span className="ml-md">Forward all headers from client</span>
{tooltips.clientHeaderForward}
</label>
<OverlayTrigger
placement="right"
overlay={tooltips.clientHeaderForward}
>
<FaQuestionCircle aria-hidden="true" size={'1em'} />
</OverlayTrigger>
</div>
<div className={`${subHeading} font-normal`}>
<div className={`${subHeading} font-normal flex items-center`}>
Additional headers:
<OverlayTrigger
placement="right"
overlay={tooltips.additionalHeaders}
>
<FaQuestionCircle aria-hidden="true" className="mb-1 ml-xs" />
</OverlayTrigger>
{tooltips.additionalHeaders}
</div>
<CommonHeader
eventPrefix="REMOTE_SCHEMA"
@ -232,11 +216,9 @@ class Common extends React.Component {
<hr className="my-md" />
{getTimeoutSection()}
<hr className="my-md" />
<div className={subHeading}>
<div className={`${subHeading} flex items-center`}>
Comment
<OverlayTrigger placement="right" overlay={tooltips.comment}>
<FaQuestionCircle aria-hidden="true" className="mb-1 ml-xs" />
</OverlayTrigger>
{tooltips.comment}
</div>
<label className="pl-0">
<input
@ -254,18 +236,12 @@ class Common extends React.Component {
{/* <GraphQLCustomization mode="edit" customization={customization} dispatch={this.props.dispatch} /> */}
{isNew ? null : (
<>
<div className="text-lg font-bold">
<div className="text-lg font-bold flex items-center">
GraphQL Customizations{' '}
<OverlayTrigger
placement="right"
overlay={
<Tooltip id="tooltip-cascade">
Individual Types and Fields will be editable after saving.
</Tooltip>
}
>
<FaQuestionCircle aria-hidden="true" className="mb-1 ml-xs" />
</OverlayTrigger>
<IconTooltip
message="Individual Types and Fields will be editable after saving."
side="right"
/>
</div>
<GraphQLCustomizationEdit
remoteSchemaName={name}

View File

@ -1,7 +1,5 @@
import React, { useState, useEffect } from 'react';
import { OverlayTrigger } from 'react-bootstrap';
import { FaQuestionCircle } from 'react-icons/fa';
import Tooltip from 'react-bootstrap/lib/Tooltip';
import { IconTooltip } from '@/new-components/Tooltip';
import { Button } from '../../../../Common';
import TypeMapping from './TypeMapping';
import { inputStyles } from '../../constants';
@ -25,9 +23,10 @@ type Props = {
};
const tooltip = (
<Tooltip id="tooltip-cascade">
Field remapping takes precedence to prefixes and suffixes.
</Tooltip>
<IconTooltip
message="Field remapping takes precedence to prefixes and suffixes."
side="right"
/>
);
const SelectOne = ({
@ -138,10 +137,7 @@ const FieldNames = ({
</div>
<div className="text-lg font-bold mt-md">
Remap Field Names{' '}
<OverlayTrigger placement="right" overlay={tooltip}>
<FaQuestionCircle aria-hidden="true" />
</OverlayTrigger>
Remap Field Names {tooltip}
<TypeMapping
types={
types.find(x => x.typeName === fieldNameInput?.parentType)

View File

@ -1,8 +1,6 @@
import React, { useState, useEffect } from 'react';
import { OverlayTrigger } from 'react-bootstrap';
import Tooltip from 'react-bootstrap/lib/Tooltip';
import { GraphQLSchema } from 'graphql';
import { FaQuestionCircle } from 'react-icons/fa';
import { IconTooltip } from '@/new-components/Tooltip';
import { Button } from '../../../../Common';
import { graphQLCustomization as GType } from '../../types';
import TypeMapping from './TypeMapping';
@ -156,16 +154,7 @@ const GraphQLCustomizationEdit = ({
<div className="flex items-center mt-md">
<label className="w-1/3">
Root Field Namespace{' '}
<OverlayTrigger
placement="right"
overlay={
<Tooltip id="tooltip-cascade">
Root field type names will be prefixed by this name.
</Tooltip>
}
>
<FaQuestionCircle aria-hidden="true" />
</OverlayTrigger>
<IconTooltip message="Root field type names will be prefixed by this name." />
</label>
<div className="w-2/3">
<input
@ -232,16 +221,10 @@ const GraphQLCustomizationEdit = ({
<div className="text-lg font-bold mt-md">
Rename Type Names{' '}
<OverlayTrigger
placement="right"
overlay={
<Tooltip id="tooltip-cascade">
Type remapping takes precedence to prefixes and suffixes.
</Tooltip>
}
>
<FaQuestionCircle aria-hidden="true" />
</OverlayTrigger>
<IconTooltip
message="Type remapping takes precedence to prefixes and suffixes."
side="right"
/>
</div>
<TypeMapping

View File

@ -53,25 +53,6 @@ import { Component } from '@/new-components/Component';
export const Template = args => <Component {...args} />;
export const TemplateStories = stories => {
return (
<div>
{Object.entries(stories).map(([storyName, story]) => (
<div key={storyName}>
<div
className={
'text-black dark:text-white bg-gray-100 dark:bg-gray-700 underline p-2'
}
>
{storyName}
</div>
<div className={'py-4'}>{Template(story)}</div>
</div>
))}
</div>
);
};
export const stories = {
'States - Disabled': {
prop1: 'text-gray-400',

View File

@ -1,5 +1,5 @@
import React from 'react';
import { ToolTip } from '@/new-components/Tooltip';
import { IconTooltip } from '@/new-components/Tooltip';
import { InputField, Radio } from '@/new-components/Form';
import { Collapse } from '@/new-components/Collapse';
import { RequestHeadersSelector } from '@/new-components/RequestHeadersSelector';
@ -21,7 +21,7 @@ export const AdvancedSettings = () => {
<div className="mb-md">
<label className="block flex items-center text-gray-600 font-semibold mb-xs">
Headers
<ToolTip message="Configure headers for the request to the webhook" />
<IconTooltip message="Configure headers for the request to the webhook" />
</label>
<RequestHeadersSelector
name="headers"
@ -31,7 +31,7 @@ export const AdvancedSettings = () => {
<div className="mb-md">
<label className="block flex items-center text-gray-600 font-semibold">
Method
<ToolTip message="Configure method to transform your request to (optional). GET requests will be sent without a payload or content-type header" />
<IconTooltip message="Configure method to transform your request to (optional). GET requests will be sent without a payload or content-type header" />
</label>
<Radio
orientation="horizontal"
@ -50,7 +50,7 @@ export const AdvancedSettings = () => {
<div className="mb-md">
<label className="block flex items-center text-gray-600 font-semibold mb-xs">
Query Params
<ToolTip message="Configure headers for the request to the webhook" />
<IconTooltip message="Configure headers for the request to the webhook" />
</label>
<RequestHeadersSelector
name="query_params"

View File

@ -1,5 +1,5 @@
import React from 'react';
import { ToolTip } from '@/new-components/Tooltip';
import { IconTooltip } from '@/new-components/Tooltip';
import { InputField } from '@/new-components/Form';
import FrequentlyUsedCrons from '@/components/Services/Events/Common/Components/FrequentlyUsedCrons';
import { useFormContext } from 'react-hook-form';
@ -16,7 +16,7 @@ export const CronScheduleSelector = () => {
<>
<div className="block flex items-center text-gray-600 font-semibold mb-xs">
<label htmlFor="schedule">Cron Schedule</label>
<ToolTip 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)" />
<a
className="ml-xs cursor-pointer hover:no-underline font-normal text-secondary hover:text-secondary-darker italic"
href="https://crontab.guru/#*_*_*_*_*"

View File

@ -1,5 +1,5 @@
import React from 'react';
import { ToolTip } from '@/new-components/Tooltip';
import { IconTooltip } from '@/new-components/Tooltip';
import { useFormContext } from 'react-hook-form';
import { Switch } from '@/new-components/Switch';
@ -14,7 +14,7 @@ export const IncludeInMetadataSwitch = () => {
<>
<label className="block flex items-center text-gray-600 font-semibold mb-xs">
Include in Metadata
<ToolTip message="If enabled, this cron trigger will be included in the metadata of GraphqL Engine i.e. it will be a part of the metadata that is exported as migrations" />
<IconTooltip message="If enabled, this cron trigger will be included in the metadata of GraphqL Engine i.e. it will be a part of the metadata that is exported as migrations" />
</label>
<div className="relative w-full max-w-xl mb-xs">
<Switch

View File

@ -1,6 +1,6 @@
import React from 'react';
import { Collapse } from '@/new-components/Collapse';
import { ToolTip } from '@/new-components/Tooltip';
import { IconTooltip } from '@/new-components/Tooltip';
import { useFormContext } from 'react-hook-form';
const inputStyes =
@ -20,7 +20,7 @@ export const RetryConfiguration = () => {
<div className="grid grid-cols-12 gap-3">
<div className="col-span-6 flex items-center">
<label className="block">Number of retries</label>
<ToolTip message="Number of retries that Hasura makes to the webhook in case of failure" />
<IconTooltip message="Number of retries that Hasura makes to the webhook in case of failure" />
</div>
<div className="col-span-6">
{/* TODO: This is a horizontal/inline input field, currently we do not have it in common so this component implements its own,
@ -36,7 +36,7 @@ export const RetryConfiguration = () => {
<div className="grid grid-cols-12 gap-3">
<div className="col-span-6 flex items-center">
<label className="block">Retry interval in seconds</label>
<ToolTip message="Interval (in seconds) between each retry" />
<IconTooltip message="Interval (in seconds) between each retry" />
</div>
<div className="col-span-6">
<input
@ -50,7 +50,7 @@ export const RetryConfiguration = () => {
<div className="grid grid-cols-12 gap-3">
<div className="col-span-6 flex items-center">
<label className="block">Timeout in seconds</label>
<ToolTip message="Request timeout for the webhook" />
<IconTooltip message="Request timeout for the webhook" />
</div>
<div className="col-span-6">
<input

View File

@ -8,7 +8,7 @@ import { NormalizedTable, Table } from '@/dataSources/types';
import { PGFunction } from '@/dataSources/services/postgresql/types';
import { generateTableDef } from '@/dataSources';
import { InputField } from '@/new-components/Form';
import { ToolTip } from '@/new-components/Tooltip';
import { IconTooltip } from '@/new-components/Tooltip';
import { Collapse } from '@/new-components/Collapse';
import { getIngForm } from '../../../components/Services/Data/utils';
@ -32,7 +32,7 @@ const NoChecksLabel = () => (
const CustomLabel = () => (
<span data-test="custom-check" className="flex items-center">
With custom check:
<ToolTip message="Create custom check using permissions builder" />
<IconTooltip message="Create custom check using permissions builder" />
</span>
);

View File

@ -1,4 +1,4 @@
import { ToolTip } from '@/new-components/Tooltip';
import { IconTooltip } from '@/new-components/Tooltip';
import React from 'react';
type SubFieldTitleProps = {
@ -17,7 +17,7 @@ export const SubFieldTitle = ({
<div className="flex items-center cursor-pointer w-max whitespace-nowrap">
{!enabled ? (
<>
<ToolTip
<IconTooltip
className="mr-sm text-gray-400"
message="Only fields with arguments or subfields can be toggled"
/>

View File

@ -3,7 +3,7 @@ import { Button } from '@/new-components/Button';
import { FieldError } from 'react-hook-form';
import { Form, InputField } from '@/new-components/Form';
import { useFireNotification } from '@/new-components/Notifications';
import { ToolTip } from '@/new-components/Tooltip';
import { IconTooltip } from '@/new-components/Tooltip';
import get from 'lodash.get';
import { APIError } from '@/hooks/error';
import React, { useState } from 'react';
@ -116,7 +116,7 @@ export const Create = ({ onSuccess }: Props) => {
<div className="mb-lg w-4/12">
<label className="block flex items-center text-gray-600 font-semibold mb-xs">
GraphQL Server Timeout
<ToolTip message="Configure timeout for your remote GraphQL server. Defaults to 60 seconds." />
<IconTooltip message="Configure timeout for your remote GraphQL server. Defaults to 60 seconds." />
</label>
<div className="relative w-full">
<input
@ -144,7 +144,7 @@ export const Create = ({ onSuccess }: Props) => {
/>
<label className="pl-3 flex items-center mt-2">
Forward all headers from client
<ToolTip
<IconTooltip
message="Toggle forwarding headers sent by the client app in the request to
your remote GraphQL server"
/>
@ -153,7 +153,7 @@ export const Create = ({ onSuccess }: Props) => {
<div>
<label className="flex items-center mb-xs">
Additional headers:
<ToolTip message="Custom headers to be sent to the remote GraphQL server" />
<IconTooltip message="Custom headers to be sent to the remote GraphQL server" />
</label>
</div>
@ -190,7 +190,7 @@ export const Create = ({ onSuccess }: Props) => {
<div className="flex items-center col-span-4">
<label className="flex items-center text-gray-600 font-medium">
Root Field Namespace
<ToolTip message="Root field type names will be prefixed by this name." />
<IconTooltip message="Root field type names will be prefixed by this name." />
</label>
</div>
<div className="col-span-8">
@ -208,7 +208,7 @@ export const Create = ({ onSuccess }: Props) => {
<h2 className="text-lg font-semibold mb-xs items-center flex">
Types
<ToolTip message="add a prefix / suffix to all types of the remote schema" />
<IconTooltip message="add a prefix / suffix to all types of the remote schema" />
</h2>
<div className="grid gap-3 grid-cols-12 mb-md">
@ -246,7 +246,7 @@ export const Create = ({ onSuccess }: Props) => {
<h2 className="text-lg font-semibold mb-xs flex items-center">
Fields
<ToolTip message="add a prefix / suffix to the fields of the query / mutation root fields" />
<IconTooltip message="add a prefix / suffix to the fields of the query / mutation root fields" />
</h2>
<h3 className="font-semibold mb-xs text-gray-600 text-lg">

View File

@ -1,5 +1,5 @@
import React from 'react';
import { ToolTip } from '@/new-components/Tooltip';
import { IconTooltip } from '@/new-components/Tooltip';
import { useFormContext } from 'react-hook-form';
export const GraphQLServiceUrl = () => {
@ -9,7 +9,7 @@ export const GraphQLServiceUrl = () => {
<div className="mb-md w-6/12">
<label className="block flex items-center text-gray-600 font-semibold mb-xs">
GraphQL Service URL
<ToolTip message="Remote GraphQL servers URL. E.g. https://my-domain/v1/graphql" />
<IconTooltip message="Remote GraphQL servers URL. E.g. https://my-domain/v1/graphql" />
</label>
<p className="text-sm text-gray-600 mb-sm">
Note: Specifying the server URL via an environmental variable is

View File

@ -2,7 +2,7 @@ import React from 'react';
import * as Collapsible from '@radix-ui/react-collapsible';
import { FaChevronRight } from 'react-icons/fa';
import { ToolTip } from '@/new-components/Tooltip';
import { IconTooltip } from '@/new-components/Tooltip';
export interface CollapseHeaderProps extends React.HTMLAttributes<HTMLElement> {
title?: string;
@ -72,7 +72,7 @@ const CollapseHeader: React.FC<CollapseHeaderProps> = ({
</Collapsible.Trigger>
<div className="flex items-center gap-2">
{!!tooltip && <ToolTip message={tooltip} />}
{!!tooltip && <IconTooltip message={tooltip} />}
{!!status && (
<span>

View File

@ -131,25 +131,6 @@ export const Template = args => {
);
};
export const TemplateStories = stories => {
return (
<div>
{Object.entries(stories).map(([storyName, story]) => (
<div key={storyName}>
<div
className={
'text-black dark:text-white bg-gray-100 dark:bg-gray-700 underline p-2'
}
>
{storyName}
</div>
<div>{Template(story)}</div>
</div>
))}
</div>
);
};
export const stories = {
'States - With default value': {
name: 'codeEditorFieldName',

View File

@ -1,4 +1,4 @@
import { ToolTip } from '@/new-components/Tooltip';
import { IconTooltip } from '@/new-components/Tooltip';
import * as React from 'react';
import { FieldError } from 'react-hook-form';
import clsx from 'clsx';
@ -125,7 +125,7 @@ export const FieldWrapper = (props: FieldWrapperProps) => {
: null}
{label}
</span>
{tooltip ? <ToolTip message={tooltip} /> : null}
{tooltip ? <IconTooltip message={tooltip} /> : null}
</span>
{description ? (
<span className="text-gray-600 mb-xs font-normal text-sm">

View File

@ -0,0 +1,163 @@
import { Canvas, Meta, Story, ArgsTable } from '@storybook/addon-docs';
import { TemplateStoriesFactory } from '@/utils/StoryUtils';
import { IconTooltip } from '@/new-components/Tooltip';
<Meta
title="components/IconTooltip ✨"
component={IconTooltip}
parameters={{
docs: { source: { type: 'code' } },
chromatic: { disableSnapshot: true },
}}
decorators={[
Story => <div className={'p-16 w-full flex justify-center'}>{Story()}</div>,
]}
/>
# IconTooltip
- [🧰 Overview](#-overview)
- [🔁 States](#-states)
- [🎭 Variants](#-variants)
- [⚙️ API](#-api)
- [🧪 Testing](#-testing)
- [🐙 Code on Github](https://github.com/hasura/graphql-engine-mono/tree/main/console/src/new-components/Tooltip/IconTooltip.tsx)
## 🧰 Overview
This component is a special variant of the `<Tooltip />` component triggered by hovering an embedded icon.
Default CSS display is `inline`, provided without padding and margin (displayed here with `padding: 2rem;`).
### Basic usage
```ts
import { IconTooltip } from '@/new-components/IconTooltip';
```
```tsx
<IconTooltip message="The tooltip message" />
```
<Canvas>
<Story name="Overview">
<IconTooltip message="The tooltip message" />
</Story>
</Canvas>
export const Template = args => <IconTooltip {...args} />;
export const CustomTemplateStoriesFactory = Template =>
TemplateStoriesFactory(Template, 'py-20 flex justify-center');
export const stories = {
'Variant - Side Top': {
side: 'top',
message: 'The tooltip message',
},
'Variant - Side Right': {
side: 'right',
message: 'The tooltip message',
},
'Variant - Side Bottom': {
side: 'bottom',
message: 'The tooltip message',
},
'Variant - Side Left': {
side: 'left',
message: 'The tooltip message',
},
'Variant - Default Open': {
defaultOpen: true,
message: 'The tooltip message',
},
'API playground': {
message: 'The tooltip message',
},
'Testing - Scalability': {
message: `Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod
tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim
veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea
commodo consequat. Duis aute irure dolor in reprehenderit in voluptate
velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint
occaecat cupidatat non proident, sunt in culpa qui officia deserunt
mollit anim id est laborum.`,
},
};
## 🎭 Variants
### 🎭 Side
<Canvas>
<Story
name="Variant - Side"
args={{
'Variant - Side Top': stories['Variant - Side Top'],
'Variant - Side Right': stories['Variant - Side Right'],
'Variant - Side Bottom': stories['Variant - Side Bottom'],
'Variant - Side Left': stories['Variant - Side Left'],
}}
>
{TemplateStoriesFactory(Template).bind({})}
</Story>
</Canvas>
### 🎭 Default Open
<Canvas>
<Story name="Variant - Default Open" args={stories['Variant - Default Open']}>
{Template.bind({})}
</Story>
</Canvas>
## ⚙️ API
<Canvas>
<Story name="API playground" args={stories['API playground']}>
{Template.bind({})}
</Story>
</Canvas>
<details open>
<summary className="mdx-collapsible-section">
<p className="mdx-collapsible-section__chevron">
<strong>&gt;</strong>
</p>
<p className="mdx-collapsible-section__label">
<strong>Show/hide</strong> props
</p>
</summary>
<ArgsTable story="API playground" />
</details>
## 🧪 Testing
### 🧪 Scalability
<Canvas>
<Story name="Testing - Scalability" args={stories['Testing - Scalability']}>
{Template.bind({})}
</Story>
</Canvas>
### 🧪 Snapshot 📸
<Canvas>
<Story
name="Testing - Snapshot"
args={Object.keys(stories).reduce((accumulator, storyId) => ({
...accumulator,
[storyId]: {
...stories[storyId],
defaultOpen: true,
},
}))}
parameters={{
chromatic: { disableSnapshot: false },
}}
>
{CustomTemplateStoriesFactory(Template).bind({})}
</Story>
</Canvas>

View File

@ -0,0 +1,32 @@
import React from 'react';
import { Tooltip, TooltipProps } from '@/new-components/Tooltip';
import { FaQuestionCircle } from 'react-icons/fa';
export type IconTooltipProps = {
/**
* The tooltip message
*/
message: string;
/**
* The tooltip icon classes
*/
className?: string;
} & Pick<TooltipProps, 'side'> &
Pick<TooltipProps, 'defaultOpen'>;
export const IconTooltip: React.VFC<IconTooltipProps> = ({
message,
className,
side = 'right',
defaultOpen = false,
}) => (
<Tooltip
tooltipContentChildren={message}
side={side}
defaultOpen={defaultOpen}
>
<FaQuestionCircle
className={`h-4 text-muted cursor-pointer ${className}`}
/>
</Tooltip>
);

View File

@ -0,0 +1,228 @@
import { Canvas, Meta, Story, ArgsTable } from '@storybook/addon-docs';
import {
userEvent,
waitFor,
waitForElementToBeRemoved,
within,
} from '@storybook/testing-library';
import { screen } from '@testing-library/dom';
import { expect } from '@storybook/jest';
import { TemplateStoriesFactory } from '@/utils/StoryUtils';
import { Tooltip } from '@/new-components/Tooltip';
<Meta
title="components/Tooltip ✨"
component={Tooltip}
parameters={{
docs: { source: { type: 'code' } },
chromatic: { disableSnapshot: true },
}}
decorators={[
Story => <div className={'p-16 w-full flex justify-center'}>{Story()}</div>,
]}
/>
export const ChildrenExample = (
<div
className={
'text-black dark:text-white text-center bg-gray-200 dark:bg-gray-700 py-1 rounded'
}
style={{ backgroundImage: '100%' }}
>
&lt;--SLOT--&gt;
</div>
);
# Tooltip
- [🧰 Overview](#-overview)
- [🔁 States](#-states)
- [🎭 Variants](#-variants)
- [⚙️ API](#-api)
- [🧪 Testing](#-testing)
- [🐙 Code on Github](https://github.com/hasura/graphql-engine-mono/tree/main/console/src/new-components/Tooltip/Tooltip.tsx)
## 🧰 Overview
This component is wrapping a [radix-ui tooltip component](https://www.radix-ui.com/docs/primitives/components/tooltip)
triggered when one of its children is hovered. The side of the tooltip is determined by the `side` prop.
Default CSS display is `inline`, provided without padding and margin (displayed here with `padding: 2rem;`).
### Basic usage
```ts
import { Tooltip } from '@/new-components/Tooltip';
```
```tsx
<Tooltip
tooltipContentChildren="The tooltip tooltipContentChildren"
children={ChildrenExample}
/>
```
<Canvas>
<Story name="Overview">
<Tooltip
tooltipContentChildren="The tooltip tooltipContentChildren"
children={ChildrenExample}
/>
</Story>
</Canvas>
export const Template = args => <Tooltip {...args} />;
export const CustomTemplateStoriesFactory = Template =>
TemplateStoriesFactory(Template, 'py-20 flex justify-center');
export const stories = {
'States - Hovered': {
tooltipContentChildren: 'The tooltip tooltipContentChildren',
children: ChildrenExample,
},
'Variant - Side Top': {
side: 'top',
tooltipContentChildren: 'The tooltip tooltipContentChildren',
children: ChildrenExample,
},
'Variant - Side Right': {
side: 'right',
tooltipContentChildren: 'The tooltip tooltipContentChildren',
children: ChildrenExample,
},
'Variant - Side Bottom': {
side: 'bottom',
tooltipContentChildren: 'The tooltip tooltipContentChildren',
children: ChildrenExample,
},
'Variant - Side Left': {
side: 'left',
tooltipContentChildren: 'The tooltip tooltipContentChildren',
children: ChildrenExample,
},
'Variant - Default Open': {
defaultOpen: true,
tooltipContentChildren: 'The tooltip tooltipContentChildren',
children: ChildrenExample,
},
'API playground': {
tooltipContentChildren: 'The tooltip tooltipContentChildren',
children: ChildrenExample,
},
'Testing - Scalability': {
children: ChildrenExample,
tooltipContentChildren: `Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod
tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim
veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea
commodo consequat. Duis aute irure dolor in reprehenderit in voluptate
velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint
occaecat cupidatat non proident, sunt in culpa qui officia deserunt
mollit anim id est laborum.`,
},
};
## 🔁 States
### 🔁 Hovered
<Canvas>
<Story
name="States - Hovered"
args={stories['States - Hovered']}
play={async ({ canvasElement }) => {
const canvas = within(canvasElement);
await waitFor(async () => {
await userEvent.hover(canvas.getByTestId('tooltip-trigger'));
});
await waitFor(() => {
expect(screen.getByRole('tooltip')).toBeInTheDocument();
});
await waitFor(async () => {
await userEvent.unhover(canvas.getByTestId('tooltip-trigger'));
});
await waitFor(() => {
expect(screen.queryByRole('tooltip')).not.toBeInTheDocument();
});
}}
>
>{Template.bind({})}
</Story>
</Canvas>
## 🎭 Variants
### 🎭 Side
<Canvas>
<Story
name="Variant - Side"
args={{
'Variant - Side Top': stories['Variant - Side Top'],
'Variant - Side Right': stories['Variant - Side Right'],
'Variant - Side Bottom': stories['Variant - Side Bottom'],
'Variant - Side Left': stories['Variant - Side Left'],
}}
>
{CustomTemplateStoriesFactory(Template).bind({})}
</Story>
</Canvas>
### 🎭 Default Open
<Canvas>
<Story name="Variant - Default Open" args={stories['Variant - Default Open']}>
{Template.bind({})}
</Story>
</Canvas>
## ⚙️ API
<Canvas>
<Story name="API playground" args={stories['API playground']}>
{Template.bind({})}
</Story>
</Canvas>
<details open>
<summary className="mdx-collapsible-section">
<p className="mdx-collapsible-section__chevron">
<strong>&gt;</strong>
</p>
<p className="mdx-collapsible-section__label">
<strong>Show/hide</strong> props
</p>
</summary>
<ArgsTable story="API playground" />
</details>
## 🧪 Testing
### 🧪 Scalability
<Canvas>
<Story name="Testing - Scalability" args={stories['Testing - Scalability']}>
{Template.bind({})}
</Story>
</Canvas>
### 🧪 Snapshot 📸
<Canvas>
<Story
name="Testing - Snapshot"
args={Object.keys(stories).reduce((accumulator, storyId) => ({
...accumulator,
[storyId]: {
...stories[storyId],
defaultOpen: true,
},
}))}
parameters={{
chromatic: { disableSnapshot: false },
}}
>
{CustomTemplateStoriesFactory(Template).bind({})}
</Story>
</Canvas>

View File

@ -1,24 +0,0 @@
import React from 'react';
import { ComponentMeta } from '@storybook/react';
import { ToolTip } from './Tooltip';
export default {
title: 'components/Tooltip',
component: ToolTip,
parameters: {
layout: 'centered',
},
} as ComponentMeta<typeof ToolTip>;
const longMessage =
'Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed est nulla, aliquet elementum lectus vitae, eleifend semper ante. Nam tempor sollicitudin erat vitae posuere. Vivamus lobortis lorem vitae mauris convallis fermentum. Donec semper tincidunt tincidunt. Etiam pretium eu elit sit amet posuere. Quisque et mollis sem. Etiam at nibh et turpis viverra fermentum sed consequat lectus. Vivamus porttitor a nulla id malesuada. Phasellus lorem neque, facilisis quis mollis eu, scelerisque a nulla. Donec non nisl tempor, lacinia ante ut, interdum arcu. Etiam in arcu arcu. Integer finibus scelerisque purus non maximus. Suspendisse potenti. Curabitur tincidunt, mauris sit amet mollis tristique, odio est posuere diam, sed lobortis tellus leo quis neque. Sed ornare magna ut lorem vehicula, id elementum lectus condimentum.';
export const Showcase = () => (
<div className="space-y-4">
<ToolTip message={longMessage} />
<ToolTip message="Lorem ipsum dolor sit amet." defaultOpen />
<ToolTip message="Lorem ipsum dolor sit amet." side="left" />
<ToolTip message="Lorem ipsum dolor sit amet." side="bottom" />
<ToolTip message="Lorem ipsum dolor sit amet." side="top" />
</div>
);

View File

@ -1,31 +1,37 @@
import React from 'react';
import * as Tooltip from '@radix-ui/react-tooltip';
import { FaQuestionCircle } from 'react-icons/fa';
import * as RadixTooltip from '@radix-ui/react-tooltip';
export type TooltipProps = {
message: string;
className?: string;
} & Pick<Tooltip.TooltipContentProps, 'side'> &
Pick<Tooltip.TooltipProps, 'defaultOpen'>;
/**
* The component children
*/
children: React.ReactNode;
/**
* The tooltip content children
*/
tooltipContentChildren: React.ReactNode;
} & Pick<RadixTooltip.TooltipContentProps, 'side'> &
Pick<RadixTooltip.TooltipProps, 'defaultOpen'>;
export const ToolTip: React.VFC<TooltipProps> = ({
message,
className,
export const Tooltip: React.VFC<TooltipProps> = ({
children,
tooltipContentChildren,
side = 'right',
defaultOpen = false,
}) => (
<Tooltip.Root delayDuration={0} defaultOpen={defaultOpen}>
<Tooltip.Trigger className="ml-xs flex">
<FaQuestionCircle
className={`h-4 text-muted cursor-pointer ${className}`}
/>
</Tooltip.Trigger>
<Tooltip.Content
<RadixTooltip.Root delayDuration={0} defaultOpen={defaultOpen}>
<RadixTooltip.Trigger
className="ml-xs inline"
data-testid="tooltip-trigger"
>
{children}
</RadixTooltip.Trigger>
<RadixTooltip.Content
side={side}
className="bg-gray-800 p-sm text-white rounded max-w-lg"
>
<Tooltip.Arrow className="fill-current text-gray-800" offset={5} />
{message}
</Tooltip.Content>
</Tooltip.Root>
<RadixTooltip.Arrow className="fill-current text-gray-800" offset={5} />
{tooltipContentChildren}
</RadixTooltip.Content>
</RadixTooltip.Root>
);

View File

@ -1 +1,2 @@
export * from './Tooltip';
export * from './IconTooltip';

View File

@ -1,9 +1,10 @@
import React from 'react';
export const TemplateStoriesFactory = (
Template: (stories: Record<string, any>) => React.ReactNode
Template: (stories: Record<string, any>) => React.ReactNode,
classNames = ''
) => (stories: Record<string, any>): React.ReactComponentElement<any> => (
<div>
<div className="w-full">
{Object.entries(stories)
// Only use objects as function are events handlers injected by storybook
.filter(
@ -15,7 +16,7 @@ export const TemplateStoriesFactory = (
<div className="text-black dark:text-white bg-gray-100 dark:bg-gray-700 underline p-2">
{storyName}
</div>
<div className="py-4">{Template(story)}</div>
<div className={`py-4 ${classNames}`}>{Template(story)}</div>
</div>
))}
</div>