feat(web): Add helper tooltips for inputs and link them to docs (#663)

* Fixed button border in settings/download-clients

Button border for "Add new" is now uniform with other "Add new"-buttons

* enhancement(docs) add helper tooltips

-  Add helper tooltips for inputs and link them to docs #161

* fix build error

* tooltips: changed positition

* hide tooltip below 640 width

* made tooltips better

now attaching to TextField names

* Added icon variation for MultiSelect and CheckboxField

* cleaned up old code

* refactor

Co-authored-by: ze0s <zze0s@users.noreply.github.com>

* added tooltips for DownloadClientForms

* added tooltips for indexerforms

* div for passwordfieldwide

* tooltips for details.tsx

* added tooltips to actions.tsx

* added tooltips to indexerforms.tsx

* replaced info icon with a more rudimentary one

* linting, removed duplicate tailwind display properties

* remove margin for flex centering

* fixed tooltip alignment on all fields

* add tooltip to PwFieldWide in indexer edit window

* refactor: simplify tooltips

* refactor: scope tooltip css

* refactor: tooltip default clickable

---------

Co-authored-by: martylukyy <35452459+martylukyy@users.noreply.github.com>
Co-authored-by: ze0s <43699394+zze0s@users.noreply.github.com>
This commit is contained in:
soup 2023-02-03 17:03:49 +01:00 committed by GitHub
parent a50332394c
commit 3fdd7cf5e4
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
15 changed files with 256 additions and 90 deletions

View file

@ -1,5 +1,6 @@
import { Field, FieldProps } from "formik";
import { classNames } from "../../utils";
import { CustomTooltip } from "../tooltips/CustomTooltip";
interface ErrorFieldProps {
name: string;
@ -16,17 +17,29 @@ const ErrorField = ({ name, classNames }: ErrorFieldProps) => (
</div>
);
interface RequiredFieldProps {
required?: boolean
}
const RequiredField = ({ required }: RequiredFieldProps) => (
<>
{required && <span className="ml-1 text-red-500">*</span>}
</>
);
interface CheckboxFieldProps {
name: string;
label: string;
sublabel?: string;
disabled?: boolean;
tooltip?: JSX.Element;
}
const CheckboxField = ({
name,
label,
sublabel,
tooltip,
disabled
}: CheckboxFieldProps) => (
<div className="relative flex items-start">
@ -43,12 +56,17 @@ const CheckboxField = ({
/>
</div>
<div className="ml-3 text-sm">
<label htmlFor={name} className="font-medium text-gray-900 dark:text-gray-100">
{label}
<label htmlFor={name} className="flex mb-2 text-xs font-bold text-gray-700 dark:text-gray-200 uppercase tracking-wide">
<div className="flex">
{label}
{tooltip && (
<CustomTooltip anchorId={name}>{tooltip}</CustomTooltip>
)}
</div>
</label>
<p className="text-gray-500">{sublabel}</p>
</div>
</div>
);
export { ErrorField, CheckboxField };
export { ErrorField, RequiredField, CheckboxField };

View file

@ -2,6 +2,7 @@ import { Field, FieldProps } from "formik";
import { classNames } from "../../utils";
import { EyeIcon, EyeSlashIcon } from "@heroicons/react/24/solid";
import { useToggle } from "../../hooks/hooks";
import { CustomTooltip } from "../tooltips/CustomTooltip";
type COL_WIDTHS = 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12;
@ -14,6 +15,7 @@ interface TextFieldProps {
autoComplete?: string;
hidden?: boolean;
disabled?: boolean;
tooltip?: JSX.Element;
}
export const TextField = ({
@ -24,6 +26,7 @@ export const TextField = ({
columns,
autoComplete,
hidden,
tooltip,
disabled
}: TextFieldProps) => (
<div
@ -33,8 +36,13 @@ export const TextField = ({
)}
>
{label && (
<label htmlFor={name} className="block text-xs font-bold text-gray-700 dark:text-gray-200 uppercase tracking-wide">
{label}
<label htmlFor={name} className="flex float-left mb-2 text-xs font-bold text-gray-700 dark:text-gray-200 uppercase tracking-wide">
<div className="flex">
{label}
{tooltip && (
<CustomTooltip anchorId={name}>{tooltip}</CustomTooltip>
)}
</div>
</label>
)}
<Field name={name}>
@ -45,7 +53,7 @@ export const TextField = ({
<div>
<input
{...field}
id={name}
name={name}
type="text"
defaultValue={defaultValue}
autoComplete={autoComplete}
@ -207,6 +215,7 @@ interface NumberFieldProps {
required?: boolean;
min?: number;
max?: number;
tooltip?: JSX.Element;
}
export const NumberField = ({
@ -216,12 +225,18 @@ export const NumberField = ({
step,
min,
max,
tooltip,
disabled,
required
}: NumberFieldProps) => (
<div className="col-span-12 sm:col-span-6">
<label htmlFor={name} className="block text-xs font-bold text-gray-700 dark:text-gray-200 uppercase tracking-wide">
{label}
<label htmlFor={name} className="flex float-left mb-2 text-xs font-bold text-gray-700 dark:text-gray-200 uppercase tracking-wide">
<div className="flex">
{label}
{tooltip && (
<CustomTooltip anchorId={name}>{tooltip}</CustomTooltip>
)}
</div>
</label>
<Field name={name} type="number">

View file

@ -4,9 +4,10 @@ import { classNames } from "../../utils";
import { useToggle } from "../../hooks/hooks";
import { EyeIcon, EyeSlashIcon } from "@heroicons/react/24/solid";
import { Switch } from "@headlessui/react";
import { ErrorField } from "./common";
import { ErrorField, RequiredField } from "./common";
import Select, { components, ControlProps, InputProps, MenuProps, OptionProps } from "react-select";
import { SelectFieldProps } from "./select";
import { CustomTooltip } from "../tooltips/CustomTooltip";
interface TextFieldWideProps {
name: string;
@ -16,6 +17,7 @@ interface TextFieldWideProps {
defaultValue?: string;
required?: boolean;
hidden?: boolean;
tooltip?: JSX.Element;
validate?: FieldValidator;
}
@ -26,13 +28,17 @@ export const TextFieldWide = ({
placeholder,
defaultValue,
required,
tooltip,
hidden,
validate
}: TextFieldWideProps) => (
<div hidden={hidden} className="space-y-1 p-4 sm:space-y-0 sm:grid sm:grid-cols-3 sm:gap-4">
<div>
<label htmlFor={name} className="block text-sm font-medium text-gray-900 dark:text-white sm:mt-px sm:pt-2">
{label} {required && <span className="text-gray-500">*</span>}
<label htmlFor={name} className="flex text-sm font-medium text-gray-900 dark:text-white sm:mt-px sm:pt-2">
<div className="flex">
{label} {tooltip && (<CustomTooltip anchorId={name}>{tooltip}</CustomTooltip>)}
<RequiredField required={required} />
</div>
</label>
</div>
<div className="sm:col-span-2">
@ -71,6 +77,7 @@ interface PasswordFieldWideProps {
help?: string;
required?: boolean;
defaultVisible?: boolean;
tooltip?: JSX.Element;
validate?: FieldValidator;
}
@ -82,6 +89,7 @@ export const PasswordFieldWide = ({
help,
required,
defaultVisible,
tooltip,
validate
}: PasswordFieldWideProps) => {
const [isVisible, toggleVisibility] = useToggle(defaultVisible);
@ -89,8 +97,11 @@ export const PasswordFieldWide = ({
return (
<div className="space-y-1 p-4 sm:space-y-0 sm:grid sm:grid-cols-3 sm:gap-4">
<div>
<label htmlFor={name} className="block text-sm font-medium text-gray-900 dark:text-white sm:mt-px sm:pt-2">
{label} {required && <span className="text-gray-500">*</span>}
<label htmlFor={name} className="flex text-sm font-medium text-gray-900 dark:text-white sm:mt-px sm:pt-2">
<div className="flex">
{label} {tooltip && (<CustomTooltip anchorId={name}>{tooltip}</CustomTooltip>)}
<RequiredField required={required} />
</div>
</label>
</div>
<div className="sm:col-span-2">
@ -132,6 +143,7 @@ interface NumberFieldWideProps {
placeholder?: string;
defaultValue?: number;
required?: boolean;
tooltip?: JSX.Element;
}
export const NumberFieldWide = ({
@ -140,6 +152,7 @@ export const NumberFieldWide = ({
placeholder,
help,
defaultValue,
tooltip,
required
}: NumberFieldWideProps) => (
<div className="px-4 space-y-1 sm:space-y-0 sm:grid sm:grid-cols-3 sm:gap-4 sm:py-4">
@ -148,7 +161,10 @@ export const NumberFieldWide = ({
htmlFor={name}
className="block text-sm font-medium text-gray-900 dark:text-white sm:mt-px sm:pt-2"
>
{label} {required && <span className="text-gray-500">*</span>}
<div className="flex">
{label} {tooltip && (<CustomTooltip anchorId={name}>{tooltip}</CustomTooltip>)}
<RequiredField required={required} />
</div>
</label>
</div>
<div className="sm:col-span-2">
@ -187,12 +203,14 @@ interface SwitchGroupWideProps {
description?: string;
defaultValue?: boolean;
className?: string;
tooltip?: JSX.Element;
}
export const SwitchGroupWide = ({
name,
label,
description,
tooltip,
defaultValue
}: SwitchGroupWideProps) => (
<ul className="mt-2 px-4 divide-y divide-gray-200 dark:divide-gray-700">
@ -200,7 +218,9 @@ export const SwitchGroupWide = ({
<div className="flex flex-col">
<Switch.Label as="p" className="text-sm font-medium text-gray-900 dark:text-white"
passive>
{label}
<div className="flex">
{label} {tooltip && (<CustomTooltip anchorId={name}>{tooltip}</CustomTooltip>)}
</div>
</Switch.Label>
{description && (
<Switch.Description className="text-sm text-gray-500 dark:text-gray-700">
@ -350,15 +370,19 @@ export const SelectFieldWide = ({
name,
label,
optionDefaultText,
tooltip,
options
}: SelectFieldProps) => (
<div className="flex items-center justify-between space-y-1 px-4 py-4 sm:space-y-0 sm:grid sm:grid-cols-3 sm:gap-4">
<div>
<label
htmlFor={name}
className="block text-sm font-medium text-gray-900 dark:text-white"
className="flex text-sm font-medium text-gray-900 dark:text-white"
>
{label}
<div className="flex">
{label}
{tooltip && (<CustomTooltip anchorId={name}>{tooltip}</CustomTooltip>)}
</div>
</label>
</div>
<div className="sm:col-span-2">

View file

@ -1,4 +1,4 @@
import { Fragment } from "react";
import React, { Fragment } from "react";
import { Field, FieldProps } from "formik";
import { Listbox, Transition } from "@headlessui/react";
import { CheckIcon, ChevronUpDownIcon } from "@heroicons/react/24/solid";
@ -6,6 +6,7 @@ import { MultiSelect as RMSC } from "react-multi-select-component";
import { classNames, COL_WIDTHS } from "../../utils";
import { SettingsContext } from "../../utils/Context";
import { CustomTooltip } from "../tooltips/CustomTooltip";
export interface MultiSelectOption {
value: string | number;
@ -21,6 +22,7 @@ interface MultiSelectProps {
columns?: COL_WIDTHS;
creatable?: boolean;
disabled?: boolean;
tooltip?: JSX.Element;
}
export const MultiSelect = ({
@ -29,6 +31,7 @@ export const MultiSelect = ({
options,
columns,
creatable,
tooltip,
disabled
}: MultiSelectProps) => {
const settingsContext = SettingsContext.useValue();
@ -46,10 +49,13 @@ export const MultiSelect = ({
)}
>
<label
className="block mb-2 text-xs font-bold tracking-wide text-gray-700 uppercase dark:text-gray-200"
htmlFor={label}
>
{label}
htmlFor={label} className="flex mb-2 text-xs font-bold tracking-wide text-gray-700 uppercase dark:text-gray-200">
<div className="flex">
{label}
{tooltip && (
<CustomTooltip anchorId={name}>{tooltip}</CustomTooltip>
)}
</div>
</label>
<Field name={name} type="select" multiple={true}>
@ -155,7 +161,7 @@ export function DownloadClientSelect({
{({ open }) => (
<>
<Listbox.Label className="block text-xs font-bold text-gray-700 dark:text-gray-200 uppercase tracking-wide">
Client
Client
</Listbox.Label>
<div className="mt-2 relative">
<Listbox.Button className="bg-white dark:bg-gray-800 relative w-full border border-gray-300 dark:border-gray-700 rounded-md shadow-sm pl-3 pr-10 py-2 text-left cursor-default focus:outline-none focus:ring-1 focus:ring-blue-500 dark:focus:ring-blue-500 focus:border-blue-500 dark:focus:border-blue-500 dark:text-gray-200 sm:text-sm">
@ -244,11 +250,13 @@ export interface SelectFieldProps {
label: string;
optionDefaultText: string;
options: SelectFieldOption[];
tooltip?: JSX.Element;
}
export const Select = ({
name,
label,
tooltip,
optionDefaultText,
options
}: SelectFieldProps) => {
@ -265,8 +273,13 @@ export const Select = ({
>
{({ open }) => (
<>
<Listbox.Label className="block text-xs font-bold text-gray-700 dark:text-gray-200 uppercase tracking-wide">
{label}
<Listbox.Label className="flex float-left mb-2 text-xs font-bold text-gray-700 dark:text-gray-200 uppercase tracking-wide">
<div className="flex">
{label}
{tooltip && (
<CustomTooltip anchorId={name}>{tooltip}</CustomTooltip>
)}
</div>
</Listbox.Label>
<div className="mt-2 relative">
<Listbox.Button className="bg-white dark:bg-gray-800 relative w-full border border-gray-300 dark:border-gray-700 rounded-md shadow-sm pl-3 pr-10 py-2.5 text-left cursor-default focus:outline-none focus:ring-1 focus:ring-blue-500 dark:focus:ring-blue-500 focus:border-blue-500 dark:focus:border-blue-500 dark:text-gray-200 sm:text-sm">

View file

@ -3,6 +3,7 @@ import type { FieldInputProps, FieldMetaProps, FieldProps, FormikProps, FormikVa
import { Field } from "formik";
import { Switch as HeadlessSwitch } from "@headlessui/react";
import { classNames } from "../../utils";
import { CustomTooltip } from "../tooltips/CustomTooltip";
type SwitchProps<V = unknown> = {
label?: string
@ -69,19 +70,26 @@ interface SwitchGroupProps {
description?: string;
className?: string;
heading?: boolean;
tooltip?: JSX.Element;
}
const SwitchGroup = ({
name,
label,
description,
tooltip,
heading
}: SwitchGroupProps) => (
<HeadlessSwitch.Group as="ol" className="py-4 flex items-center justify-between">
{label && <div className="flex flex-col">
<HeadlessSwitch.Label as={heading ? "h2" : "p"} className={classNames("font-medium text-gray-900 dark:text-gray-100", heading ? "text-lg" : "text-sm")}
<HeadlessSwitch.Label as={heading ? "h2" : "p"} className={classNames("flex float-left cursor-default mb-2 text-xs font-bold text-gray-700 dark:text-gray-200 uppercase tracking-wide", heading ? "text-lg" : "text-sm")}
passive>
{label}
<div className="flex">
{label}
{tooltip && (
<CustomTooltip anchorId={name}>{tooltip}</CustomTooltip>
)}
</div>
</HeadlessSwitch.Label>
{description && (
<HeadlessSwitch.Description className="text-sm mt-1 text-gray-500 dark:text-gray-400">

View file

@ -0,0 +1,11 @@
.react-tooltip code {
background-color: rgb(63, 71, 94);
padding-left: 4px;
padding-right: 4px;
padding-top: 2px;
padding-bottom: 2px;
}
.react-tooltip a:hover {
text-decoration-line: underline;
}

View file

@ -0,0 +1,27 @@
import { PlacesType, Tooltip } from "react-tooltip";
import "./CustomTooltip.css";
interface CustomTooltipProps {
anchorId: string;
children?: React.ReactNode;
clickable?: boolean;
place?: PlacesType;
}
export const CustomTooltip = ({
anchorId,
children,
clickable = true,
place = "top"
}: CustomTooltipProps) => {
const id = `${anchorId}-tooltip`;
return (
<div className="flex items-center">
<svg id={id} className="ml-1 w-4 h-4" viewBox="0 0 72 72"><path d="M32 2C15.432 2 2 15.432 2 32s13.432 30 30 30s30-13.432 30-30S48.568 2 32 2m5 49.75H27v-24h10v24m-5-29.5a5 5 0 1 1 0-10a5 5 0 0 1 0 10" fill="currentcolor"/></svg>
<Tooltip style= {{ maxWidth: "350px", fontSize: "12px", textTransform: "none", fontWeight: "normal", borderRadius: "0.375rem", backgroundColor: "#34343A", color: "#fff", opacity: "1" }} delayShow={100} delayHide={150} place={place} anchorId={id} data-html={true} clickable={clickable}>
{children}
</Tooltip>
</div>
);
};