From 43f3e199e9fbdb231a1e766054a5884275108491 Mon Sep 17 00:00:00 2001 From: Birk Johansson Date: Wed, 11 Dec 2024 17:35:00 +0100 Subject: [PATCH] fix: cleanup and rename modelsingleselectlegacy --- .../SearchableSingleSelect.tsx | 9 +- .../CategoryComboSelect.tsx | 20 +- .../BaseModelSingleSelect.tsx | 15 +- .../ModelSingleSelect/ModelSingleSelect.tsx | 274 ++++++------------ .../ModelSingleSelectField.tsx | 2 +- .../ModelSingleSelectLegacy.tsx | 197 +++++++++++++ .../ModelSingleSelectRefactor.tsx | 101 ------- .../ModelSingleSelect/index.ts | 3 + .../OptionSetSelect/OptionSetSelect.tsx | 4 +- src/lib/form/createJsonPatchOperations.ts | 2 +- .../fields/CategoryComboField.tsx | 53 +++- 11 files changed, 362 insertions(+), 318 deletions(-) create mode 100644 src/components/metadataFormControls/ModelSingleSelect/ModelSingleSelectLegacy.tsx delete mode 100644 src/components/metadataFormControls/ModelSingleSelect/ModelSingleSelectRefactor.tsx diff --git a/src/components/SearchableSingleSelect/SearchableSingleSelect.tsx b/src/components/SearchableSingleSelect/SearchableSingleSelect.tsx index 02024f32..a1544583 100644 --- a/src/components/SearchableSingleSelect/SearchableSingleSelect.tsx +++ b/src/components/SearchableSingleSelect/SearchableSingleSelect.tsx @@ -145,16 +145,9 @@ export const SearchableSingleSelect = ({ value={filter} onChange={({ value }) => setFilterValue(value ?? '')} placeholder={i18n.t('Filter options')} + type='search' /> - - {withAllOptions.map(({ value, label }) => ( diff --git a/src/components/metadataFormControls/CategoryComboSelect/CategoryComboSelect.tsx b/src/components/metadataFormControls/CategoryComboSelect/CategoryComboSelect.tsx index 93142207..c213c760 100644 --- a/src/components/metadataFormControls/CategoryComboSelect/CategoryComboSelect.tsx +++ b/src/components/metadataFormControls/CategoryComboSelect/CategoryComboSelect.tsx @@ -1,12 +1,13 @@ import i18n from '@dhis2/d2-i18n' -import React from 'react' +import React, { useCallback } from 'react' +import { DEFAULT_CATEGORY_COMBO } from '../../../lib' import { PlainResourceQuery } from '../../../types' import { CategoryCombo } from '../../../types/generated' import { DisplayableModel } from '../../../types/models' import { ModelSingleSelect, ModelSingleSelectProps, -} from '../ModelSingleSelect/ModelSingleSelectRefactor' +} from '../ModelSingleSelect/ModelSingleSelect' export const categoryCombosSelectQuery = { resource: 'categoryCombos', @@ -33,7 +34,20 @@ export const CategoryComboSelect = < ...modelSingleSelectProps }: CategoryComboSelectProps) => { const resolvedQuery = query ?? categoryCombosSelectQuery + // add defaultCatcombo (None) to the list of categoryCombos + const transform = useCallback( + (value: TCategoryCombo[]) => [ + DEFAULT_CATEGORY_COMBO as TCategoryCombo, + ...value, + ], + [] + ) + return ( - + + query={resolvedQuery} + transform={transform} + {...modelSingleSelectProps} + /> ) } diff --git a/src/components/metadataFormControls/ModelSingleSelect/BaseModelSingleSelect.tsx b/src/components/metadataFormControls/ModelSingleSelect/BaseModelSingleSelect.tsx index 86306025..b0a8207d 100644 --- a/src/components/metadataFormControls/ModelSingleSelect/BaseModelSingleSelect.tsx +++ b/src/components/metadataFormControls/ModelSingleSelect/BaseModelSingleSelect.tsx @@ -1,3 +1,4 @@ +import i18n from '@dhis2/d2-i18n' import React, { useCallback, useMemo } from 'react' import { DisplayableModel } from '../../../types/models' import { @@ -5,16 +6,17 @@ import { SearchableSingleSelectPropTypes, } from '../../SearchableSingleSelect' + const toDisplayOption = (model: DisplayableModel) => ({ value: model.id, - label: model.displayName, + label: model.displayName || i18n.t('Loading...'), }) type OwnProps = { selected?: TModel available: TModel[] onChange: (selected: TModel | undefined) => void - showNoValueOption?: boolean + noValueOption?: { value: string; label: string } | boolean } export type BaseModelSingleSelectProps = Omit< @@ -28,7 +30,7 @@ export const BaseModelSingleSelect = ({ available, selected, onChange, - showNoValueOption, + noValueOption, ...searchableSingleSelectProps }: BaseModelSingleSelectProps) => { const { allModelsMap, allSingleSelectOptions } = useMemo(() => { @@ -40,15 +42,16 @@ export const BaseModelSingleSelect = ({ const allSingleSelectOptions = Array.from(allModelsMap).map( ([, value]) => toDisplayOption(value) ) - if (showNoValueOption) { - allSingleSelectOptions.unshift({ value: '', label: '' }) + if (noValueOption) { + const option = noValueOption === true ? { value: '', label: i18n.t('') } : noValueOption + allSingleSelectOptions.unshift(option) } return { allModelsMap, allSingleSelectOptions, } - }, [available, selected, showNoValueOption]) + }, [available, selected, noValueOption]) const handleOnChange: SearchableSingleSelectPropTypes['onChange'] = useCallback( diff --git a/src/components/metadataFormControls/ModelSingleSelect/ModelSingleSelect.tsx b/src/components/metadataFormControls/ModelSingleSelect/ModelSingleSelect.tsx index 1288e696..9bed173d 100644 --- a/src/components/metadataFormControls/ModelSingleSelect/ModelSingleSelect.tsx +++ b/src/components/metadataFormControls/ModelSingleSelect/ModelSingleSelect.tsx @@ -1,197 +1,101 @@ -import i18n from '@dhis2/d2-i18n' -import React, { - forwardRef, - useCallback, - useImperativeHandle, - useRef, - useState, -} from 'react' -import { SelectOption, QueryResponse } from '../../../types' -import { Pager } from '../../../types/generated' -import { SearchableSingleSelect } from '../../SearchableSingleSelect' - -function computeDisplayOptions({ - selected, - selectedOption: _selectedOption, - required, - options, -}: { - options: SelectOption[] - selected?: string - required?: boolean - selectedOption?: SelectOption -}): SelectOption[] { - let selectedOption = _selectedOption - if (!_selectedOption && selected) { - const foundOption = options.find((option) => option.value === selected) - - // This happens only when we haven't fetched the lable for an initially - // selected value. Don't show anything to prevent error that an option is - // missing - if (!foundOption) { - return [] - } - - selectedOption = foundOption - } - - const optionsContainSelected = options?.find( - ({ value }) => value === selected - ) - - const withSelectedOption = - selectedOption && !optionsContainSelected - ? [...options, selectedOption] - : options - - if (!required) { - // This default value has been copied from the old app - return [ - { value: '', label: i18n.t('') }, - ...withSelectedOption, - ] - } - - return withSelectedOption +import React, { useMemo, useState } from 'react' +import { useInfiniteQuery } from 'react-query' +import { useDebouncedCallback } from 'use-debounce' +import { useBoundResourceQueryFn } from '../../../lib/query/useBoundQueryFn' +import { PlainResourceQuery } from '../../../types' +import { PagedResponse } from '../../../types/generated' +import { DisplayableModel } from '../../../types/models' +import { + BaseModelSingleSelect, + BaseModelSingleSelectProps, +} from './BaseModelSingleSelect' + +type Response = PagedResponse + +const defaultQuery = { + params: { + order: 'displayName:asc', + fields: ['id', 'displayName'], + pageSize: 10, + }, +} satisfies Omit + +export type ModelSingleSelectProps = Omit< + BaseModelSingleSelectProps, + | 'available' + | 'onFilterChange' + | 'onRetryClick' + | 'onEndReached' + | 'showEndLoader' + | 'loading' + | 'error' +> & { + query: Omit + onFilterChange?: (value: string) => void + transform?: (value: TModel[]) => TModel[] } -type UseInitialOptionQuery = ({ +export const ModelSingleSelect = ({ selected, - onComplete, -}: { - onComplete: (option: SelectOption) => void - selected?: string -}) => QueryResponse - -export interface ModelSingleSelectProps { - onChange: ({ selected }: { selected: string }) => void - required?: boolean - disabled?: boolean - invalid?: boolean - placeholder?: string - selected?: string - showAllOption?: boolean - onBlur?: () => void - onFocus?: () => void - useInitialOptionQuery: UseInitialOptionQuery - useOptionsQuery: () => QueryResponse -} - -export const ModelSingleSelectLegacy = forwardRef(function ModelSingleSelect( - { - onChange, - invalid, - disabled, - placeholder = '', - required, - selected, - showAllOption, - onBlur, - onFocus, - useInitialOptionQuery, - useOptionsQuery, - }: ModelSingleSelectProps, - ref -) { - // Using a ref because we don't want to react to changes. - // We're using this value only when imperatively calling `refetch`, - // nothing that depends on the render-cycle depends on this value - const filterRef = useRef('') - const pageRef = useRef(0) - - // We need to persist the selected option so we can display an