diff --git a/packages/components/public/locales/fr/api.json b/packages/components/public/locales/fr/api.json index 7e061923..fa0adca5 100644 --- a/packages/components/public/locales/fr/api.json +++ b/packages/components/public/locales/fr/api.json @@ -60,5 +60,7 @@ "Select the localized catalog where the explain will be applied": "Sélection du catalogue localisé sur lequel l'explication' sera appliqué", "Indicate a term": "Indiquez un terme", "Search term": "Terme de recherche", - "Explain and compare": "Explication et comparaison" + "Explain and compare": "Explication et comparaison", + "Analyzer": "Analyseur", + "standard: The textual attribute for searching with possible fuzziness support (if spellcheck = yes).
reference: SKU-like, a sequence of numbers or letters, no understandable text, and no deduction, exact matching...
standard_edge_ngram: standard + support for searching the beginnings of words regardless of the word's length.": "standard: L'attribut textuel permettant une recherche avec un support possible pour les approximations (si spellcheck = yes).
reference:Type similaire à un SKU, une séquence de chiffres ou de lettres, sans texte compréhensible ni déduction, avec correspondance exacte ...
standard_edge_ngram: Standard + support pour rechercher les débuts de mots, quelle que soit la longueur du mot." } diff --git a/packages/components/src/components/atoms/form/InfoTooltip.tsx b/packages/components/src/components/atoms/form/InfoTooltip.tsx index f9a24186..83eb8786 100644 --- a/packages/components/src/components/atoms/form/InfoTooltip.tsx +++ b/packages/components/src/components/atoms/form/InfoTooltip.tsx @@ -7,11 +7,31 @@ interface IProps { title: string | NonNullable children?: ReactNode noStyle?: boolean + withHTMLTitle?: boolean } -function InfoTooltip({ title, children, noStyle }: IProps): JSX.Element { +function InfoTooltip({ + title, + children, + noStyle, + withHTMLTitle, +}: IProps): JSX.Element { return ( - + + ) : ( + title + ) + } + placement="right" + > {stickyHeader.label} + {stickyHeader.gridHeaderInfoTooltip?.trim() ? ( + + ) : null} ))} @@ -135,7 +142,9 @@ function CustomTableHeader(props: IProps): JSX.Element { whiteSpace: 'nowrap', ...((header.type === DataContentType.SCORE || header.type === DataContentType.PRICE) && { width: '10%' }), - ...(header.type === DataContentType.STOCK && { width: '15%' }), + ...(header.type === DataContentType.STOCK && { + width: '15%', + }), ...(header.type === DataContentType.STRING && { maxWidth: 'fit-content', }), @@ -143,6 +152,12 @@ function CustomTableHeader(props: IProps): JSX.Element { }} > {t(header.label)} + {header.gridHeaderInfoTooltip?.trim() ? ( + + ) : null} ))} diff --git a/packages/components/src/components/organisms/CustomTable/CustomTableRow/DraggableRow.tsx b/packages/components/src/components/organisms/CustomTable/CustomTableRow/DraggableRow.tsx index 80d1cbb2..cd026786 100644 --- a/packages/components/src/components/organisms/CustomTable/CustomTableRow/DraggableRow.tsx +++ b/packages/components/src/components/organisms/CustomTable/CustomTableRow/DraggableRow.tsx @@ -9,8 +9,8 @@ import { ITableHeader, ITableHeaderSticky, ITableRow, - getFieldState, getImageValue, + getPropsFromFieldState, } from '@elastic-suite/gally-admin-shared' import { @@ -130,7 +130,7 @@ function DraggableRow(props: IProps): JSX.Element { )} - {stickyHeaders.map((stickyHeader, i) => ( + {stickyHeaders.map(({ gridHeaderInfoTooltip, ...stickyHeader }, i) => ( ))} - {nonStickyHeaders.map((header) => { + {nonStickyHeaders.map(({ gridHeaderInfoTooltip, ...header }) => { const value = tableRow[header.name] && header.input === 'image' ? getImageValue( @@ -167,7 +167,6 @@ function DraggableRow(props: IProps): JSX.Element { tableRow[header.name] as IImage | string ) : tableRow[header.name] - return ( )} - {stickyHeaders.map((stickyHeader, i) => { - return ( - - - - ) - })} + {stickyHeaders.map(({ gridHeaderInfoTooltip, ...stickyHeader }, i) => ( + + + + ))} - {nonStickyHeaders.map((header) => { + {nonStickyHeaders.map(({ gridHeaderInfoTooltip, ...header }) => { const value = tableRow[header.name] && header.input === 'image' ? getImageValue( @@ -172,7 +168,7 @@ function NonDraggableRow(props: IProps): JSX.Element { onChange={handleChange} row={tableRow} value={value} - {...getFieldState( + {...getPropsFromFieldState( tableRow, header.depends, tableConfig[header.name] diff --git a/packages/components/src/components/stateful/FieldGuesser/EditableFieldGuesser.tsx b/packages/components/src/components/stateful/FieldGuesser/EditableFieldGuesser.tsx index 2047ee8f..ecac22d0 100644 --- a/packages/components/src/components/stateful/FieldGuesser/EditableFieldGuesser.tsx +++ b/packages/components/src/components/stateful/FieldGuesser/EditableFieldGuesser.tsx @@ -2,7 +2,6 @@ import React, { SyntheticEvent } from 'react' import { useTranslation } from 'next-i18next' import { DataContentType, - IDependsForm, IExpansions, IFieldGuesserProps, IOption, @@ -23,7 +22,6 @@ import { IDoubleDatePickerValues } from '../../atoms/form/DoubleDatePickerWithou import DoubleDatePicker from '../../atoms/form/DoubleDatePicker' import { Box } from '@mui/material' import RequestTypeManager from '../../stateful/RequestTypeManager/RequestTypeManager' -import { isHiddenDepends } from '../../../services' import RulesManager from '../RulesManager/RulesManager' import Slider from '../../atoms/form/Slider' import Synonym from '../../atoms/form/Synonym' @@ -34,7 +32,6 @@ function EditableFieldGuesser(props: IFieldGuesserProps): JSX.Element { const { diffValue, input, - disabled, label, multiple, name, @@ -47,7 +44,6 @@ function EditableFieldGuesser(props: IFieldGuesserProps): JSX.Element { suffix, type, validation, - depends, requestTypeConfigurations, data, optionConfig, @@ -56,6 +52,7 @@ function EditableFieldGuesser(props: IFieldGuesserProps): JSX.Element { error, helperText, replacementErrorsMessages, + disabled, } = props const { t } = useTranslation('common') @@ -87,17 +84,6 @@ function EditableFieldGuesser(props: IFieldGuesserProps): JSX.Element { } } - if (depends) { - const isHidden = isHiddenDepends( - depends instanceof Array ? (depends as IDependsForm[]) : [depends], - data - ) - - if (isHidden) { - return null - } - } - switch (input ?? type) { case DataContentType.NUMBER: case DataContentType.STRING: { diff --git a/packages/components/src/components/stateful/FieldGuesser/FieldGuesser.tsx b/packages/components/src/components/stateful/FieldGuesser/FieldGuesser.tsx index ca33a537..efbe17e0 100644 --- a/packages/components/src/components/stateful/FieldGuesser/FieldGuesser.tsx +++ b/packages/components/src/components/stateful/FieldGuesser/FieldGuesser.tsx @@ -1,15 +1,25 @@ import React from 'react' -import { IFieldGuesserProps } from '@elastic-suite/gally-admin-shared' +import { + IFieldGuesserProps, + getFieldState, +} from '@elastic-suite/gally-admin-shared' import EditableFieldGuesser from './EditableFieldGuesser' import ReadableFieldGuesser from './ReadableFieldGuesser' function FieldGuesser(props: IFieldGuesserProps): JSX.Element { const { editable, ...fieldProps } = props + const { visible, ...fieldStateProps } = getFieldState( + fieldProps.data, + fieldProps.depends + ) + + if (visible === false) return null + if (editable) { - return + return } - return + return } export default FieldGuesser diff --git a/packages/components/src/components/stateful/FieldGuesser/FormFieldGuesser.tsx b/packages/components/src/components/stateful/FieldGuesser/FormFieldGuesser.tsx index 560a554b..ba802eb7 100644 --- a/packages/components/src/components/stateful/FieldGuesser/FormFieldGuesser.tsx +++ b/packages/components/src/components/stateful/FieldGuesser/FormFieldGuesser.tsx @@ -15,6 +15,7 @@ interface IFormFieldGuesserProps function FormFieldGuesser(props: IFormFieldGuesserProps): JSX.Element { const { data, field, ...fieldProps } = props const value = useValue(field, data) + return } diff --git a/packages/components/src/hooks/useApiTable.ts b/packages/components/src/hooks/useApiTable.ts index f0a9babd..6d9390de 100644 --- a/packages/components/src/hooks/useApiTable.ts +++ b/packages/components/src/hooks/useApiTable.ts @@ -30,11 +30,12 @@ export function useApiHeadersForm( resource: IResource ): IFieldConfigFormWithFieldset[] { const apiHeaders = useApiHeaders(resource) + return useMemo(() => { const apiHeaderMap = apiHeaders.reduce< Record >( - (acc, header) => { + (acc, { gridHeaderInfoTooltip, ...header }) => { const fieldsetCode = header.fieldset const fieldset = resource.gally?.fieldset?.[fieldsetCode] if (fieldsetCode && fieldset) { diff --git a/packages/components/src/services/form.ts b/packages/components/src/services/form.ts index f1aaab9d..cfaf7ffa 100644 --- a/packages/components/src/services/form.ts +++ b/packages/components/src/services/form.ts @@ -1,5 +1,4 @@ import { - IDependsForm, IFetch, IFieldConfig, IHydraResponse, @@ -29,20 +28,6 @@ export function getDoubleDatePickerValue( return { fromDate: data?.fromDate, toDate: data?.toDate } } -export function isHiddenDepends( - dependsForm: IDependsForm[], - data: Record | undefined -): boolean { - return dependsForm.some((item) => { - const field = item?.field as string - const { value } = item - const fieldValue = data?.[field] - return ( - fieldValue === undefined || (value !== fieldValue && value !== undefined) - ) - }) -} - export function getRequestTypeData( data?: Record, limitationTypeOptionsApi?: IFetch> diff --git a/packages/shared/src/services/field.test.ts b/packages/shared/src/services/field.test.ts index 2360474a..b616225e 100644 --- a/packages/shared/src/services/field.test.ts +++ b/packages/shared/src/services/field.test.ts @@ -147,7 +147,7 @@ describe('Field service', () => { expect( getFieldState( { foo: 'bar', baz: true }, - { conditions: { baz: true }, disabled: true } + { conditions: { field: 'baz', value: false }, type: 'enabled' } ) ).toEqual({ disabled: true }) }) @@ -159,7 +159,7 @@ describe('Field service', () => { expect( getFieldState( { foo: 'bar', baz: true }, - { conditions: { baz: true }, disabled: true }, + { conditions: { field: 'baz', value: true }, type: 'enabled' }, { disabled: false } ) ).toEqual({ disabled: false }) diff --git a/packages/shared/src/services/field.ts b/packages/shared/src/services/field.ts index 2f6bbaea..5890b3f1 100644 --- a/packages/shared/src/services/field.ts +++ b/packages/shared/src/services/field.ts @@ -5,6 +5,7 @@ import { IFieldCondition, IFieldConfig, IFieldConfigFormWithFieldset, + IFieldDepends, IFieldState, } from '../types' @@ -59,24 +60,76 @@ export function isDropdownStaticOptions( return 'values' in options } -export function getFieldState( +/** + * It works like an "or" for the first level, but if we have an array at the second level then it works like an "and" (all conditions must be true) + */ +function isConditionActive( entity: Record, - depends?: IFieldCondition, + conditions: (IFieldCondition | IFieldCondition[])[] | IFieldCondition +): boolean { + if (!Array.isArray(conditions)) { + return entity[conditions.field] === conditions.value + } + let conditionActive = false + let index = 0 + while (!conditionActive && index < conditions.length) { + const item = conditions[index] + if (Array.isArray(item)) { + conditionActive = item.every( + ({ field, value }) => entity[field] === value + ) + } else { + const { field, value } = item + conditionActive = entity[field] === value + } + index++ + } + return conditionActive +} + +/** + * Allows to return a state based on conditions dependent on another field. + * The field [field name] depends on the condition [condition] to be [type]. + */ +export function getFieldState( + entity?: Record, + depends?: IFieldDepends, state: IFieldState = {} ): IFieldState { + if (!entity) return {} if (!depends?.conditions) { return state } - const { conditions, ...conditionalState } = depends - const conditionActive = Object.entries(conditions).every( - ([field, value]) => entity[field] === value - ) + const { conditions, type } = depends + + const newState: IFieldState = {} + + switch (type) { + case 'visible': + newState.visible = isConditionActive(entity, conditions) + break + case 'enabled': + newState.disabled = !isConditionActive(entity, conditions) + } + return { - ...(conditionActive && conditionalState), + ...newState, ...state, } } +/** + * return fieldState without the states that are not field props like "visible" + */ +export function getPropsFromFieldState( + entity?: Record, + depends?: IFieldDepends, + state: IFieldState = {} +): Omit { + const { visible, ...otherProps } = getFieldState(entity, depends, state) + return otherProps +} + export function isFieldConfigFormWithFieldset( fieldConfig: IFieldConfig | IFieldConfigFormWithFieldset ): fieldConfig is IFieldConfigFormWithFieldset { diff --git a/packages/shared/src/services/form.ts b/packages/shared/src/services/form.ts index 4595be9e..a3a87fc9 100644 --- a/packages/shared/src/services/form.ts +++ b/packages/shared/src/services/form.ts @@ -1,7 +1,6 @@ import { TFunction } from 'next-i18next' import { IExpansions, - IField, IRequestType, IRequestTypesOptions, ISearchLimitations, @@ -138,14 +137,3 @@ export function getExpansionsErrorMessages(expansions: IExpansions): string[] { export function areExpansionsValid(expansions: IExpansions): boolean { return getExpansionsErrorMessages(expansions).length === 0 } - -export function isDependsField( - field: IField, - data: Record -): boolean { - return ( - !field?.gally?.depends || - (field.gally.depends?.field in data && - data[field.gally.depends.field] === field.gally.depends?.value) - ) -} diff --git a/packages/shared/src/services/table.ts b/packages/shared/src/services/table.ts index 9f4cfded..c079aa5d 100644 --- a/packages/shared/src/services/table.ts +++ b/packages/shared/src/services/table.ts @@ -120,6 +120,7 @@ export function getFieldHeader(field: IField, t: TFunction): IFieldConfig { ...fieldConfig, editable: field.gally?.editable && field.writeable, fieldset: field.gally?.fieldset, + gridHeaderInfoTooltip: field.gally?.gridHeaderInfoTooltip, id, input, infoTooltip: field.gally?.infoTooltip, diff --git a/packages/shared/src/types/field.ts b/packages/shared/src/types/field.ts index c66e0fb6..23e38702 100644 --- a/packages/shared/src/types/field.ts +++ b/packages/shared/src/types/field.ts @@ -5,17 +5,23 @@ import { DataContentType, ITableRow } from './customTables' import { IMultipleInputConfiguration, IMultipleValueFormat } from './hydra' import { IOption, IOptions } from './option' -export interface IFieldCondition { - conditions: Record +export interface IFieldState { disabled?: boolean + visible?: boolean } -export interface IFieldState { - disabled?: boolean +export interface IFieldCondition { + field: string + value: any +} + +export interface IFieldDepends { + type: 'enabled' | 'visible' + conditions: (IFieldCondition | IFieldCondition[])[] | IFieldCondition } export interface IFieldConfig extends IFieldState { - depends?: any + depends?: IFieldDepends editable?: boolean field?: IField fieldset?: string @@ -42,6 +48,7 @@ export interface IFieldConfig extends IFieldState { cellsStyle?: CSSProperties showError?: boolean replacementErrorsMessages?: Record + gridHeaderInfoTooltip?: string } export interface IFieldConfigFormWithFieldset { diff --git a/packages/shared/src/types/hydra.ts b/packages/shared/src/types/hydra.ts index 4704912f..b8c2d51d 100644 --- a/packages/shared/src/types/hydra.ts +++ b/packages/shared/src/types/hydra.ts @@ -11,7 +11,7 @@ import { } from './jsonld' import { IOptions } from './option' import { IHydraSimpleCatalog } from './catalog' -import { IFieldGuesserProps } from './field' +import { IFieldDepends, IFieldGuesserProps } from './field' export enum HydraType { ARRAY = 'array', @@ -112,7 +112,7 @@ export interface IMultipleValueFormat { export interface IGallyProperty { context?: Record - depends?: any + depends?: IFieldDepends editable?: boolean input?: string options?: IDropdownOptions & (IDropdownStaticOptions | IDropdownApiOptions) @@ -134,6 +134,7 @@ export interface IGallyProperty { placeholder?: string defaultValue?: unknown showError?: boolean + gridHeaderInfoTooltip?: string } export interface IDropdownOptions { objectKeyValue?: string