/** * WordPress dependencies */ import { SelectControl } from '@safe-wordpress/components'; import { useSelect } from '@safe-wordpress/data'; import { _x, sprintf } from '@safe-wordpress/i18n'; /** * External dependencies */ import { find, isArray, map, noop } from 'lodash'; import { store as NAB_EXPERIMENTS } from '@nab/experiments'; import { isEmpty } from '@nab/utils'; import type { ExperimentTypeName, Maybe } from '@nab/types'; export type PresetAlternativeProps = { readonly experimentType: ExperimentTypeName; readonly value: Maybe< string >; readonly onChange: ( val: Maybe< Value > ) => void; readonly collection?: 'default' | 'nab-check-availability' | string; readonly disabled?: boolean; readonly additionalValues?: { readonly before?: ReadonlyArray< Value >; readonly after?: ReadonlyArray< Value >; }; readonly exclusions?: ReadonlyArray< string >; readonly labels?: { readonly selection?: string; readonly selectAction?: string; readonly loading?: string; readonly empty?: string; readonly unknownOption?: string; }; }; type Value = { readonly value: string; readonly label: string; readonly disabled?: boolean; }; export const PresetAlternative = ( props: PresetAlternativeProps ): JSX.Element => { const { experimentType, collection = 'default', value, disabled, onChange, additionalValues, exclusions = [], labels: { selection = '', selectAction: selectActionText, loading: loadingPlaceholder = _x( 'Loading…', 'text', 'nelio-ab-testing' ), empty: emptyPlaceholder = _x( 'No options available', 'text', 'nelio-ab-testing' ), unknownOption: unknownOptionWarning = _x( 'Option not found', 'text', 'nelio-ab-testing' ), } = {}, } = props; const presetAlternatives = usePresetAlternatives( experimentType, collection, additionalValues, exclusions ); if ( ! presetAlternatives ) { return ( ); } if ( isEmpty( presetAlternatives ) ) { return ( ); } let options = presetAlternatives; if ( value && ! map( options, 'value' ).includes( value ) ) { options = [ { value, label: !! selection ? sprintf( /* translators: %1$s: Unknown option message as in “Option not found”. %2$s: Option name as, for instance, “Twenty Nineteen”. */ _x( '%1$s (%2$s)', 'text (invalid option)', 'nelio-ab-testing' ), unknownOptionWarning, selection ) : unknownOptionWarning, }, ...options, ]; } if ( selectActionText ) { options = [ { disabled: true, value: '_nab_no_option_selected' as const, label: selectActionText, }, ...options, ]; } return ( onChange( find( presetAlternatives, { value: id } ) ) } options={ options } disabled={ disabled } /> ); }; // ===== // HOOKS // ===== function usePresetAlternatives( experimentType: ExperimentTypeName, collection: string, additionalValues: PresetAlternativeProps[ 'additionalValues' ], exclusions: ReadonlyArray< string > ): Maybe< ReadonlyArray< Value > > { const presetAlts = useSelect( ( select ) => { const { getExperimentSupport } = select( NAB_EXPERIMENTS ); const presetAlternatives = getExperimentSupport( experimentType, 'presetAlternatives' ); return presetAlternatives?.( select, collection ); }, [ collection, experimentType ] ); const { before = [], after = [] } = additionalValues ?? {}; if ( ! isArray( presetAlts ) ) { return before.length + after.length ? [ ...before, ...after ] : undefined; } const result = before.length + after.length ? [ ...before, ...( presetAlts as ReadonlyArray< Value > ), ...after, ] : ( presetAlts as ReadonlyArray< Value > ); return exclusions.length ? result.filter( ( a ) => ! exclusions.includes( a.value ) ) : result; }