From cc66cbdae710a434fafdfb1f2efe78413ba83a9c Mon Sep 17 00:00:00 2001 From: Jan-Gerke Salomon Date: Wed, 4 Oct 2023 10:47:28 +0200 Subject: [PATCH 01/14] fix(edit de): allow removing nested values --- src/pages/dataElements/New.tsx | 6 ++++-- src/pages/dataElements/edit/createJsonPatchOperations.ts | 2 +- 2 files changed, 5 insertions(+), 3 deletions(-) diff --git a/src/pages/dataElements/New.tsx b/src/pages/dataElements/New.tsx index 0a352f2f..35251698 100644 --- a/src/pages/dataElements/New.tsx +++ b/src/pages/dataElements/New.tsx @@ -26,8 +26,10 @@ function computeInitialValues(customAttributes: Attribute[]) { code: '', description: '', url: '', - color: '', - icon: '', + style: { + color: '', + icon: '', + }, fieldMask: '', domainType: 'AGGREGATE', formName: '', diff --git a/src/pages/dataElements/edit/createJsonPatchOperations.ts b/src/pages/dataElements/edit/createJsonPatchOperations.ts index 11feec85..c8937c37 100644 --- a/src/pages/dataElements/edit/createJsonPatchOperations.ts +++ b/src/pages/dataElements/edit/createJsonPatchOperations.ts @@ -46,6 +46,6 @@ export function createJsonPatchOperations({ return adjustedDirtyFieldsKeys.map((name) => ({ op: get(name, dataElement) ? 'replace' : 'add', path: `/${name.replace(/[.]/g, '/')}`, - value: get(name, values), + value: get(name, values) || '', })) } From f557dd0f392cf6cafbab0a199f7d1c138a1c6ff1 Mon Sep 17 00:00:00 2001 From: Jan-Gerke Salomon Date: Wed, 4 Oct 2023 12:57:19 +0200 Subject: [PATCH 02/14] fix(searchable single selects): add posibility to deselect --- .../CategoryComboSelect.tsx | 15 ++++++------ .../CategoryComboSelect/useOptionsQuery.ts | 6 ++++- .../ModelSingleSelect/ModelSingleSelect.tsx | 23 ++++++++++++------- .../ModelSingleSelect/index.ts | 2 +- .../OptionSetSelect/OptionSetSelect.tsx | 15 ++++++------ src/pages/dataElements/New.tsx | 1 - .../dataElements/form/CustomAttributes.tsx | 5 ++-- src/pages/dataElements/form/customFields.tsx | 1 + 8 files changed, 38 insertions(+), 30 deletions(-) diff --git a/src/components/metadataFormControls/CategoryComboSelect/CategoryComboSelect.tsx b/src/components/metadataFormControls/CategoryComboSelect/CategoryComboSelect.tsx index c2d1a050..869f7e2a 100644 --- a/src/components/metadataFormControls/CategoryComboSelect/CategoryComboSelect.tsx +++ b/src/components/metadataFormControls/CategoryComboSelect/CategoryComboSelect.tsx @@ -1,22 +1,20 @@ import i18n from '@dhis2/d2-i18n' import React, { forwardRef } from 'react' import { ModelSingleSelect } from '../ModelSingleSelect' +import type { ModelSingleSelectProps } from '../ModelSingleSelect' import { useInitialOptionQuery } from './useInitialOptionQuery' import { useOptionsQuery } from './useOptionsQuery' -interface CategoryComboSelectProps { - onChange: ({ selected }: { selected: string }) => void - placeholder?: string - selected?: string - showAllOption?: boolean - onBlur?: () => void - onFocus?: () => void -} +type CategoryComboSelectProps = Omit< + ModelSingleSelectProps, + 'useInitialOptionQuery' | 'useOptionsQuery' +> export const CategoryComboSelect = forwardRef(function CategoryComboSelect( { onChange, placeholder = i18n.t('Category combo'), + required, selected, showAllOption, onBlur, @@ -27,6 +25,7 @@ export const CategoryComboSelect = forwardRef(function CategoryComboSelect( return ( value === selected ) - if (selectedOption && !optionsContainSelected) { - return [...options, selectedOption] + const withSelectedOption = + selectedOption && !optionsContainSelected + ? [...options, selectedOption] + : options + + if (!required) { + return [{ value: '', label: i18n.t('None') }, ...withSelectedOption] } - return options + return withSelectedOption } type UseInitialOptionQuery = ({ @@ -44,8 +52,9 @@ type UseInitialOptionQuery = ({ selected?: string }) => QueryResponse -interface ModelSingleSelectProps { +export interface ModelSingleSelectProps { onChange: ({ selected }: { selected: string }) => void + required?: boolean placeholder?: string selected?: string showAllOption?: boolean @@ -55,14 +64,11 @@ interface ModelSingleSelectProps { useOptionsQuery: () => QueryResponse } -export interface ModelSingleSelectHandle { - refetch: () => void -} - export const ModelSingleSelect = forwardRef(function ModelSingleSelect( { onChange, placeholder = '', + required, selected, showAllOption, onBlur, @@ -134,6 +140,7 @@ export const ModelSingleSelect = forwardRef(function ModelSingleSelect( const displayOptions = computeDisplayOptions({ selected, selectedOption, + required, options: result, }) diff --git a/src/components/metadataFormControls/ModelSingleSelect/index.ts b/src/components/metadataFormControls/ModelSingleSelect/index.ts index 077b733f..d5dbc231 100644 --- a/src/components/metadataFormControls/ModelSingleSelect/index.ts +++ b/src/components/metadataFormControls/ModelSingleSelect/index.ts @@ -1 +1 @@ -export { ModelSingleSelect } from './ModelSingleSelect' +export * from './ModelSingleSelect' diff --git a/src/components/metadataFormControls/OptionSetSelect/OptionSetSelect.tsx b/src/components/metadataFormControls/OptionSetSelect/OptionSetSelect.tsx index a84bc158..4adbd951 100644 --- a/src/components/metadataFormControls/OptionSetSelect/OptionSetSelect.tsx +++ b/src/components/metadataFormControls/OptionSetSelect/OptionSetSelect.tsx @@ -1,22 +1,20 @@ import i18n from '@dhis2/d2-i18n' import React, { forwardRef } from 'react' import { ModelSingleSelect } from '../ModelSingleSelect' +import type { ModelSingleSelectProps } from '../ModelSingleSelect' import { useInitialOptionQuery } from './useInitialOptionQuery' import { useOptionsQuery } from './useOptionsQuery' -interface OptionSetSelectProps { - onChange: ({ selected }: { selected: string }) => void - placeholder?: string - selected?: string - showAllOption?: boolean - onBlur?: () => void - onFocus?: () => void -} +type OptionSetSelectProps = Omit< + ModelSingleSelectProps, + 'useInitialOptionQuery' | 'useOptionsQuery' +> export const OptionSetSelect = forwardRef(function OptionSetSelect( { onChange, placeholder = i18n.t('Option set'), + required, selected, showAllOption, onBlur, @@ -27,6 +25,7 @@ export const OptionSetSelect = forwardRef(function OptionSetSelect( return ( { variables: payload, }) } catch (e) { - console.log('> e', e) return { [FORM_ERROR]: (e as Error | string).toString() } } diff --git a/src/pages/dataElements/form/CustomAttributes.tsx b/src/pages/dataElements/form/CustomAttributes.tsx index 2eaf1bd9..ce0b0326 100644 --- a/src/pages/dataElements/form/CustomAttributes.tsx +++ b/src/pages/dataElements/form/CustomAttributes.tsx @@ -64,9 +64,8 @@ function CustomAttribute({ attribute, index }: CustomAttributeProps) { ) } - throw new Error( - `@TODO(CustomAttributes): Implement value type "${attribute.valueType}"!` - ) + // @TODO: Verify that all value types have been covered! + throw new Error(`Implement value type "${attribute.valueType}"!`) } export function CustomAttributes({ diff --git a/src/pages/dataElements/form/customFields.tsx b/src/pages/dataElements/form/customFields.tsx index 757850e2..2bce4ada 100644 --- a/src/pages/dataElements/form/customFields.tsx +++ b/src/pages/dataElements/form/customFields.tsx @@ -251,6 +251,7 @@ export function CategoryComboField() { validationText={meta.error} > Date: Wed, 4 Oct 2023 13:05:04 +0200 Subject: [PATCH 03/14] fix(color and icon picker): add remove icon btn & adjust styles according to specs --- .../ColorAndIconPicker/ColorAndIconPicker.tsx | 1 - .../ColorAndIconPicker/ColorPicker.module.css | 5 +- .../ColorAndIconPicker/ColorPicker.tsx | 57 ++----------------- .../ColorAndIconPicker/IconPicker.module.css | 14 +++-- .../ColorAndIconPicker/IconPicker.tsx | 4 +- .../ColorAndIconPicker/IconPickerModal.tsx | 12 +++- .../ColorAndIconPicker/availableColors.ts | 41 +++++++++++++ 7 files changed, 67 insertions(+), 67 deletions(-) create mode 100644 src/components/ColorAndIconPicker/availableColors.ts diff --git a/src/components/ColorAndIconPicker/ColorAndIconPicker.tsx b/src/components/ColorAndIconPicker/ColorAndIconPicker.tsx index cf0321c5..8260e559 100644 --- a/src/components/ColorAndIconPicker/ColorAndIconPicker.tsx +++ b/src/components/ColorAndIconPicker/ColorAndIconPicker.tsx @@ -17,7 +17,6 @@ export function ColorAndIconPicker({ return (
-
) diff --git a/src/components/ColorAndIconPicker/ColorPicker.module.css b/src/components/ColorAndIconPicker/ColorPicker.module.css index 77075001..3ffea902 100644 --- a/src/components/ColorAndIconPicker/ColorPicker.module.css +++ b/src/components/ColorAndIconPicker/ColorPicker.module.css @@ -3,7 +3,7 @@ gap: var(--spacers-dp8); padding: var(--spacers-dp8); border-radius: 3px; - border: 1px solid var(--colors-grey600); + border: 1px solid var(--colors-grey500); background: var(--colors-white); width: 68px; cursor: pointer; @@ -15,7 +15,8 @@ justify-content: center; height: 26px; width: 26px; - border: 1px solid var(--colors-black); + border: 1px dashed var(--colors-grey400); + border-radius: 2px; } .hasColor .chosenColor { diff --git a/src/components/ColorAndIconPicker/ColorPicker.tsx b/src/components/ColorAndIconPicker/ColorPicker.tsx index 36857731..34ee79e9 100644 --- a/src/components/ColorAndIconPicker/ColorPicker.tsx +++ b/src/components/ColorAndIconPicker/ColorPicker.tsx @@ -1,57 +1,10 @@ -import { - IconChevronDown16, - IconChevronUp16, - IconEmptyFrame24, - Layer, - Popper, -} from '@dhis2/ui' +import { IconChevronDown16, IconChevronUp16, Layer, Popper } from '@dhis2/ui' import cx from 'classnames' import React, { useRef, useState } from 'react' import { SwatchesPicker } from 'react-color' +import { AVAILABLE_COLORS } from './availableColors' import classes from './ColorPicker.module.css' -const COLORS = [ - [ - '#ffcdd2', - '#e57373', - '#d32f2f', - '#f06292', - '#c2185b', - '#880e4f', - '#f50057', - ], - [ - '#e1bee7', - '#ba68c8', - '#8e24aa', - '#aa00ff', - '#7e57c2', - '#4527a0', - '#7c4dff', - '#6200ea', - ], - ['#c5cae9', '#7986cb', '#3949ab', '#304ffe'], - ['#e3f2fd', '#64b5f6', '#1976d2', '#0288d1'], - ['#40c4ff', '#00b0ff', '#80deea'], - ['#00acc1', '#00838f', '#006064'], - ['#e0f2f1', '#80cbc4', '#00695c', '#64ffda'], - ['#c8e6c9', '#66bb6a', '#2e7d32', '#1b5e20'], - ['#00e676', '#aed581', '#689f38', '#33691e'], - ['#76ff03', '#64dd17', '#cddc39', '#9e9d24', '#827717'], - [ - '#fff9c4', - '#fbc02d', - '#f57f17', - '#ffff00', - '#ffcc80', - '#ffccbc', - '#ffab91', - ], - ['#bcaaa4', '#8d6e63', '#4e342e'], - ['#fafafa', '#bdbdbd', '#757575', '#424242'], - ['#cfd8dc', '#b0bec5', '#607d8b', '#37474f'], -] - export function ColorPicker({ onColorPick, color = '', @@ -74,9 +27,7 @@ export function ColorPicker({
- {!color && } -
+ />
{showPicker ? : } @@ -88,7 +39,7 @@ export function ColorPicker({
- {!icon && } {selectedIcon && ( setShowPicker(false)} onChange={({ icon }) => { onIconPick({ icon }) diff --git a/src/components/ColorAndIconPicker/IconPickerModal.tsx b/src/components/ColorAndIconPicker/IconPickerModal.tsx index 00227bff..7cc1fd39 100644 --- a/src/components/ColorAndIconPicker/IconPickerModal.tsx +++ b/src/components/ColorAndIconPicker/IconPickerModal.tsx @@ -19,15 +19,17 @@ import { useIconsQuery, Icon } from './useIconsQuery' type TabName = 'all' | 'positive' | 'negative' | 'outline' export function IconPickerModal({ + selected, onChange, onCancel, }: { + selected: string onChange: ({ icon }: { icon: string }) => void onCancel: () => void }) { const [searchValue, setSearchValue] = useState('') const [activeTab, setActiveTab] = useState('all') - const [icon, setIcon] = useState('') + const [icon, setIcon] = useState(selected) const icons = useIconsQuery() const displayIcons = searchValue ? filterIcons(icons.data[activeTab], searchValue) @@ -107,11 +109,15 @@ export function IconPickerModal({ disabled={!icon} onClick={() => onChange({ icon })} > - Select + {i18n.t('Select')} + + + diff --git a/src/components/ColorAndIconPicker/availableColors.ts b/src/components/ColorAndIconPicker/availableColors.ts new file mode 100644 index 00000000..81b1977c --- /dev/null +++ b/src/components/ColorAndIconPicker/availableColors.ts @@ -0,0 +1,41 @@ +export const AVAILABLE_COLORS = [ + [ + '#ffcdd2', + '#e57373', + '#d32f2f', + '#f06292', + '#c2185b', + '#880e4f', + '#f50057', + ], + [ + '#e1bee7', + '#ba68c8', + '#8e24aa', + '#aa00ff', + '#7e57c2', + '#4527a0', + '#7c4dff', + '#6200ea', + ], + ['#c5cae9', '#7986cb', '#3949ab', '#304ffe'], + ['#e3f2fd', '#64b5f6', '#1976d2', '#0288d1'], + ['#40c4ff', '#00b0ff', '#80deea'], + ['#00acc1', '#00838f', '#006064'], + ['#e0f2f1', '#80cbc4', '#00695c', '#64ffda'], + ['#c8e6c9', '#66bb6a', '#2e7d32', '#1b5e20'], + ['#00e676', '#aed581', '#689f38', '#33691e'], + ['#76ff03', '#64dd17', '#cddc39', '#9e9d24', '#827717'], + [ + '#fff9c4', + '#fbc02d', + '#f57f17', + '#ffff00', + '#ffcc80', + '#ffccbc', + '#ffab91', + ], + ['#bcaaa4', '#8d6e63', '#4e342e'], + ['#fafafa', '#bdbdbd', '#757575', '#424242'], + ['#cfd8dc', '#b0bec5', '#607d8b', '#37474f'], +] From 402de21fa4ac08a7ba9cb3f6c7834fa8852a2bb1 Mon Sep 17 00:00:00 2001 From: Jan-Gerke Salomon Date: Wed, 4 Oct 2023 13:07:01 +0200 Subject: [PATCH 04/14] feat(de form): add aggregation level intro text --- i18n/en.pot | 35 ++++++++++--------- .../form/DataElementFormFields.tsx | 14 ++++---- 2 files changed, 27 insertions(+), 22 deletions(-) diff --git a/i18n/en.pot b/i18n/en.pot index fc9d9f30..64ac5e45 100644 --- a/i18n/en.pot +++ b/i18n/en.pot @@ -5,8 +5,8 @@ msgstr "" "Content-Type: text/plain; charset=utf-8\n" "Content-Transfer-Encoding: 8bit\n" "Plural-Forms: nplurals=2; plural=(n != 1)\n" -"POT-Creation-Date: 2023-10-11T10:29:42.492Z\n" -"PO-Revision-Date: 2023-10-11T10:29:42.492Z\n" +"POT-Creation-Date: 2023-10-25T11:41:05.785Z\n" +"PO-Revision-Date: 2023-10-25T11:41:05.785Z\n" msgid "schemas" msgstr "schemas" @@ -48,6 +48,15 @@ msgstr "Search for menu items" msgid "Search icons" msgstr "Search icons" +msgid "Select" +msgstr "Select" + +msgid "Remove icon" +msgstr "Remove icon" + +msgid "Cancel" +msgstr "Cancel" + msgid "Retry" msgstr "Retry" @@ -69,12 +78,15 @@ msgstr "Aggregation level(s)" msgid "Category combo" msgstr "Category combo" -msgid "None" -msgstr "None" +msgid "Default (none)" +msgstr "Default (none)" msgid "Filter legend sets" msgstr "Filter legend sets" +msgid "None" +msgstr "None" + msgid "Option set" msgstr "Option set" @@ -108,9 +120,6 @@ msgstr "Type to filter options" msgid "No matches" msgstr "No matches" -msgid "Data set" -msgstr "Data set" - msgid "Clear all filters" msgstr "Clear all filters" @@ -135,9 +144,6 @@ msgstr "Failed to save" msgid "Manage {{section}} table columns" msgstr "Manage {{section}} table columns" -msgid "Cancel" -msgstr "Cancel" - msgid "Update table columns" msgstr "Update table columns" @@ -213,6 +219,9 @@ msgstr "Data element group set" msgid "Data element group sets" msgstr "Data element group sets" +msgid "Data set" +msgstr "Data set" + msgid "Data sets" msgstr "Data sets" @@ -621,9 +630,6 @@ msgstr "{{fieldLabel}} (required)" msgid "A data element name should be concise and easy to recognize." msgstr "A data element name should be concise and easy to recognize." -msgid "Short name" -msgstr "Short name" - msgid "Often used in reports where space is limited" msgstr "Often used in reports where space is limited" @@ -704,9 +710,6 @@ msgstr "Refresh list" msgid "Add new" msgstr "Add new" -msgid "Value type" -msgstr "Value type" - msgid "The type of data that will be recorded." msgstr "The type of data that will be recorded." diff --git a/src/pages/dataElements/form/DataElementFormFields.tsx b/src/pages/dataElements/form/DataElementFormFields.tsx index 8fa7613a..4cf302b1 100644 --- a/src/pages/dataElements/form/DataElementFormFields.tsx +++ b/src/pages/dataElements/form/DataElementFormFields.tsx @@ -217,12 +217,14 @@ export function DataElementFormFields() { {i18n.t('Aggregation levels')} - {` - @TODO(DataElementForm): Help text to describe the aggregation levels - functionality. It appears as if this section hasn't been - finalized yet by Joe, so I guess we'll have to talk about - this particluar part. - `} + By default, the aggregation will start at the lowest + assigned organisation unit. If you for example select + "Chiefdom", it means that "Chiefdom", + "District" and "National" aggregates use + "Chiefdom" (the highest aggregation level + available) as the data source, and PHU data will not be + included. PHU will still be available for the PHU level, but + not included in the aggregations to the levels above. From 32f7e20a1ae4f7a20d5971c7546ba3de42b38aa7 Mon Sep 17 00:00:00 2001 From: Jan-Gerke Salomon Date: Wed, 4 Oct 2023 17:15:56 +0200 Subject: [PATCH 05/14] feat(de edit and new): handle loading and error states correctly --- i18n/en.pot | 10 +- src/pages/dataElements/Edit.tsx | 100 +++++++++++------- src/pages/dataElements/New.tsx | 49 ++++----- .../form/DataElementFormFields.tsx | 25 ++--- 4 files changed, 96 insertions(+), 88 deletions(-) diff --git a/i18n/en.pot b/i18n/en.pot index 64ac5e45..b1b577d6 100644 --- a/i18n/en.pot +++ b/i18n/en.pot @@ -5,8 +5,8 @@ msgstr "" "Content-Type: text/plain; charset=utf-8\n" "Content-Transfer-Encoding: 8bit\n" "Plural-Forms: nplurals=2; plural=(n != 1)\n" -"POT-Creation-Date: 2023-10-25T11:41:05.785Z\n" -"PO-Revision-Date: 2023-10-25T11:41:05.785Z\n" +"POT-Creation-Date: 2023-10-25T11:43:32.223Z\n" +"PO-Revision-Date: 2023-10-25T11:43:32.223Z\n" msgid "schemas" msgstr "schemas" @@ -606,6 +606,9 @@ msgstr "Owner" msgid "Zero is significant" msgstr "Zero is significant" +msgid "Custom attributes" +msgstr "Custom attributes" + msgid "Something went wrong when submitting the form" msgstr "Something went wrong when submitting the form" @@ -682,9 +685,6 @@ msgstr "" msgid "Aggregation levels" msgstr "Aggregation levels" -msgid "Custom attributes" -msgstr "Custom attributes" - msgid "Custom fields for your DHIS2 instance" msgstr "Custom fields for your DHIS2 instance" diff --git a/src/pages/dataElements/Edit.tsx b/src/pages/dataElements/Edit.tsx index 767a2cdd..85adb34c 100644 --- a/src/pages/dataElements/Edit.tsx +++ b/src/pages/dataElements/Edit.tsx @@ -5,7 +5,11 @@ import { FORM_ERROR, FormApi } from 'final-form' import React, { useEffect, useRef } from 'react' import { withTypes } from 'react-final-form' import { useNavigate, useParams } from 'react-router-dom' -import { StandardFormActions, StandardFormSection } from '../../components' +import { + Loader, + StandardFormActions, + StandardFormSection, +} from '../../components' import { SCHEMA_SECTIONS, getSectionPath } from '../../lib' import { JsonPatchOperation } from '../../types' import { Attribute, DataElement } from '../../types/generated' @@ -40,12 +44,18 @@ function useDataElementQuery(id: string) { } function computeInitialValues({ + dataElementId, dataElement, customAttributes, }: { + dataElementId: string dataElement: DataElement customAttributes: Attribute[] }) { + if (!dataElement) { + return {} + } + // We want to have an array in the state with all attributes, not just the // ones that have a value which is what the endpoint responds with const attributeValues = customAttributes.map((attribute) => { @@ -62,6 +72,7 @@ function computeInitialValues({ }) return { + id: dataElementId, name: dataElement.name, shortName: dataElement.shortName, code: dataElement.code, @@ -86,34 +97,22 @@ function computeInitialValues({ } } -export const Component = () => { +function usePatchDirtyFields() { const dataEngine = useDataEngine() - const navigate = useNavigate() - const params = useParams() - - const dataElementId = params.id as string - const dataElementQuery = useDataElementQuery(dataElementId) - const customAttributesQuery = useCustomAttributesQuery() - const loading = dataElementQuery.loading || customAttributesQuery.loading - const error = dataElementQuery.error || customAttributesQuery.error - - if (error && !loading) { - // @TODO(Edit): Implement error screen - return `Error: ${error.toString()}` - } - - if (loading) { - // @TODO(Edit): Implement loading screen - return 'Loading...' - } - - async function onSubmit(values: FormValues, form: FinalFormFormApi) { - const dirtyFields = form.getState().dirtyFields + return async ({ + values, + dirtyFields, + dataElement, + }: { + values: FormValues + dirtyFields: { [name: string]: boolean } + dataElement: DataElement + }) => { const jsonPatchPayload = createJsonPatchOperations({ values, dirtyFields, - dataElement: dataElementQuery.data?.dataElement as DataElement, + dataElement, }) // We want the promise so we know when submitting is done. The promise @@ -121,7 +120,7 @@ export const Component = () => { // resolve const ADD_NEW_DATA_ELEMENT_MUTATION = { resource: 'dataElements', - id: dataElementId, + id: values.id, type: 'json-patch', data: ({ operations }: { operations: JsonPatchOperation[] }) => operations, @@ -134,26 +133,55 @@ export const Component = () => { } catch (e) { return { [FORM_ERROR]: (e as Error | string).toString() } } + } +} + +export const Component = () => { + const navigate = useNavigate() + const params = useParams() + const dataElementId = params.id as string + const dataElementQuery = useDataElementQuery(dataElementId) + const customAttributesQuery = useCustomAttributesQuery() + const patchDirtyFields = usePatchDirtyFields() + + async function onSubmit(values: FormValues, form: FinalFormFormApi) { + const errors = await patchDirtyFields({ + values, + dirtyFields: form.getState().dirtyFields, + dataElement: dataElementQuery.data?.dataElement as DataElement, + }) + + if (errors) { + return errors + } navigate(listPath) } const initialValues = computeInitialValues({ + dataElementId, dataElement: dataElementQuery.data?.dataElement as DataElement, - customAttributes: customAttributesQuery.data, + customAttributes: customAttributesQuery.data || [], }) return ( -
- {({ handleSubmit, submitting, submitError }) => ( - - - - )} - + + +
+ {({ handleSubmit, submitting, submitError }) => ( + + + + )} + +
+
) } diff --git a/src/pages/dataElements/New.tsx b/src/pages/dataElements/New.tsx index 0543277e..8f03acba 100644 --- a/src/pages/dataElements/New.tsx +++ b/src/pages/dataElements/New.tsx @@ -5,7 +5,11 @@ import { FORM_ERROR } from 'final-form' import React, { useEffect, useRef } from 'react' import { Form } from 'react-final-form' import { useNavigate } from 'react-router-dom' -import { StandardFormActions, StandardFormSection } from '../../components' +import { + Loader, + StandardFormActions, + StandardFormSection, +} from '../../components' import { SCHEMA_SECTIONS, getSectionPath } from '../../lib' import { Attribute } from '../../types/generated' import { DataElementFormFields, useCustomAttributesQuery } from './form' @@ -80,27 +84,11 @@ function formatFormValues({ values }: { values: FormValues }) { } } -// @TODO(DataElements/new): values dynamic or static? export const Component = () => { const dataEngine = useDataEngine() - const navigate = useNavigate() const customAttributesQuery = useCustomAttributesQuery() - - const loading = customAttributesQuery.loading - const error = customAttributesQuery.error - - if (error && !loading) { - // @TODO(Edit): Implement error screen - return <>Error: {error.toString()} - } - - if (loading) { - // @TODO(Edit): Implement loading screen - return <>Loading... - } - - const initialValues = computeInitialValues(customAttributesQuery.data) + const initialValues = computeInitialValues(customAttributesQuery.data || []) async function onSubmit(values: FormValues) { const payload = formatFormValues({ values }) @@ -120,16 +108,21 @@ export const Component = () => { } return ( -
- {({ handleSubmit, submitting, submitError }) => ( - - - - )} - + +
+ {({ handleSubmit, submitting, submitError }) => ( + + + + )} + +
) } diff --git a/src/pages/dataElements/form/DataElementFormFields.tsx b/src/pages/dataElements/form/DataElementFormFields.tsx index 4cf302b1..43fb642f 100644 --- a/src/pages/dataElements/form/DataElementFormFields.tsx +++ b/src/pages/dataElements/form/DataElementFormFields.tsx @@ -3,6 +3,7 @@ import { CheckboxFieldFF, InputFieldFF, TextAreaFieldFF } from '@dhis2/ui' import React from 'react' import { Field as FieldRFF } from 'react-final-form' import { + Loader, StandardFormSection, StandardFormSectionTitle, StandardFormSectionDescription, @@ -25,25 +26,11 @@ import { useCustomAttributesQuery } from './useCustomAttributesQuery' export function DataElementFormFields() { const customAttributes = useCustomAttributesQuery() - const loading = customAttributes.loading - const error = customAttributes.error - - if (loading) { - return <>@TODO(DataElementForm): Loading - } - - if (error) { - return ( - <> - @TODO(DataElementForm): Error -
- {error.toString()} - - ) - } - return ( - <> + {i18n.t('Basic information')} @@ -246,6 +233,6 @@ export function DataElementFormFields() { /> )} - + ) } From 7e5c907b71b88b593fed77e8f730b4fe33661a9e Mon Sep 17 00:00:00 2001 From: Jan-Gerke Salomon Date: Tue, 17 Oct 2023 14:45:47 +0200 Subject: [PATCH 06/14] fix(searchable single selects): use old-app default labels --- .../CategoryComboSelect/useOptionsQuery.ts | 4 +--- .../ModelSingleSelect/ModelSingleSelect.tsx | 6 +++++- 2 files changed, 6 insertions(+), 4 deletions(-) diff --git a/src/components/metadataFormControls/CategoryComboSelect/useOptionsQuery.ts b/src/components/metadataFormControls/CategoryComboSelect/useOptionsQuery.ts index 277f904a..d3b0b3f1 100644 --- a/src/components/metadataFormControls/CategoryComboSelect/useOptionsQuery.ts +++ b/src/components/metadataFormControls/CategoryComboSelect/useOptionsQuery.ts @@ -64,9 +64,7 @@ export function useOptionsQuery() { value: id, // This should be distinguishable from other selects // where "none" means no selection - label: isDefault - ? i18n.t('Default (none)') - : displayName, + label: isDefault ? i18n.t('None') : displayName, } }) || []), ]) diff --git a/src/components/metadataFormControls/ModelSingleSelect/ModelSingleSelect.tsx b/src/components/metadataFormControls/ModelSingleSelect/ModelSingleSelect.tsx index 04623c55..cf6b672a 100644 --- a/src/components/metadataFormControls/ModelSingleSelect/ModelSingleSelect.tsx +++ b/src/components/metadataFormControls/ModelSingleSelect/ModelSingleSelect.tsx @@ -38,7 +38,11 @@ function computeDisplayOptions({ : options if (!required) { - return [{ value: '', label: i18n.t('None') }, ...withSelectedOption] + // This default value has been copied from the old app + return [ + { value: '', label: i18n.t('') }, + ...withSelectedOption, + ] } return withSelectedOption From 92fb57ac908ac91066c4fb683c1acb8f4ebb69d1 Mon Sep 17 00:00:00 2001 From: Birk Johansson Date: Mon, 23 Oct 2023 22:07:02 +0300 Subject: [PATCH 07/14] fix(form): fix render 0 when empty custom attributes --- src/pages/dataElements/form/DataElementFormFields.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/pages/dataElements/form/DataElementFormFields.tsx b/src/pages/dataElements/form/DataElementFormFields.tsx index 43fb642f..907102c2 100644 --- a/src/pages/dataElements/form/DataElementFormFields.tsx +++ b/src/pages/dataElements/form/DataElementFormFields.tsx @@ -219,7 +219,7 @@ export function DataElementFormFields() {
- {customAttributes.data?.length && ( + {customAttributes.data?.length > 0 && ( {i18n.t('Custom attributes')} From de8cf8cbb71ad4c743d6ff50bd043ae60910fe95 Mon Sep 17 00:00:00 2001 From: Jan-Gerke Salomon Date: Wed, 25 Oct 2023 13:29:20 +0200 Subject: [PATCH 08/14] feat(custom attributes): add "No value" option to optionset attributes --- src/pages/dataElements/form/CustomAttributes.tsx | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/src/pages/dataElements/form/CustomAttributes.tsx b/src/pages/dataElements/form/CustomAttributes.tsx index ce0b0326..fb67856a 100644 --- a/src/pages/dataElements/form/CustomAttributes.tsx +++ b/src/pages/dataElements/form/CustomAttributes.tsx @@ -1,3 +1,4 @@ +import i18n from '@dhis2/d2-i18n' import { InputFieldFF, SingleSelectFieldFF, TextAreaFieldFF } from '@dhis2/ui' import * as React from 'react' import { Field as FieldRFF } from 'react-final-form' @@ -15,13 +16,18 @@ function CustomAttribute({ attribute, index }: CustomAttributeProps) { const name = `attributeValues[${index}].value` if (attribute.optionSet?.options) { - const options = attribute.optionSet?.options.map( + const attributeOptions = attribute.optionSet?.options.map( ({ code, displayName }) => ({ value: code, label: displayName, }) ) + const options = [ + { value: '', label: i18n.t('') }, + ...(attributeOptions || []), + ] + return ( Date: Wed, 25 Oct 2023 13:32:33 +0200 Subject: [PATCH 09/14] fix(de form fields): make text translatable --- src/pages/dataElements/form/DataElementFormFields.tsx | 11 +++-------- 1 file changed, 3 insertions(+), 8 deletions(-) diff --git a/src/pages/dataElements/form/DataElementFormFields.tsx b/src/pages/dataElements/form/DataElementFormFields.tsx index 907102c2..edf5b8c5 100644 --- a/src/pages/dataElements/form/DataElementFormFields.tsx +++ b/src/pages/dataElements/form/DataElementFormFields.tsx @@ -204,14 +204,9 @@ export function DataElementFormFields() { {i18n.t('Aggregation levels')} - By default, the aggregation will start at the lowest - assigned organisation unit. If you for example select - "Chiefdom", it means that "Chiefdom", - "District" and "National" aggregates use - "Chiefdom" (the highest aggregation level - available) as the data source, and PHU data will not be - included. PHU will still be available for the PHU level, but - not included in the aggregations to the levels above. + {i18n.t( + 'By default, the aggregation will start at the lowest assigned organisation unit. If you for example select "Chiefdom", it means that "Chiefdom", "District" and "National" aggregates use "Chiefdom" (the highest aggregation level available) as the data source, and PHU data will not be included. PHU will still be available for the PHU level, but not included in the aggregations to the levels above.' + )} From c10ec33781ee2efc0864dc08b8d20238312ba093 Mon Sep 17 00:00:00 2001 From: Jan-Gerke Salomon Date: Mon, 30 Oct 2023 12:27:40 +0100 Subject: [PATCH 10/14] fix(custom attributes optionsets): add no-value-option only when not required --- src/pages/dataElements/form/CustomAttributes.tsx | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/src/pages/dataElements/form/CustomAttributes.tsx b/src/pages/dataElements/form/CustomAttributes.tsx index fb67856a..f379ffd5 100644 --- a/src/pages/dataElements/form/CustomAttributes.tsx +++ b/src/pages/dataElements/form/CustomAttributes.tsx @@ -14,25 +14,25 @@ type CustomAttributeProps = { function CustomAttribute({ attribute, index }: CustomAttributeProps) { const name = `attributeValues[${index}].value` + const required = attribute.mandatory if (attribute.optionSet?.options) { - const attributeOptions = attribute.optionSet?.options.map( + const options = attribute.optionSet?.options.map( ({ code, displayName }) => ({ value: code, label: displayName, }) ) - const options = [ - { value: '', label: i18n.t('') }, - ...(attributeOptions || []), - ] + if (required) { + options.unshift({ value: '', label: i18n.t('') }) + } return ( Date: Wed, 22 Nov 2023 13:21:20 +0100 Subject: [PATCH 11/14] feat(data element forms): add validation (#359) --- i18n/en.pot | 109 +-- jest-setup.js | 7 + .../SearchableSingleSelect.tsx | 3 + .../AggregationLevelMultiSelect.tsx | 4 +- .../CategoryComboSelect.tsx | 2 + .../ModelSingleSelect/ModelSingleSelect.tsx | 3 + .../OptionSetSelect/OptionSetSelect.tsx | 2 + src/lib/index.ts | 3 +- src/lib/useLoadApp.ts | 12 +- src/pages/dataElements/Edit.tsx | 16 +- src/pages/dataElements/List.spec.tsx | 2 +- src/pages/dataElements/New.spec.tsx | 64 +- src/pages/dataElements/New.tsx | 102 ++- .../edit/createJsonPatchOperations.ts | 2 +- .../dataElements/form/CustomAttributes.tsx | 37 +- .../form/DataElementFormFields.tsx | 173 ++--- .../form/createDataElementSchema.ts | 104 +++ .../form/{customFields.tsx => fields.tsx} | 228 ++++++- src/pages/dataElements/form/index.ts | 2 + .../form/useIsFieldValueUnique.ts | 59 ++ src/pages/dataElements/form/useValidate.ts | 40 ++ src/types/generated/index.ts | 2 +- yarn.lock | 618 +----------------- 23 files changed, 709 insertions(+), 885 deletions(-) create mode 100644 src/pages/dataElements/form/createDataElementSchema.ts rename src/pages/dataElements/form/{customFields.tsx => fields.tsx} (63%) create mode 100644 src/pages/dataElements/form/useIsFieldValueUnique.ts create mode 100644 src/pages/dataElements/form/useValidate.ts diff --git a/i18n/en.pot b/i18n/en.pot index b1b577d6..0d0bd2fc 100644 --- a/i18n/en.pot +++ b/i18n/en.pot @@ -5,8 +5,8 @@ msgstr "" "Content-Type: text/plain; charset=utf-8\n" "Content-Transfer-Encoding: 8bit\n" "Plural-Forms: nplurals=2; plural=(n != 1)\n" -"POT-Creation-Date: 2023-10-25T11:43:32.223Z\n" -"PO-Revision-Date: 2023-10-25T11:43:32.223Z\n" +"POT-Creation-Date: 2023-11-16T09:17:45.955Z\n" +"PO-Revision-Date: 2023-11-16T09:17:45.955Z\n" msgid "schemas" msgstr "schemas" @@ -78,14 +78,14 @@ msgstr "Aggregation level(s)" msgid "Category combo" msgstr "Category combo" -msgid "Default (none)" -msgstr "Default (none)" +msgid "None" +msgstr "None" msgid "Filter legend sets" msgstr "Filter legend sets" -msgid "None" -msgstr "None" +msgid "" +msgstr "" msgid "Option set" msgstr "Option set" @@ -621,12 +621,64 @@ msgstr "Exit without saving" msgid "Create data element" msgstr "Create data element" +msgid "Loading custom attributes" +msgstr "Loading custom attributes" + +msgid "Something went wrong with retrieving the custom attributes" +msgstr "Something went wrong with retrieving the custom attributes" + msgid "Basic information" msgstr "Basic information" msgid "Set up the information for this data element" msgstr "Set up the information for this data element" +msgid "Disaggregation and Option sets" +msgstr "Disaggregation and Option sets" + +msgid "Set up disaggregation and predefined options." +msgstr "Set up disaggregation and predefined options." + +msgid "LegendSet" +msgstr "LegendSet" + +msgid "" +"Visualize values for this data element in Analytics app. Multiple legendSet " +"can be applied." +msgstr "" +"Visualize values for this data element in Analytics app. Multiple legendSet " +"can be applied." + +msgid "Aggregation levels" +msgstr "Aggregation levels" + +msgid "" +"By default, the aggregation will start at the lowest assigned organisation " +"unit. If you for example select \"Chiefdom\", it means that \"Chiefdom\", " +"\"District\" and \"National\" aggregates use \"Chiefdom\" (the highest " +"aggregation level available) as the data source, and PHU data will not be " +"included. PHU will still be available for the PHU level, but not included " +"in the aggregations to the levels above." +msgstr "" +"By default, the aggregation will start at the lowest assigned organisation " +"unit. If you for example select \"Chiefdom\", it means that \"Chiefdom\", " +"\"District\" and \"National\" aggregates use \"Chiefdom\" (the highest " +"aggregation level available) as the data source, and PHU data will not be " +"included. PHU will still be available for the PHU level, but not included " +"in the aggregations to the levels above." + +msgid "Custom fields for your DHIS2 instance" +msgstr "Custom fields for your DHIS2 instance" + +msgid "Required" +msgstr "Required" + +msgid "Cannot be longer than {{number}} character" +msgstr "Cannot be longer than {{number}} character" + +msgid "The value is to long. You can use up to 255 characters" +msgstr "The value is to long. You can use up to 255 characters" + msgid "{{fieldLabel}} (required)" msgstr "{{fieldLabel}} (required)" @@ -648,6 +700,16 @@ msgstr "Url" msgid "A web link that provides extra information" msgstr "A web link that provides extra information" +msgid "Color and icon" +msgstr "Color and icon" + +msgid "" +"A color and icon are helpful for identifying data elements in " +"information-dense screens." +msgstr "" +"A color and icon are helpful for identifying data elements in " +"information-dense screens." + msgid "Field mask" msgstr "Field mask" @@ -666,38 +728,6 @@ msgstr "An alternative name used in section or automatic data entry forms." msgid "Store zero data values" msgstr "Store zero data values" -msgid "Disaggregation and Option sets" -msgstr "Disaggregation and Option sets" - -msgid "Set up disaggregation and predefined options." -msgstr "Set up disaggregation and predefined options." - -msgid "LegendSet" -msgstr "LegendSet" - -msgid "" -"Visualize values for this data element in Analytics app. Multiple legendSet " -"can be applied." -msgstr "" -"Visualize values for this data element in Analytics app. Multiple legendSet " -"can be applied." - -msgid "Aggregation levels" -msgstr "Aggregation levels" - -msgid "Custom fields for your DHIS2 instance" -msgstr "Custom fields for your DHIS2 instance" - -msgid "Color and icon" -msgstr "Color and icon" - -msgid "" -"A color and icon are helpful for identifying data elements in " -"information-dense screens." -msgstr "" -"A color and icon are helpful for identifying data elements in " -"information-dense screens." - msgid "A data element can either be aggregated or tracked data." msgstr "A data element can either be aggregated or tracked data." @@ -731,6 +761,9 @@ msgstr "Option set comment" msgid "Choose a set of predefined comment for data entry" msgstr "Choose a set of predefined comment for data entry" +msgid "This field requires a unique value, please choose another one" +msgstr "This field requires a unique value, please choose another one" + msgid "Metadata management" msgstr "Metadata management" diff --git a/jest-setup.js b/jest-setup.js index c89d1c98..3cba363c 100644 --- a/jest-setup.js +++ b/jest-setup.js @@ -1,6 +1,13 @@ import { configure } from '@testing-library/react' import '@testing-library/jest-dom' +// Not defined on nodejs +window.IntersectionObserver = jest.fn(() => ({ + observe: jest.fn(), + unobserve: jest.fn(), + disconnect: jest.fn(), +})) + beforeEach(() => { configure({ testIdAttribute: 'data-test' }) }) diff --git a/src/components/SearchableSingleSelect/SearchableSingleSelect.tsx b/src/components/SearchableSingleSelect/SearchableSingleSelect.tsx index 4d1471e6..5cbccdde 100644 --- a/src/components/SearchableSingleSelect/SearchableSingleSelect.tsx +++ b/src/components/SearchableSingleSelect/SearchableSingleSelect.tsx @@ -58,6 +58,7 @@ interface SearchableSingleSelectPropTypes { showEndLoader: boolean loading: boolean selected?: string + invalid?: boolean error?: string showAllOption?: boolean onBlur?: () => void @@ -65,6 +66,7 @@ interface SearchableSingleSelectPropTypes { } export const SearchableSingleSelect = ({ + invalid, error, loading, placeholder, @@ -121,6 +123,7 @@ export const SearchableSingleSelect = ({ // fetched the corresponding label yet. Therefore we don't want to pass in // any value to the "selected" prop, as otherwise an error will be thrown selected={hasSelectedInOptionList ? selected : ''} + error={invalid} onChange={onChange} placeholder={placeholder} onBlur={onBlur} diff --git a/src/components/metadataFormControls/AggregationLevelMultiSelect/AggregationLevelMultiSelect.tsx b/src/components/metadataFormControls/AggregationLevelMultiSelect/AggregationLevelMultiSelect.tsx index 931cb38f..23e3c480 100644 --- a/src/components/metadataFormControls/AggregationLevelMultiSelect/AggregationLevelMultiSelect.tsx +++ b/src/components/metadataFormControls/AggregationLevelMultiSelect/AggregationLevelMultiSelect.tsx @@ -31,6 +31,7 @@ interface AggregationLevelMultiSelectProps { onChange: ({ selected }: { selected: string[] }) => void onRetryClick: () => void inputWidth?: string + invalid?: boolean placeholder?: string selected?: string[] showAllOption?: boolean @@ -43,6 +44,7 @@ export const AggregationLevelMultiSelect = forwardRef( { onChange, inputWidth, + invalid, selected, showAllOption, placeholder = i18n.t('Aggregation level(s)'), @@ -67,7 +69,7 @@ export const AggregationLevelMultiSelect = forwardRef( onChange={({ selected }: { selected: string[] }) => { onChange({ selected }) }} - error={!!optionsQuery.error} + error={!!optionsQuery.error || invalid} selected={loading ? [] : selected} loading={loading} onBlur={onBlur} diff --git a/src/components/metadataFormControls/CategoryComboSelect/CategoryComboSelect.tsx b/src/components/metadataFormControls/CategoryComboSelect/CategoryComboSelect.tsx index 869f7e2a..36ffb628 100644 --- a/src/components/metadataFormControls/CategoryComboSelect/CategoryComboSelect.tsx +++ b/src/components/metadataFormControls/CategoryComboSelect/CategoryComboSelect.tsx @@ -13,6 +13,7 @@ type CategoryComboSelectProps = Omit< export const CategoryComboSelect = forwardRef(function CategoryComboSelect( { onChange, + invalid, placeholder = i18n.t('Category combo'), required, selected, @@ -26,6 +27,7 @@ export const CategoryComboSelect = forwardRef(function CategoryComboSelect( void required?: boolean + invalid?: boolean placeholder?: string selected?: string showAllOption?: boolean @@ -71,6 +72,7 @@ export interface ModelSingleSelectProps { export const ModelSingleSelect = forwardRef(function ModelSingleSelect( { onChange, + invalid, placeholder = '', required, selected, @@ -150,6 +152,7 @@ export const ModelSingleSelect = forwardRef(function ModelSingleSelect( return ( { diff --git a/src/components/metadataFormControls/OptionSetSelect/OptionSetSelect.tsx b/src/components/metadataFormControls/OptionSetSelect/OptionSetSelect.tsx index 4adbd951..2ea77896 100644 --- a/src/components/metadataFormControls/OptionSetSelect/OptionSetSelect.tsx +++ b/src/components/metadataFormControls/OptionSetSelect/OptionSetSelect.tsx @@ -13,6 +13,7 @@ type OptionSetSelectProps = Omit< export const OptionSetSelect = forwardRef(function OptionSetSelect( { onChange, + invalid, placeholder = i18n.t('Option set'), required, selected, @@ -26,6 +27,7 @@ export const OptionSetSelect = forwardRef(function OptionSetSelect( export type ModelSchemas = ModelSchemasBase // same fields as headbar-request to hit the cache -export const userFields = [ +const userFields = [ 'authorities', 'avatar', 'email', @@ -41,6 +41,12 @@ const userFieldsFilter = userFields.concat() type UserPropertyFields = (typeof userFields)[number] type CurrentUserResponse = Pick +/** + * !!! WARNING !!! + * There's already a `CurrentUser` type exported from the generated schema + * types. We need to think about the name of this type, see: + * https://github.com/dhis2/maintenance-app-beta/pull/359#discussion_r1399267866 + */ export interface CurrentUser extends Omit { authorities: Set // use a set for faster lookup } diff --git a/src/pages/dataElements/Edit.tsx b/src/pages/dataElements/Edit.tsx index 85adb34c..1051df0c 100644 --- a/src/pages/dataElements/Edit.tsx +++ b/src/pages/dataElements/Edit.tsx @@ -15,8 +15,12 @@ import { JsonPatchOperation } from '../../types' import { Attribute, DataElement } from '../../types/generated' import { createJsonPatchOperations } from './edit/' import classes from './Edit.module.css' -import { DataElementFormFields, useCustomAttributesQuery } from './form' -import { FormValues } from './form/types' +import { + DataElementFormFields, + useCustomAttributesQuery, + useValidate, +} from './form' +import type { FormValues } from './form' type FinalFormFormApi = FormApi @@ -143,6 +147,7 @@ export const Component = () => { const dataElementQuery = useDataElementQuery(dataElementId) const customAttributesQuery = useCustomAttributesQuery() const patchDirtyFields = usePatchDirtyFields() + const validate = useValidate() async function onSubmit(values: FormValues, form: FinalFormFormApi) { const errors = await patchDirtyFields({ @@ -170,7 +175,12 @@ export const Component = () => { queryResponse={customAttributesQuery} label={i18n.t('Custom attributes')} > -
+ {({ handleSubmit, submitting, submitError }) => ( { const { id } = dataElementsMock.result[0] const firstRow = getByTestId(`section-list-row-${id}`) expect(firstRow).toHaveTextContent( - 'Accute Flaccid Paralysis (Deaths < 5 yrs)AggregateNumber6 years agoPublic can edit' + /Accute Flaccid Paralysis \(Deaths < 5 yrs\)AggregateNumber\d+ years agoPublic can edit/ ) }) it('should display all the columns', async () => { diff --git a/src/pages/dataElements/New.spec.tsx b/src/pages/dataElements/New.spec.tsx index 66c02259..56d01086 100644 --- a/src/pages/dataElements/New.spec.tsx +++ b/src/pages/dataElements/New.spec.tsx @@ -1,10 +1,4 @@ -import { - RenderResult, - act, - fireEvent, - render, - waitFor, -} from '@testing-library/react' +import { RenderResult, fireEvent, render } from '@testing-library/react' import React from 'react' import { RouterProvider, createMemoryRouter } from 'react-router-dom' import dataElementSchemaMock from '../../__mocks__/schema/dataElementsSchema.json' @@ -114,7 +108,7 @@ describe('Data Elements / New', () => { result.container.querySelectorAll( '.error[data-test$="-validation"]' ) - ).toHaveLength(4) + ).toHaveLength(3) const nameRequiredError = await result.findByText('Required', { selector: '[data-test="dataelementsformfields-name-validation"]', @@ -127,24 +121,18 @@ describe('Data Elements / New', () => { }) expect(shortNameRequiredError).toBeTruthy() - const valueTypeRequiredError = await result.findByText('Required', { + const categoryComboRequiredError = await result.findByText('Required', { selector: - '[data-test="dataelementsformfields-valuetype-validation"]', + '[data-test="dataelementsformfields-categorycombo-validation"]', }) - expect(valueTypeRequiredError).toBeTruthy() - - const aggregationTypeRequiredError = await result.findByText( - 'Required', - { - selector: - '[data-test="dataelementsformfields-aggregationtype-validation"]', - } - ) - expect(aggregationTypeRequiredError).toBeTruthy() + expect(categoryComboRequiredError).toBeTruthy() }) it('should submit the data and return to the list view on success', async () => { - const dataElementCustomData = jest.fn(() => Promise.resolve({})) + const dataElementCustomData = () => + Promise.resolve({ + pager: { total: 0 }, + }) const router = createMemoryRouter( [ { path: '/new', element: }, @@ -175,23 +163,27 @@ describe('Data Elements / New', () => { expect(submitButton).toBeTruthy() - fireEvent.change( - result.getByRole('textbox', { - name: 'Name (required) *', - }) as HTMLElement, - { target: { value: 'Data element name' } } - ) - - fireEvent.change( - result.getByRole('textbox', { - name: 'Short name (required) *', - }) as HTMLElement, - { target: { value: 'Data element short name' } } - ) + const nameInput = result.getByRole('textbox', { + name: 'Name (required) *', + }) as HTMLInputElement + fireEvent.change(nameInput, { + target: { value: 'Data element name' }, + }) + fireEvent.blur(nameInput) - await changeSingleSelect(result, 'Value type (required)', 'Text') + const shortNameInput = result.getByRole('textbox', { + name: 'Short name (required) *', + }) as HTMLInputElement + fireEvent.change(shortNameInput, { + target: { value: 'Data element short name' }, + }) + fireEvent.blur(shortNameInput) - await changeSingleSelect(result, 'Aggregation type (required)', 'Sum') + await changeSingleSelect( + result, + 'Category combination (required)', + 'None' + ) fireEvent.click(submitButton) diff --git a/src/pages/dataElements/New.tsx b/src/pages/dataElements/New.tsx index 8f03acba..c0d87c89 100644 --- a/src/pages/dataElements/New.tsx +++ b/src/pages/dataElements/New.tsx @@ -2,7 +2,7 @@ import { useDataEngine } from '@dhis2/app-runtime' import i18n from '@dhis2/d2-i18n' import { NoticeBox } from '@dhis2/ui' import { FORM_ERROR } from 'final-form' -import React, { useEffect, useRef } from 'react' +import React, { useEffect, useMemo, useRef } from 'react' import { Form } from 'react-final-form' import { useNavigate } from 'react-router-dom' import { @@ -10,42 +10,58 @@ import { StandardFormActions, StandardFormSection, } from '../../components' -import { SCHEMA_SECTIONS, getSectionPath } from '../../lib' +import { SCHEMA_SECTIONS, getSectionPath, useSchemas } from '../../lib' import { Attribute } from '../../types/generated' -import { DataElementFormFields, useCustomAttributesQuery } from './form' -import { FormValues } from './form/types' +import { + DataElementFormFields, + useCustomAttributesQuery, + useValidate, +} from './form' +import type { FormValues } from './form' import classes from './New.module.css' const listPath = `/${getSectionPath(SCHEMA_SECTIONS.dataElement)}` -function computeInitialValues(customAttributes: Attribute[]) { - const attributeValues = customAttributes.map((attribute) => ({ - attribute, - value: '', - })) +function useInitialValues(customAttributes: Attribute[]) { + const schemas = useSchemas() - return { - name: '', - shortName: '', - code: '', - description: '', - url: '', - style: { - color: '', - icon: '', - }, - fieldMask: '', - domainType: 'AGGREGATE', - formName: '', - valueType: '', - aggregationType: '', - categoryCombo: { id: '' }, - optionSet: { id: '' }, - commentOptionSet: { id: '' }, - legendSets: [], - aggregationLevels: [], - attributeValues, - } + const attributeValues = useMemo( + () => + customAttributes.map((attribute) => ({ + attribute, + value: '', + })), + [customAttributes] + ) + + return useMemo( + () => ({ + name: '', + shortName: '', + code: '', + description: '', + url: '', + fieldMask: '', + domainType: 'AGGREGATE', + formName: '', + valueType: schemas.dataElement.properties.valueType.constants?.[0], + aggregationType: + schemas.dataElement.properties.aggregationType.constants?.[0], + style: { icon: '', color: '' }, + categoryCombo: { id: '' }, + optionSet: { id: '' }, + commentOptionSet: { id: '' }, + legendSets: [], + aggregationLevels: [], + attributeValues, + zeroIsSignificant: false, + }), + [ + attributeValues, + schemas.dataElement.properties.valueType.constants, + schemas.dataElement.properties.aggregationType.constants, + ] + ) } const ADD_NEW_DATA_ELEMENT_MUTATION = { @@ -85,10 +101,25 @@ function formatFormValues({ values }: { values: FormValues }) { } export const Component = () => { + const validate = useValidate() const dataEngine = useDataEngine() const navigate = useNavigate() const customAttributesQuery = useCustomAttributesQuery() - const initialValues = computeInitialValues(customAttributesQuery.data || []) + + const loading = customAttributesQuery.loading + const error = customAttributesQuery.error + + const initialValues = useInitialValues(customAttributesQuery.data) + + if (error && !loading) { + // @TODO(Edit): Implement error screen + return <>Error: {error.toString()} + } + + if (loading) { + // @TODO(Edit): Implement loading screen + return <>Loading... + } async function onSubmit(values: FormValues) { const payload = formatFormValues({ values }) @@ -112,7 +143,12 @@ export const Component = () => { queryResponse={customAttributesQuery} label={i18n.t('Custom attributes')} > - + {({ handleSubmit, submitting, submitError }) => ( {i18n.t('Loading custom attributes')} + } + + if (error) { + return ( + + {error.toString()} + + ) + } + return ( <> - {customAttributes.map((customAttribute, index) => { + {customAttributes.data?.map((customAttribute, index) => { return ( + <> {i18n.t('Basic information')} + {i18n.t('Set up the information for this data element')} - (!value ? 'Required' : undefined)} - /> - - - - (!value ? 'Required' : undefined)} - inputWidth="400px" - name="shortName" - label={i18n.t('{{fieldLabel}} (required)', { - fieldLabel: i18n.t('Short name'), - })} - helpText={i18n.t( - 'Often used in reports where space is limited' - )} - /> - - - - - - - - - - - - + - + + + + + + + + + - + - + - + + + + + + + + + @@ -167,6 +92,7 @@ export function DataElementFormFields() { {i18n.t('Disaggregation and Option sets')} + {i18n.t('Set up disaggregation and predefined options.')} @@ -188,6 +114,7 @@ export function DataElementFormFields() { {i18n.t('LegendSet')} + {i18n.t( 'Visualize values for this data element in Analytics app. Multiple legendSet can be applied.' @@ -203,6 +130,7 @@ export function DataElementFormFields() { {i18n.t('Aggregation levels')} + {i18n.t( 'By default, the aggregation will start at the lowest assigned organisation unit. If you for example select "Chiefdom", it means that "Chiefdom", "District" and "National" aggregates use "Chiefdom" (the highest aggregation level available) as the data source, and PHU data will not be included. PHU will still be available for the PHU level, but not included in the aggregations to the levels above.' @@ -214,20 +142,17 @@ export function DataElementFormFields() { - {customAttributes.data?.length > 0 && ( - - - {i18n.t('Custom attributes')} - - - {i18n.t('Custom fields for your DHIS2 instance')} - - - - - )} - + + + {i18n.t('Custom attributes')} + + + + {i18n.t('Custom fields for your DHIS2 instance')} + + + + + ) } diff --git a/src/pages/dataElements/form/createDataElementSchema.ts b/src/pages/dataElements/form/createDataElementSchema.ts new file mode 100644 index 00000000..87a35f98 --- /dev/null +++ b/src/pages/dataElements/form/createDataElementSchema.ts @@ -0,0 +1,104 @@ +import i18n from '@dhis2/d2-i18n' +import { z } from 'zod' +import type { ModelSchemas } from '../../../lib' +import { DataElement } from '../../../types/generated' + +const requiredMessage = i18n.t('Required') +const max50Message = i18n.t('Cannot be longer than {{number}} character', { + number: 50, +}) + +export const createDataElementSchema = (schemas: ModelSchemas) => + z.object({ + name: z + .string() + .min(1, { message: requiredMessage }) + .max(schemas.dataElement.properties.name.length as number, { + message: max50Message, + }) + .trim(), + shortName: z + .string() + .min(1, { message: requiredMessage }) + .max(schemas.dataElement.properties.shortName.length as number, { + message: max50Message, + }) + .trim(), + code: z + .string() + .max(schemas.dataElement.properties.code.length as number, { + message: max50Message, + }) + .trim() + .optional(), + description: z + .string() + .max(schemas.dataElement.properties.description.length as number, { + message: i18n.t( + 'The value is to long. You can use up to 255 characters' + ), + }) + .trim() + .optional(), + formName: z + .string() + .max(schemas.dataElement.properties.formName.length as number, { + message: i18n.t( + 'The value is to long. You can use up to 255 characters' + ), + }) + .trim() + .optional(), + url: z + .string() + .max(schemas.dataElement.properties.url.length as number, { + message: i18n.t( + 'The value is to long. You can use up to 255 characters' + ), + }) + .trim() + .optional(), + fieldMask: z + .string() + .max(schemas.dataElement.properties.fieldMask.length as number, { + message: i18n.t( + 'The value is to long. You can use up to 255 characters' + ), + }) + .trim() + .optional(), + style: z.object({ + color: z.string().optional(), + icon: z.string().optional(), + }), + domainType: z.union([z.literal('AGGREGATE'), z.literal('TRACKER')]), + valueType: z + .nativeEnum(DataElement.valueType) + .refine((v) => !!v, { message: requiredMessage }), + aggregationType: z + .nativeEnum(DataElement.aggregationType) + .refine((v) => !!v, { message: requiredMessage }), + categoryCombo: z.object({ + id: z.string().min(1, { message: requiredMessage }), + }), + optionSet: z.object({ + id: z.string().optional(), + }), + commentOptionSet: z.object({ + id: z.string().optional(), + }), + legendSets: z.array( + z.object({ + id: z.string(), + }) + ), + aggregationLevels: z.array(z.number()), + attributeValues: z.array( + z.object({ + value: z.string(), + attribute: z.object({ + id: z.string(), + }), + }) + ), + }) diff --git a/src/pages/dataElements/form/customFields.tsx b/src/pages/dataElements/form/fields.tsx similarity index 63% rename from src/pages/dataElements/form/customFields.tsx rename to src/pages/dataElements/form/fields.tsx index 2bce4ada..a40ccc31 100644 --- a/src/pages/dataElements/form/customFields.tsx +++ b/src/pages/dataElements/form/fields.tsx @@ -2,13 +2,17 @@ import i18n from '@dhis2/d2-i18n' import { ButtonStrip, Button, + CheckboxFieldFF, Field, + InputFieldFF, Radio, SingleSelectFieldFF, + TextAreaFieldFF, } from '@dhis2/ui' import React, { useRef } from 'react' import { Field as FieldRFF, useField } from 'react-final-form' import { useHref } from 'react-router' +import { useParams } from 'react-router-dom' import { AggregationLevelMultiSelect, ColorAndIconPicker, @@ -24,10 +28,122 @@ import { } from '../../../lib' import classes from './customFields.module.css' import { EditableFieldWrapper } from './EditableFieldWrapper' +import { useIsFieldValueUnique } from './useIsFieldValueUnique' + +export function NameField() { + const params = useParams() + const dataElementId = params.id as string + const checkIsValueTaken = useIsFieldValueUnique({ + field: 'name', + id: dataElementId, + }) + const { meta } = useField('name', { + subscription: { validating: true }, + }) + + return ( + checkIsValueTaken(name)} + validateFields={[]} + /> + ) +} + +export function ShortNameField() { + const params = useParams() + const dataElementId = params.id as string + const checkIsValueTaken = useIsFieldValueUnique({ + field: 'shortName', + id: dataElementId, + }) + const { meta } = useField('shortName', { + subscription: { validating: true }, + }) + + return ( + checkIsValueTaken(shortName)} + validateFields={[]} + /> + ) +} + +export function CodeField() { + return ( + + ) +} + +export function DescriptionField() { + return ( + + ) +} + +export function UrlField() { + return ( + + ) +} export function ColorAndIconField() { - const { input: colorInput } = useField('style.color') - const { input: iconInput } = useField('style.icon') + const { input: colorInput } = useField('style.color', { + validateFields: [], + }) + const { input: iconInput } = useField('style.icon', { + validateFields: [], + }) return ( + ) +} + +export function FormNameField() { + return ( + + ) +} + +export function ZeroIsSignificantField() { + return ( + + ) +} + export function DomainField() { const name = 'domainType' - const validate = (value: string) => (!value ? 'Required' : undefined) const aggregateInput = useField(name, { type: 'radio', value: 'AGGREGATE', - validate, + validateFields: [], }) const trackerInput = useField(name, { type: 'radio', value: 'TRACKER', - validate, + validateFields: [], }) + const touched = aggregateInput.meta.touched || trackerInput.meta.touched const error = aggregateInput.meta.error || trackerInput.meta.error return ( @@ -77,8 +239,8 @@ export function DomainField() { helpText={i18n.t( 'A data element can either be aggregated or tracked data.' )} - error={!!error} - validationText={error} + error={touched && !!error} + validationText={touched ? error : undefined} >
legendSets?.map((legendSet) => legendSet.id), parse: (ids: string[]) => ids.map((id) => ({ id })), + validateFields: [], }) const newLegendSetLink = useHref('/legendSets/new') @@ -177,10 +340,9 @@ export function ValueTypeField() { ) return ( (!value ? 'Required' : undefined)} inputWidth="400px" name="valueType" label={i18n.t('{{fieldLabel}} (required)', { @@ -188,6 +350,7 @@ export function ValueTypeField() { })} helpText={i18n.t('The type of data that will be recorded.')} options={options || []} + validateFields={[]} /> ) } @@ -206,7 +369,6 @@ export function AggregationTypeField() { component={SingleSelectFieldFF} dataTest="dataelementsformfields-aggregationtype" required - validate={(value: string) => (!value ? 'Required' : undefined)} inputWidth="400px" name="aggregationType" label={i18n.t('{{fieldLabel}} (required)', { @@ -216,13 +378,16 @@ export function AggregationTypeField() { 'The default way to aggregate this data element in analytics.' )} options={options || []} + validateFields={[]} /> ) } export function CategoryComboField() { const newCategoryComboLink = useHref('/categoryCombos/new') - const { input, meta } = useField('categoryCombo.id') + const { input, meta } = useField('categoryCombo.id', { + validateFields: [], + }) const categoryComboHandle = useRef({ refetch: () => { throw new Error('Not initialized') @@ -238,9 +403,6 @@ export function CategoryComboField() {
- !value ? 'Required' : undefined - } name="categoryCombo.id" label={i18n.t('{{fieldLabel}} (required)', { fieldLabel: i18n.t('Category combination'), @@ -248,14 +410,24 @@ export function CategoryComboField() { helpText={i18n.t( 'Choose how this data element is disaggregated' )} - validationText={meta.error} + error={meta.touched && !!meta.error} + validationText={meta.touched ? meta.error : undefined} + dataTest="dataelementsformfields-categorycombo" > input.onChange(selected)} + onChange={({ selected }) => { + input.onChange(selected) + + // Our selects don't trigger a blur event when + // selecting a value, but the select is not focused + // anymore either + input.onBlur() + }} onBlur={input.onBlur} onFocus={input.onFocus} /> @@ -267,7 +439,9 @@ export function CategoryComboField() { export function OptionSetField() { const newOptionSetLink = useHref('/optionSets/new') - const { input, meta } = useField('optionSet.id') + const { input, meta } = useField('optionSet.id', { + validateFields: [], + }) const optionSetHandle = useRef({ refetch: () => { throw new Error('Not initialized') @@ -287,10 +461,13 @@ export function OptionSetField() { helpText={i18n.t( 'Choose a set of predefined options for data entry' )} - validationText={meta.error} + validationText={meta.touched ? meta.error : undefined} + error={meta.touched && !!meta.error} + dataTest="dataelementsformfields-optionset" > input.onChange(selected)} @@ -305,7 +482,9 @@ export function OptionSetField() { export function OptionSetCommentField() { const newOptionSetLink = useHref('/optionSets/new') - const { input, meta } = useField('commentOptionSet.id') + const { input, meta } = useField('commentOptionSet.id', { + validateFields: [], + }) const optionSetHandle = useRef({ refetch: () => { throw new Error('Not initialized') @@ -325,10 +504,13 @@ export function OptionSetCommentField() { helpText={i18n.t( 'Choose a set of predefined comment for data entry' )} - validationText={meta.error} + validationText={meta.touched ? meta.error : undefined} + error={meta.touched && !!meta.error} + dataTest="dataelementsformfields-commentoptionset" > input.onChange(selected)} @@ -347,6 +529,7 @@ export function AggregationLevelsField() { multiple: true, format: (levels: number[]) => levels.map((level) => level.toString()), parse: (levels: string[]) => levels.map((level) => parseInt(level, 10)), + validateFields: [], }) const aggregationLevelHandle = useRef({ refetch: () => { @@ -367,10 +550,13 @@ export function AggregationLevelsField() { helpText={i18n.t( 'Choose how this data element is disaggregated' )} - validationText={meta.error} + validationText={meta.touched ? meta.error : undefined} + error={meta.touched && !!meta.error} + dataTest="dataelementsformfields-aggregationlevels" > ) => { + const filter = [`${variables.field}:eq:${variables.value}`] + + if (variables.id) { + filter.push(`id:ne:${variables.id}`) + } + + return { pageSize: 1, fields: 'id', filter } + }, + }, +} + +interface QueryResponse { + dataElements: { + pager: Pager + } +} + +export function useIsFieldValueUnique({ + field, + id, +}: { + field: string + id: string +}) { + const engine = useDataEngine() + + const validate = useMemo( + () => + memoize(async (value: string) => { + if (!value) { + return undefined + } + + const data = (await engine.query(HAS_FIELD_VALUE_QUERY, { + variables: { field, value, id }, + })) as unknown as QueryResponse + + if (data.dataElements.pager.total > 0) { + return i18n.t( + 'This field requires a unique value, please choose another one' + ) + } + }), + [field, engine, id] + ) + + return useDebouncedCallback(validate, 200, { leading: true }) +} diff --git a/src/pages/dataElements/form/useValidate.ts b/src/pages/dataElements/form/useValidate.ts new file mode 100644 index 00000000..2d70d326 --- /dev/null +++ b/src/pages/dataElements/form/useValidate.ts @@ -0,0 +1,40 @@ +import { setIn } from 'final-form' +import { useMemo } from 'react' +import { useSchemas } from '../../../lib' +import { createDataElementSchema } from './createDataElementSchema' +import type { FormValues } from './types' + +// @TODO: Figure out if there's a utility for this? I couldn't find one +function segmentsToPath(segments: Array) { + return segments.reduce((path, segment) => { + return typeof segment === 'number' + ? `${path}[${segment}]` + : `${path}.${segment}` + }) as string +} + +export function useValidate() { + const schemas = useSchemas() + const dataElementSchema = useMemo( + () => createDataElementSchema(schemas), + [schemas] + ) + + return (values: FormValues) => { + const zodResult = dataElementSchema.safeParse(values) + + if (zodResult.success !== false) { + return undefined + } + + const allFormErrors = zodResult.error.issues.reduce( + (formErrors, error) => { + const errorPath = segmentsToPath(error.path) + return setIn(formErrors, errorPath, error.message) + }, + {} + ) + + return allFormErrors + } +} diff --git a/src/types/generated/index.ts b/src/types/generated/index.ts index fbd945bd..2084e0d4 100644 --- a/src/types/generated/index.ts +++ b/src/types/generated/index.ts @@ -1,4 +1,4 @@ /* GENERATED BY https://github.com/Birkbjo/dhis2-open-api-ts */ -export type * from './models' +export * from './models' export type * from './utility' diff --git a/yarn.lock b/yarn.lock index 9a9247c9..34a2574d 100644 --- a/yarn.lock +++ b/yarn.lock @@ -1407,18 +1407,6 @@ debug "^3.1.0" lodash.once "^4.1.1" -"@dhis2-ui/alert@8.13.15": - version "8.13.15" - resolved "https://registry.yarnpkg.com/@dhis2-ui/alert/-/alert-8.13.15.tgz#5f9a9665e751bc94d96b1d2993683731ac63c019" - integrity sha512-6KiE4TGaJC3FdChyc5FvghInSlPl4hZNDjukq5lYy0XXgnaKBSz9yUPmod5+NLHFsEr0DCISBKFhVeKAexz/hg== - dependencies: - "@dhis2-ui/portal" "8.13.15" - "@dhis2/prop-types" "^3.1.2" - "@dhis2/ui-constants" "8.13.15" - "@dhis2/ui-icons" "8.13.15" - classnames "^2.3.1" - prop-types "^15.7.2" - "@dhis2-ui/alert@8.14.7": version "8.14.7" resolved "https://registry.yarnpkg.com/@dhis2-ui/alert/-/alert-8.14.7.tgz#53cdc9bbdceee01dd5b86d0615a1c57314e0e307" @@ -1431,16 +1419,6 @@ classnames "^2.3.1" prop-types "^15.7.2" -"@dhis2-ui/box@8.13.15": - version "8.13.15" - resolved "https://registry.yarnpkg.com/@dhis2-ui/box/-/box-8.13.15.tgz#fb8fce7ee20e27b9e25e7a294c1109a881b123a4" - integrity sha512-SwzPGXbySpU/e0lJyxOfxXipFAc3C9GKKsSQYKVOo2/kxwZDui9zaUcw1hCv4rK4vy2hB1b2GLEvJLnQAE3VIQ== - dependencies: - "@dhis2/prop-types" "^3.1.2" - "@dhis2/ui-constants" "8.13.15" - classnames "^2.3.1" - prop-types "^15.7.2" - "@dhis2-ui/box@8.14.7": version "8.14.7" resolved "https://registry.yarnpkg.com/@dhis2-ui/box/-/box-8.14.7.tgz#1f7eabb6da21e67d95a49b5410af151cf61de92b" @@ -1451,20 +1429,6 @@ classnames "^2.3.1" prop-types "^15.7.2" -"@dhis2-ui/button@8.13.15": - version "8.13.15" - resolved "https://registry.yarnpkg.com/@dhis2-ui/button/-/button-8.13.15.tgz#0bcb2365a9dd200c303b4bea003a5b403382c981" - integrity sha512-rzHPLBAbG6Q0k8JZU21eWABa/+GqOvl9WV6ay8ODahJmvauOpKKNOYn2AfUZEjbwrWgVbiuGhPOvnC+bVI+jDw== - dependencies: - "@dhis2-ui/layer" "8.13.15" - "@dhis2-ui/loader" "8.13.15" - "@dhis2-ui/popper" "8.13.15" - "@dhis2/prop-types" "^3.1.2" - "@dhis2/ui-constants" "8.13.15" - "@dhis2/ui-icons" "8.13.15" - classnames "^2.3.1" - prop-types "^15.7.2" - "@dhis2-ui/button@8.14.7": version "8.14.7" resolved "https://registry.yarnpkg.com/@dhis2-ui/button/-/button-8.14.7.tgz#c241b42d0bbb98c594a8183f1bc432be4a5e23af" @@ -1479,23 +1443,6 @@ classnames "^2.3.1" prop-types "^15.7.2" -"@dhis2-ui/calendar@8.13.15": - version "8.13.15" - resolved "https://registry.yarnpkg.com/@dhis2-ui/calendar/-/calendar-8.13.15.tgz#d37395674df24565258d76aa6f771f76801c8adb" - integrity sha512-/2FVtXI6vGQlFvPul3I/EdWJfpo3QXx5aF/YKZVEEQaM5nhVmdLMVqNoIvNGzh1PJsAyrc6JhqdqqbK5AO2u0w== - dependencies: - "@dhis2-ui/button" "8.13.15" - "@dhis2-ui/card" "8.13.15" - "@dhis2-ui/input" "8.13.15" - "@dhis2-ui/layer" "8.13.15" - "@dhis2-ui/popper" "8.13.15" - "@dhis2/multi-calendar-dates" "1.0.2" - "@dhis2/prop-types" "^3.1.2" - "@dhis2/ui-constants" "8.13.15" - "@dhis2/ui-icons" "8.13.15" - classnames "^2.3.1" - prop-types "^15.7.2" - "@dhis2-ui/calendar@8.14.7": version "8.14.7" resolved "https://registry.yarnpkg.com/@dhis2-ui/calendar/-/calendar-8.14.7.tgz#16453c22bfe707f93e695318c6526c14137a4adf" @@ -1513,16 +1460,6 @@ classnames "^2.3.1" prop-types "^15.7.2" -"@dhis2-ui/card@8.13.15": - version "8.13.15" - resolved "https://registry.yarnpkg.com/@dhis2-ui/card/-/card-8.13.15.tgz#87e67ebc2c92512fb729016571d0be40cbf079ca" - integrity sha512-70VZHAuHGBqw4AKBJd1HgcsMyTfQ++yzip5c8ykLU7AZQi7WS8W/fMDvV9DGFE5Lc3JyIZdr15Uh31d/v+EBUA== - dependencies: - "@dhis2/prop-types" "^3.1.2" - "@dhis2/ui-constants" "8.13.15" - classnames "^2.3.1" - prop-types "^15.7.2" - "@dhis2-ui/card@8.14.7": version "8.14.7" resolved "https://registry.yarnpkg.com/@dhis2-ui/card/-/card-8.14.7.tgz#ee944a2c86a8f3c397da32b2c0adf3ca956a06b8" @@ -1533,16 +1470,6 @@ classnames "^2.3.1" prop-types "^15.7.2" -"@dhis2-ui/center@8.13.15": - version "8.13.15" - resolved "https://registry.yarnpkg.com/@dhis2-ui/center/-/center-8.13.15.tgz#fd434cd37e6d5eb4a01b4830c2c316fbb814c188" - integrity sha512-+YQFxzNrwYQWsZZYEHM4ywzaGbrjfQWcK4WIPAHifVYau2efPDXAMGQ8cpDrRUivpxCYi3Rpvpp965Dp5+KOYQ== - dependencies: - "@dhis2/prop-types" "^3.1.2" - "@dhis2/ui-constants" "8.13.15" - classnames "^2.3.1" - prop-types "^15.7.2" - "@dhis2-ui/center@8.14.7": version "8.14.7" resolved "https://registry.yarnpkg.com/@dhis2-ui/center/-/center-8.14.7.tgz#374d87df7570000549312d4d42d42f1ba8af1dfd" @@ -1553,18 +1480,6 @@ classnames "^2.3.1" prop-types "^15.7.2" -"@dhis2-ui/checkbox@8.13.15": - version "8.13.15" - resolved "https://registry.yarnpkg.com/@dhis2-ui/checkbox/-/checkbox-8.13.15.tgz#ed8beaf55fe32c222f35e2ee2497191dee2ddefe" - integrity sha512-fhi50vBK8v5fXzkNOcZlZR8pxO72GwwVKuTmDkrDeAIKIiNQuNZoxkAkU3vwEGv/rdO3TREbAfzTbtjRui3XHQ== - dependencies: - "@dhis2-ui/field" "8.13.15" - "@dhis2-ui/required" "8.13.15" - "@dhis2/prop-types" "^3.1.2" - "@dhis2/ui-constants" "8.13.15" - classnames "^2.3.1" - prop-types "^15.7.2" - "@dhis2-ui/checkbox@8.14.7": version "8.14.7" resolved "https://registry.yarnpkg.com/@dhis2-ui/checkbox/-/checkbox-8.14.7.tgz#ae96afc1063fddd642a72b37f7a18c8574e2311e" @@ -1577,16 +1492,6 @@ classnames "^2.3.1" prop-types "^15.7.2" -"@dhis2-ui/chip@8.13.15": - version "8.13.15" - resolved "https://registry.yarnpkg.com/@dhis2-ui/chip/-/chip-8.13.15.tgz#efd576e7556b241c852b7176762e70e22f84b2e0" - integrity sha512-MJSKIX/KIZBKjQPhIUTwMasmvY4M2ftlYQ61d9Px1YAuivuE+VTvGCsFF0q5EAKMx5ETd6SD1X3PrwzdtHru4Q== - dependencies: - "@dhis2/prop-types" "^3.1.2" - "@dhis2/ui-constants" "8.13.15" - classnames "^2.3.1" - prop-types "^15.7.2" - "@dhis2-ui/chip@8.14.7": version "8.14.7" resolved "https://registry.yarnpkg.com/@dhis2-ui/chip/-/chip-8.14.7.tgz#b57398e867d4a06847a33faad054ed67f2a195c0" @@ -1597,16 +1502,6 @@ classnames "^2.3.1" prop-types "^15.7.2" -"@dhis2-ui/cover@8.13.15": - version "8.13.15" - resolved "https://registry.yarnpkg.com/@dhis2-ui/cover/-/cover-8.13.15.tgz#28ac0f54a39f217518a0df501665829188d5945d" - integrity sha512-3e4XwsnhaVNSNTF2wNk6V+3CzsKCZuLsqMq049ScdaCCrv83qro1cRfRTNqNUJiXnmTubrgU8yxV0kXrFIKZJg== - dependencies: - "@dhis2/prop-types" "^3.1.2" - "@dhis2/ui-constants" "8.13.15" - classnames "^2.3.1" - prop-types "^15.7.2" - "@dhis2-ui/cover@8.14.7": version "8.14.7" resolved "https://registry.yarnpkg.com/@dhis2-ui/cover/-/cover-8.14.7.tgz#c023217b961f2becc479566468a9a0df96cdb0c0" @@ -1617,16 +1512,6 @@ classnames "^2.3.1" prop-types "^15.7.2" -"@dhis2-ui/css@8.13.15": - version "8.13.15" - resolved "https://registry.yarnpkg.com/@dhis2-ui/css/-/css-8.13.15.tgz#8e8073a0c69070f598c27abdef52c36981d77421" - integrity sha512-9aAWUpE36SVS17AQXz3x9KkoRpIkvruat+tk2eabw0vfTB6xVRPzC1oO28mltYhpWNOLLS+k1USwfhDSiiqASw== - dependencies: - "@dhis2/prop-types" "^3.1.2" - "@dhis2/ui-constants" "8.13.15" - classnames "^2.3.1" - prop-types "^15.7.2" - "@dhis2-ui/css@8.14.7": version "8.14.7" resolved "https://registry.yarnpkg.com/@dhis2-ui/css/-/css-8.14.7.tgz#8047e2ed197ec4ad03ad69e374a57e5b4eb914fe" @@ -1637,16 +1522,6 @@ classnames "^2.3.1" prop-types "^15.7.2" -"@dhis2-ui/divider@8.13.15": - version "8.13.15" - resolved "https://registry.yarnpkg.com/@dhis2-ui/divider/-/divider-8.13.15.tgz#c4ef063b31aa80735c2288dd9f5cbaa2c79b123e" - integrity sha512-bIQKnaCddH2Pr9e3j3CFFWMvGR51gvw71G4NTHsNGZTWNbVL54tZo4ifhkmkfNvf3cLuF/V6IlpQzrf/X8Iu9g== - dependencies: - "@dhis2/prop-types" "^3.1.2" - "@dhis2/ui-constants" "8.13.15" - classnames "^2.3.1" - prop-types "^15.7.2" - "@dhis2-ui/divider@8.14.7": version "8.14.7" resolved "https://registry.yarnpkg.com/@dhis2-ui/divider/-/divider-8.14.7.tgz#ba3ae31e5e62b610ca9f2e7e041ad623116a73fd" @@ -1657,19 +1532,6 @@ classnames "^2.3.1" prop-types "^15.7.2" -"@dhis2-ui/field@8.13.15": - version "8.13.15" - resolved "https://registry.yarnpkg.com/@dhis2-ui/field/-/field-8.13.15.tgz#f91ca25936ea926e82ac17c62b28faa2eae2898b" - integrity sha512-2aBumqc77kBC0gl0A+zGOv+en/pu2YUNYwEEpvq5TE2NOvSktWgUOR6Pk/5NDGtB8ugkxgdjAbgcOk/SRuxQJA== - dependencies: - "@dhis2-ui/box" "8.13.15" - "@dhis2-ui/help" "8.13.15" - "@dhis2-ui/label" "8.13.15" - "@dhis2/prop-types" "^3.1.2" - "@dhis2/ui-constants" "8.13.15" - classnames "^2.3.1" - prop-types "^15.7.2" - "@dhis2-ui/field@8.14.7": version "8.14.7" resolved "https://registry.yarnpkg.com/@dhis2-ui/field/-/field-8.14.7.tgz#48e27a454b65ddc09b8025a8cfd64505e8448975" @@ -1683,22 +1545,6 @@ classnames "^2.3.1" prop-types "^15.7.2" -"@dhis2-ui/file-input@8.13.15": - version "8.13.15" - resolved "https://registry.yarnpkg.com/@dhis2-ui/file-input/-/file-input-8.13.15.tgz#df974db2ea5cf072b63b4336c84eaa5f989dbcaa" - integrity sha512-il5j+6s+874XX/uS4teXQCrFjnmJgVg5RTViZ5toDUZRUpjx86zLKUcTDANfrxG3eUR+F4dkdsvccXFTkA1dTg== - dependencies: - "@dhis2-ui/button" "8.13.15" - "@dhis2-ui/field" "8.13.15" - "@dhis2-ui/label" "8.13.15" - "@dhis2-ui/loader" "8.13.15" - "@dhis2-ui/status-icon" "8.13.15" - "@dhis2/prop-types" "^3.1.2" - "@dhis2/ui-constants" "8.13.15" - "@dhis2/ui-icons" "8.13.15" - classnames "^2.3.1" - prop-types "^15.7.2" - "@dhis2-ui/file-input@8.14.7": version "8.14.7" resolved "https://registry.yarnpkg.com/@dhis2-ui/file-input/-/file-input-8.14.7.tgz#c9278b157bd5ef457a99988c627557c280154388" @@ -1715,30 +1561,6 @@ classnames "^2.3.1" prop-types "^15.7.2" -"@dhis2-ui/header-bar@8.13.15": - version "8.13.15" - resolved "https://registry.yarnpkg.com/@dhis2-ui/header-bar/-/header-bar-8.13.15.tgz#40d5c5fb0ba3ff5dce2870125ededa83cd3f1cc6" - integrity sha512-OjGKPU0ia+q53NM0hZMfnNkb8Sh/Ty4q/WcDw9qr0Yzow29G30Z4TH+B8le9SSvUBXBiZ8Q/Rv170JMON0Zygg== - dependencies: - "@dhis2-ui/box" "8.13.15" - "@dhis2-ui/button" "8.13.15" - "@dhis2-ui/card" "8.13.15" - "@dhis2-ui/center" "8.13.15" - "@dhis2-ui/divider" "8.13.15" - "@dhis2-ui/input" "8.13.15" - "@dhis2-ui/layer" "8.13.15" - "@dhis2-ui/loader" "8.13.15" - "@dhis2-ui/logo" "8.13.15" - "@dhis2-ui/menu" "8.13.15" - "@dhis2-ui/modal" "8.13.15" - "@dhis2-ui/user-avatar" "8.13.15" - "@dhis2/prop-types" "^3.1.2" - "@dhis2/ui-constants" "8.13.15" - "@dhis2/ui-icons" "8.13.15" - classnames "^2.3.1" - moment "^2.29.1" - prop-types "^15.7.2" - "@dhis2-ui/header-bar@8.14.7": version "8.14.7" resolved "https://registry.yarnpkg.com/@dhis2-ui/header-bar/-/header-bar-8.14.7.tgz#e7291dee9c6a5d4f95f6665fe47ae8695af102fc" @@ -1763,16 +1585,6 @@ moment "^2.29.1" prop-types "^15.7.2" -"@dhis2-ui/help@8.13.15": - version "8.13.15" - resolved "https://registry.yarnpkg.com/@dhis2-ui/help/-/help-8.13.15.tgz#a3c1adcc655afcc9f24cf13aa9f0597b24593810" - integrity sha512-7obQDeD0dZOyf+Sm808EZeTeCLIFndd0fdPD1vTmTOnevv+W2grT7dCwsFwZ2AsX+GLbzzd+10ikXE8da6OPPQ== - dependencies: - "@dhis2/prop-types" "^3.1.2" - "@dhis2/ui-constants" "8.13.15" - classnames "^2.3.1" - prop-types "^15.7.2" - "@dhis2-ui/help@8.14.7": version "8.14.7" resolved "https://registry.yarnpkg.com/@dhis2-ui/help/-/help-8.14.7.tgz#fad62ccf6c0d93251b189638ce3a483964acb86a" @@ -1783,22 +1595,6 @@ classnames "^2.3.1" prop-types "^15.7.2" -"@dhis2-ui/input@8.13.15": - version "8.13.15" - resolved "https://registry.yarnpkg.com/@dhis2-ui/input/-/input-8.13.15.tgz#cdeb7bf5b63814b4a571b843e580527868f57f32" - integrity sha512-8K4yqeZmYQpvNA+TRWtFwWWC4HbzgotxJ6FoXdUJ34LVHQZJNCaRC0L0B1lHLCnP41WHnY0uYcFovY0kl0G/vA== - dependencies: - "@dhis2-ui/box" "8.13.15" - "@dhis2-ui/field" "8.13.15" - "@dhis2-ui/input" "8.13.15" - "@dhis2-ui/loader" "8.13.15" - "@dhis2-ui/status-icon" "8.13.15" - "@dhis2/prop-types" "^3.1.2" - "@dhis2/ui-constants" "8.13.15" - "@dhis2/ui-icons" "8.13.15" - classnames "^2.3.1" - prop-types "^15.7.2" - "@dhis2-ui/input@8.14.7": version "8.14.7" resolved "https://registry.yarnpkg.com/@dhis2-ui/input/-/input-8.14.7.tgz#8ab3bb228932bd52a3fd06eb546dba5b7d76eb15" @@ -1815,16 +1611,6 @@ classnames "^2.3.1" prop-types "^15.7.2" -"@dhis2-ui/intersection-detector@8.13.15": - version "8.13.15" - resolved "https://registry.yarnpkg.com/@dhis2-ui/intersection-detector/-/intersection-detector-8.13.15.tgz#460389648c7cd95a45b881db7a4f997ca7fce045" - integrity sha512-M7gaWb50eu3LPoU6vd5CAz9uLDsyFmacr7RCUQO5FlBQ/cGU6Xvfd8+oaH5R7PqP/YSyNsPi4yP9cpXSlzU1Gw== - dependencies: - "@dhis2/prop-types" "^3.1.2" - "@dhis2/ui-constants" "8.13.15" - classnames "^2.3.1" - prop-types "^15.7.2" - "@dhis2-ui/intersection-detector@8.14.7": version "8.14.7" resolved "https://registry.yarnpkg.com/@dhis2-ui/intersection-detector/-/intersection-detector-8.14.7.tgz#4b510471b5b1434da8ca31eb7c8df789a29cdc2c" @@ -1835,17 +1621,6 @@ classnames "^2.3.1" prop-types "^15.7.2" -"@dhis2-ui/label@8.13.15": - version "8.13.15" - resolved "https://registry.yarnpkg.com/@dhis2-ui/label/-/label-8.13.15.tgz#6a5a42fce24f2d11507f58c3a4782d50ac2fe764" - integrity sha512-+lC8gJTxB81n0/83Nfs1KrgkR7SitdHb40nZh3/VgfRzWJzxESL/VMpA/dz1Y+7eZ8To/exRYvrfoHGI6qpvog== - dependencies: - "@dhis2-ui/required" "8.13.15" - "@dhis2/prop-types" "^3.1.2" - "@dhis2/ui-constants" "8.13.15" - classnames "^2.3.1" - prop-types "^15.7.2" - "@dhis2-ui/label@8.14.7": version "8.14.7" resolved "https://registry.yarnpkg.com/@dhis2-ui/label/-/label-8.14.7.tgz#64cce8f2cb3433fdc5feffbeda1d7d58b4d63df4" @@ -1857,17 +1632,6 @@ classnames "^2.3.1" prop-types "^15.7.2" -"@dhis2-ui/layer@8.13.15": - version "8.13.15" - resolved "https://registry.yarnpkg.com/@dhis2-ui/layer/-/layer-8.13.15.tgz#ab0f15ea5dea3666718faf08f0e812830ec536c4" - integrity sha512-OWlwvW1a4/SKdYCy+Dc7OkGT2Cc+6GHIHRDTQwNQdBGUAtosx/AmsmBv+sCPKl2CMaZQTwL7dRbN/80YOWdklA== - dependencies: - "@dhis2-ui/portal" "8.13.15" - "@dhis2/prop-types" "^3.1.2" - "@dhis2/ui-constants" "8.13.15" - classnames "^2.3.1" - prop-types "^15.7.2" - "@dhis2-ui/layer@8.14.7": version "8.14.7" resolved "https://registry.yarnpkg.com/@dhis2-ui/layer/-/layer-8.14.7.tgz#ab95b3bdfe023e4ceaba93339e5fae165a148c1c" @@ -1879,17 +1643,6 @@ classnames "^2.3.1" prop-types "^15.7.2" -"@dhis2-ui/legend@8.13.15": - version "8.13.15" - resolved "https://registry.yarnpkg.com/@dhis2-ui/legend/-/legend-8.13.15.tgz#0ba933eac4839d31b37083b4291e637c99a9188e" - integrity sha512-KE5+YBs+YUHmUB1EKfWa/yWc5WcICP3+RH1g9zqK+L6FmO+iH+pFHzF+grtaeOM7ZbrqvXGSamK+UbUYlcn5TA== - dependencies: - "@dhis2-ui/required" "8.13.15" - "@dhis2/prop-types" "^3.1.2" - "@dhis2/ui-constants" "8.13.15" - classnames "^2.3.1" - prop-types "^15.7.2" - "@dhis2-ui/legend@8.14.7": version "8.14.7" resolved "https://registry.yarnpkg.com/@dhis2-ui/legend/-/legend-8.14.7.tgz#56f595da523e8a1dbf1728e8beaf2cff75536612" @@ -1901,16 +1654,6 @@ classnames "^2.3.1" prop-types "^15.7.2" -"@dhis2-ui/loader@8.13.15": - version "8.13.15" - resolved "https://registry.yarnpkg.com/@dhis2-ui/loader/-/loader-8.13.15.tgz#726a3f7c43ec268d1ba4fa115e2ff45f4d983017" - integrity sha512-P/uMn84ME4NVlVNnHcCjbf6Gi042tuRHxAWoYf/R35rYoKnVCJTul4dKf1JCBNRJUpQtEuaT/EGk0G11iZIBog== - dependencies: - "@dhis2/prop-types" "^3.1.2" - "@dhis2/ui-constants" "8.13.15" - classnames "^2.3.1" - prop-types "^15.7.2" - "@dhis2-ui/loader@8.14.7": version "8.14.7" resolved "https://registry.yarnpkg.com/@dhis2-ui/loader/-/loader-8.14.7.tgz#77b951c45f72de4488704d59e8da8d86b680f0e8" @@ -1921,16 +1664,6 @@ classnames "^2.3.1" prop-types "^15.7.2" -"@dhis2-ui/logo@8.13.15": - version "8.13.15" - resolved "https://registry.yarnpkg.com/@dhis2-ui/logo/-/logo-8.13.15.tgz#c1c9fe8a6e0ed3055027202790d19e6234b01eee" - integrity sha512-dRW5doMz57kjn+CeHtRXy5eVjkHj2xFzU0CA4A+pfTb6mXlwDrC8C7ixpbwIcYky51AS02zKaygoGmODRZt9Ow== - dependencies: - "@dhis2/prop-types" "^3.1.2" - "@dhis2/ui-constants" "8.13.15" - classnames "^2.3.1" - prop-types "^15.7.2" - "@dhis2-ui/logo@8.14.7": version "8.14.7" resolved "https://registry.yarnpkg.com/@dhis2-ui/logo/-/logo-8.14.7.tgz#3b0c28badf9d30c2c22df8688782d1650da28973" @@ -1941,22 +1674,6 @@ classnames "^2.3.1" prop-types "^15.7.2" -"@dhis2-ui/menu@8.13.15": - version "8.13.15" - resolved "https://registry.yarnpkg.com/@dhis2-ui/menu/-/menu-8.13.15.tgz#9193a0fc5cc2610ad5b9a68b97acd0f1f218be70" - integrity sha512-dzkRRRk2eogzHlJuR6JONd7NeVU1Ct1zmhlsHDqTHc8p1vO3/FYIF6SWBJErFTXmWSs9kYA4WnZHk/xiyKWFQA== - dependencies: - "@dhis2-ui/card" "8.13.15" - "@dhis2-ui/divider" "8.13.15" - "@dhis2-ui/layer" "8.13.15" - "@dhis2-ui/popper" "8.13.15" - "@dhis2-ui/portal" "8.13.15" - "@dhis2/prop-types" "^3.1.2" - "@dhis2/ui-constants" "8.13.15" - "@dhis2/ui-icons" "8.13.15" - classnames "^2.3.1" - prop-types "^15.7.2" - "@dhis2-ui/menu@8.14.7": version "8.14.7" resolved "https://registry.yarnpkg.com/@dhis2-ui/menu/-/menu-8.14.7.tgz#20c448da00c1fb543000c34914b49b3b135a633a" @@ -1973,21 +1690,6 @@ classnames "^2.3.1" prop-types "^15.7.2" -"@dhis2-ui/modal@8.13.15": - version "8.13.15" - resolved "https://registry.yarnpkg.com/@dhis2-ui/modal/-/modal-8.13.15.tgz#d0865e7b60bc4baf2865133007d730f959592713" - integrity sha512-ptCxzIXnHv9AOcwW33XgmMqb072Z8ZishfjLzlZVhNdiFbwBmCvr+yu5zpTj0I0fISotz7fpAF38tWIsp9CPOA== - dependencies: - "@dhis2-ui/card" "8.13.15" - "@dhis2-ui/center" "8.13.15" - "@dhis2-ui/layer" "8.13.15" - "@dhis2-ui/portal" "8.13.15" - "@dhis2/prop-types" "^3.1.2" - "@dhis2/ui-constants" "8.13.15" - "@dhis2/ui-icons" "8.13.15" - classnames "^2.3.1" - prop-types "^15.7.2" - "@dhis2-ui/modal@8.14.7": version "8.14.7" resolved "https://registry.yarnpkg.com/@dhis2-ui/modal/-/modal-8.14.7.tgz#028dfd3a1f8e740f4d47a0ba6baec50184da05d4" @@ -2003,17 +1705,6 @@ classnames "^2.3.1" prop-types "^15.7.2" -"@dhis2-ui/node@8.13.15": - version "8.13.15" - resolved "https://registry.yarnpkg.com/@dhis2-ui/node/-/node-8.13.15.tgz#c0221389511bf571fc08f9b3c55963b37d65aac3" - integrity sha512-yG39zK1UEBnABqfrVL9dnoZWaqUY4WTnkfHOwxBI8k7DhTDmoFA/Otqlfu1ILgdnz/W1SLpxGhqRqa1pg4m4Gg== - dependencies: - "@dhis2-ui/loader" "8.13.15" - "@dhis2/prop-types" "^3.1.2" - "@dhis2/ui-constants" "8.13.15" - classnames "^2.3.1" - prop-types "^15.7.2" - "@dhis2-ui/node@8.14.7": version "8.14.7" resolved "https://registry.yarnpkg.com/@dhis2-ui/node/-/node-8.14.7.tgz#90289ffb9ddc649bbc9702bf76d7eaab764a6885" @@ -2025,17 +1716,6 @@ classnames "^2.3.1" prop-types "^15.7.2" -"@dhis2-ui/notice-box@8.13.15": - version "8.13.15" - resolved "https://registry.yarnpkg.com/@dhis2-ui/notice-box/-/notice-box-8.13.15.tgz#e53a8f4db842bae0966b8a562bafc685cf17af83" - integrity sha512-vY7vAwlRQMYZvNvByVRQqGTjTnMHbKPWWcNlAx23otZDIPJG4z+hxBV0dX+e6hKpZFjtW+5zAOMe4JgS8McrmA== - dependencies: - "@dhis2/prop-types" "^3.1.2" - "@dhis2/ui-constants" "8.13.15" - "@dhis2/ui-icons" "8.13.15" - classnames "^2.3.1" - prop-types "^15.7.2" - "@dhis2-ui/notice-box@8.14.7": version "8.14.7" resolved "https://registry.yarnpkg.com/@dhis2-ui/notice-box/-/notice-box-8.14.7.tgz#deed5e78870e38774a472891a78b3933cbc72bf4" @@ -2047,19 +1727,6 @@ classnames "^2.3.1" prop-types "^15.7.2" -"@dhis2-ui/organisation-unit-tree@8.13.15": - version "8.13.15" - resolved "https://registry.yarnpkg.com/@dhis2-ui/organisation-unit-tree/-/organisation-unit-tree-8.13.15.tgz#b9750f10d945baca95b8cfb43684d3e1858f7b8a" - integrity sha512-jGPiSBqo9vtvIzKpRVMUeDpmqxmjz6vwEcMqLwyWTh8YxC5nGw20wwj33z6SW/5BoSIgbgQZxxH3B6NBUbO8fA== - dependencies: - "@dhis2-ui/checkbox" "8.13.15" - "@dhis2-ui/loader" "8.13.15" - "@dhis2-ui/node" "8.13.15" - "@dhis2/prop-types" "^3.1.2" - "@dhis2/ui-constants" "8.13.15" - classnames "^2.3.1" - prop-types "^15.7.2" - "@dhis2-ui/organisation-unit-tree@8.14.7": version "8.14.7" resolved "https://registry.yarnpkg.com/@dhis2-ui/organisation-unit-tree/-/organisation-unit-tree-8.14.7.tgz#49c078222c59b1b9c10129afc0bc5075bb58bbc8" @@ -2073,19 +1740,6 @@ classnames "^2.3.1" prop-types "^15.7.2" -"@dhis2-ui/pagination@8.13.15": - version "8.13.15" - resolved "https://registry.yarnpkg.com/@dhis2-ui/pagination/-/pagination-8.13.15.tgz#ec73473380339ab646db0a5e336e5571e024f9ff" - integrity sha512-Kbixe3HFW168EiLkPu8hNe2CqnI+B6n0maVOeZJv90l0bW5FSXLHs669Fr6y2sTl0YVgwK94IoLOCJSWFldvIA== - dependencies: - "@dhis2-ui/button" "8.13.15" - "@dhis2-ui/select" "8.13.15" - "@dhis2/prop-types" "^3.1.2" - "@dhis2/ui-constants" "8.13.15" - "@dhis2/ui-icons" "8.13.15" - classnames "^2.3.1" - prop-types "^15.7.2" - "@dhis2-ui/pagination@8.14.7": version "8.14.7" resolved "https://registry.yarnpkg.com/@dhis2-ui/pagination/-/pagination-8.14.7.tgz#8f4a47cd6010e7860f95829155f3587e21d69dcc" @@ -2099,18 +1753,6 @@ classnames "^2.3.1" prop-types "^15.7.2" -"@dhis2-ui/popover@8.13.15": - version "8.13.15" - resolved "https://registry.yarnpkg.com/@dhis2-ui/popover/-/popover-8.13.15.tgz#41bb965b8fddd8b88349c9174ff7c63a392fab25" - integrity sha512-i32u23IAKkCi26Adcom8P8kMEP+EIw/rBcTYNHmPc1wrfVPnMfkVf+fFsVcoY7t1gcNFO88SiyJNffso7S1BPg== - dependencies: - "@dhis2-ui/layer" "8.13.15" - "@dhis2-ui/popper" "8.13.15" - "@dhis2/prop-types" "^3.1.2" - "@dhis2/ui-constants" "8.13.15" - classnames "^2.3.1" - prop-types "^15.7.2" - "@dhis2-ui/popover@8.14.7": version "8.14.7" resolved "https://registry.yarnpkg.com/@dhis2-ui/popover/-/popover-8.14.7.tgz#373e66dbf780ccfeedf5b5b2e2de269bb98dc940" @@ -2123,19 +1765,6 @@ classnames "^2.3.1" prop-types "^15.7.2" -"@dhis2-ui/popper@8.13.15": - version "8.13.15" - resolved "https://registry.yarnpkg.com/@dhis2-ui/popper/-/popper-8.13.15.tgz#c4c29c16c92221716a8806791bc383b6209acee6" - integrity sha512-gOIDZzvfH8oA0Hf7V0HEi3Y99KRcDdmog/BqnsiqEV/EAV/VRHq5qk239bIqKATM1Ei7Xv7G0lEBXJIZqOeNVA== - dependencies: - "@dhis2/prop-types" "^3.1.2" - "@dhis2/ui-constants" "8.13.15" - "@popperjs/core" "^2.10.1" - classnames "^2.3.1" - prop-types "^15.7.2" - react-popper "^2.2.5" - resize-observer-polyfill "^1.5.1" - "@dhis2-ui/popper@8.14.7": version "8.14.7" resolved "https://registry.yarnpkg.com/@dhis2-ui/popper/-/popper-8.14.7.tgz#0101fa266f867f8db3986d07c27b62e000f3e766" @@ -2149,14 +1778,6 @@ react-popper "^2.2.5" resize-observer-polyfill "^1.5.1" -"@dhis2-ui/portal@8.13.15": - version "8.13.15" - resolved "https://registry.yarnpkg.com/@dhis2-ui/portal/-/portal-8.13.15.tgz#701b0294d069239c7afefd1e1e211b9b80f72dc1" - integrity sha512-oqSbsOyznJwpdpAUiur5v24SOlThcWEbhnvniXSZzZy4thSJpcdF6OLMhgvVcjfO8kyAGgMbQjEx+hZ6cVpCFQ== - dependencies: - classnames "^2.3.1" - prop-types "^15.7.2" - "@dhis2-ui/portal@8.14.7": version "8.14.7" resolved "https://registry.yarnpkg.com/@dhis2-ui/portal/-/portal-8.14.7.tgz#ba050e3b0bd3c4702aa403a405e4b27e750d88c8" @@ -2165,16 +1786,6 @@ classnames "^2.3.1" prop-types "^15.7.2" -"@dhis2-ui/radio@8.13.15": - version "8.13.15" - resolved "https://registry.yarnpkg.com/@dhis2-ui/radio/-/radio-8.13.15.tgz#4475abe33148bef6cd532888ee6077864674ebeb" - integrity sha512-k+ahM17CLIwquuN6XD9LCzkjPR+Zkvpcl8Cb8GzJNpovkhqdDJMvFezmBu7oxyABzVzNYILnuouqGsQwF6G4UQ== - dependencies: - "@dhis2/prop-types" "^3.1.2" - "@dhis2/ui-constants" "8.13.15" - classnames "^2.3.1" - prop-types "^15.7.2" - "@dhis2-ui/radio@8.14.7": version "8.14.7" resolved "https://registry.yarnpkg.com/@dhis2-ui/radio/-/radio-8.14.7.tgz#213cc85b1b7a9244f0c216318ae53d24567a9901" @@ -2185,16 +1796,6 @@ classnames "^2.3.1" prop-types "^15.7.2" -"@dhis2-ui/required@8.13.15": - version "8.13.15" - resolved "https://registry.yarnpkg.com/@dhis2-ui/required/-/required-8.13.15.tgz#bba0fda4f693ab9d53f0822b3ad0375ee6c217e7" - integrity sha512-Qlt84rdcsP4i0faaOvOcedP2dFbZp08ASh3ylNwSbLcP2R+aNv9mXQCR6U2mW2WjCMsgAfGzApdMFgYZJfI9QQ== - dependencies: - "@dhis2/prop-types" "^3.1.2" - "@dhis2/ui-constants" "8.13.15" - classnames "^2.3.1" - prop-types "^15.7.2" - "@dhis2-ui/required@8.14.7": version "8.14.7" resolved "https://registry.yarnpkg.com/@dhis2-ui/required/-/required-8.14.7.tgz#16edd4b38ddee35680753a908e72484c090e4ee2" @@ -2205,16 +1806,6 @@ classnames "^2.3.1" prop-types "^15.7.2" -"@dhis2-ui/segmented-control@8.13.15": - version "8.13.15" - resolved "https://registry.yarnpkg.com/@dhis2-ui/segmented-control/-/segmented-control-8.13.15.tgz#2ecbd81075ea81b974912bab05d9d23f15c12382" - integrity sha512-+oulCoUVDHkxV5WPjGwzpgN9qmIwVW1/9TbW6JvC87gbiywAWaN59dhIIRoBRqeBOy0Ll9DW2L3siBYfMY43vg== - dependencies: - "@dhis2/prop-types" "^3.1.2" - "@dhis2/ui-constants" "8.13.15" - classnames "^2.3.1" - prop-types "^15.7.2" - "@dhis2-ui/segmented-control@8.14.7": version "8.14.7" resolved "https://registry.yarnpkg.com/@dhis2-ui/segmented-control/-/segmented-control-8.14.7.tgz#a80d0fb730a9f7c60b8939960216fd273da5c9c9" @@ -2225,28 +1816,6 @@ classnames "^2.3.1" prop-types "^15.7.2" -"@dhis2-ui/select@8.13.15": - version "8.13.15" - resolved "https://registry.yarnpkg.com/@dhis2-ui/select/-/select-8.13.15.tgz#3125ddb871da60bb1cbdf663ec4109c579fba0fb" - integrity sha512-yVNCws1GUmbXCOBKx2ZdwC5OEVt3wLPOSUOswVrJA3/ZjE0/ji5Or193/u9dhvLgDBzTt/ZIhKVksa4sT/pGCg== - dependencies: - "@dhis2-ui/box" "8.13.15" - "@dhis2-ui/button" "8.13.15" - "@dhis2-ui/card" "8.13.15" - "@dhis2-ui/checkbox" "8.13.15" - "@dhis2-ui/chip" "8.13.15" - "@dhis2-ui/field" "8.13.15" - "@dhis2-ui/input" "8.13.15" - "@dhis2-ui/layer" "8.13.15" - "@dhis2-ui/loader" "8.13.15" - "@dhis2-ui/popper" "8.13.15" - "@dhis2-ui/status-icon" "8.13.15" - "@dhis2/prop-types" "^3.1.2" - "@dhis2/ui-constants" "8.13.15" - "@dhis2/ui-icons" "8.13.15" - classnames "^2.3.1" - prop-types "^15.7.2" - "@dhis2-ui/select@8.14.7": version "8.14.7" resolved "https://registry.yarnpkg.com/@dhis2-ui/select/-/select-8.14.7.tgz#7a1b3e188715b8d1216e2ba7a9ba623db20b81a9" @@ -2269,21 +1838,6 @@ classnames "^2.3.1" prop-types "^15.7.2" -"@dhis2-ui/selector-bar@8.13.15": - version "8.13.15" - resolved "https://registry.yarnpkg.com/@dhis2-ui/selector-bar/-/selector-bar-8.13.15.tgz#96239ca8025b55999c6823c1a16ccfd95913ca46" - integrity sha512-QSNuy3nx3bh8LRvLnx+1ygovyB9kEcS965Gdfm4l3no2wVcD2GMBsQlhKGtoIgxEWSJKKQ15oGrybEsWWuMAAw== - dependencies: - "@dhis2-ui/button" "8.13.15" - "@dhis2-ui/card" "8.13.15" - "@dhis2-ui/layer" "8.13.15" - "@dhis2-ui/popper" "8.13.15" - "@dhis2/ui-constants" "8.13.15" - "@dhis2/ui-icons" "8.13.15" - "@testing-library/react" "^12.1.2" - classnames "^2.3.1" - prop-types "^15.7.2" - "@dhis2-ui/selector-bar@8.14.7": version "8.14.7" resolved "https://registry.yarnpkg.com/@dhis2-ui/selector-bar/-/selector-bar-8.14.7.tgz#5ff9d34962f594ea9c7db1fea6d52d46aaba0c8e" @@ -2299,32 +1853,6 @@ classnames "^2.3.1" prop-types "^15.7.2" -"@dhis2-ui/sharing-dialog@8.13.15": - version "8.13.15" - resolved "https://registry.yarnpkg.com/@dhis2-ui/sharing-dialog/-/sharing-dialog-8.13.15.tgz#0dd96295d200e22f937e9bfab21b444a7680f3bd" - integrity sha512-IRA5xD3vNwASnEoNV10+j/YXy5s9LqxolDWI/GlCHPGnuYxdlCb4NdbwWSFdyuzBtB3XrrGN4/h3BVMRtCKXBA== - dependencies: - "@dhis2-ui/box" "8.13.15" - "@dhis2-ui/button" "8.13.15" - "@dhis2-ui/card" "8.13.15" - "@dhis2-ui/divider" "8.13.15" - "@dhis2-ui/input" "8.13.15" - "@dhis2-ui/layer" "8.13.15" - "@dhis2-ui/menu" "8.13.15" - "@dhis2-ui/modal" "8.13.15" - "@dhis2-ui/notice-box" "8.13.15" - "@dhis2-ui/popper" "8.13.15" - "@dhis2-ui/select" "8.13.15" - "@dhis2-ui/tab" "8.13.15" - "@dhis2-ui/tooltip" "8.13.15" - "@dhis2-ui/user-avatar" "8.13.15" - "@dhis2/prop-types" "^3.1.2" - "@dhis2/ui-constants" "8.13.15" - "@dhis2/ui-icons" "8.13.15" - "@react-hook/size" "^2.1.2" - classnames "^2.3.1" - prop-types "^15.7.2" - "@dhis2-ui/sharing-dialog@8.14.7": version "8.14.7" resolved "https://registry.yarnpkg.com/@dhis2-ui/sharing-dialog/-/sharing-dialog-8.14.7.tgz#f0423418f5bb9221c64e078a226991defbed08b2" @@ -2351,18 +1879,6 @@ classnames "^2.3.1" prop-types "^15.7.2" -"@dhis2-ui/status-icon@8.13.15": - version "8.13.15" - resolved "https://registry.yarnpkg.com/@dhis2-ui/status-icon/-/status-icon-8.13.15.tgz#b97326cb30fab4f6c8913eabc06b9a1f19c7e670" - integrity sha512-+MBRhmaLA9b0bkXEr5eCwN8FFdGq5u3RWXanvAGXlZ0WT7xYUfnqFellJ7qkM/tQQEoBzwuK1nrMLt4YWCrEuw== - dependencies: - "@dhis2-ui/loader" "8.13.15" - "@dhis2/prop-types" "^3.1.2" - "@dhis2/ui-constants" "8.13.15" - "@dhis2/ui-icons" "8.13.15" - classnames "^2.3.1" - prop-types "^15.7.2" - "@dhis2-ui/status-icon@8.14.7": version "8.14.7" resolved "https://registry.yarnpkg.com/@dhis2-ui/status-icon/-/status-icon-8.14.7.tgz#5ab427ff1d21e7ffdaaa943b4d12aaddcc0ab63a" @@ -2375,18 +1891,6 @@ classnames "^2.3.1" prop-types "^15.7.2" -"@dhis2-ui/switch@8.13.15": - version "8.13.15" - resolved "https://registry.yarnpkg.com/@dhis2-ui/switch/-/switch-8.13.15.tgz#b064e8b1af0666756282da7ae59b40218e454ffb" - integrity sha512-E7o3SsNAN6OAt6zmizIfq1Uh4o0GPnwkK4l65JR32/4aMu6oC8omZFBtigmx26bo2P3AS0iIzar0gXwz4LIdyA== - dependencies: - "@dhis2-ui/field" "8.13.15" - "@dhis2-ui/required" "8.13.15" - "@dhis2/prop-types" "^3.1.2" - "@dhis2/ui-constants" "8.13.15" - classnames "^2.3.1" - prop-types "^15.7.2" - "@dhis2-ui/switch@8.14.7": version "8.14.7" resolved "https://registry.yarnpkg.com/@dhis2-ui/switch/-/switch-8.14.7.tgz#28b58f266f7c26f42d2348fcbfbc7f6e113ec9f4" @@ -2399,17 +1903,6 @@ classnames "^2.3.1" prop-types "^15.7.2" -"@dhis2-ui/tab@8.13.15": - version "8.13.15" - resolved "https://registry.yarnpkg.com/@dhis2-ui/tab/-/tab-8.13.15.tgz#a076b19761d327d481a25fc33d13be8996889b93" - integrity sha512-ak/OVhhHjnBAGPNuEl8Bekh/i7zroeP6BvkHMljpdDpbUN5Ex6+bgnwMpCEZ4qtwwTlZykbIsYelXDlRIGn7rQ== - dependencies: - "@dhis2/prop-types" "^3.1.2" - "@dhis2/ui-constants" "8.13.15" - "@dhis2/ui-icons" "8.13.15" - classnames "^2.3.1" - prop-types "^15.7.2" - "@dhis2-ui/tab@8.14.7": version "8.14.7" resolved "https://registry.yarnpkg.com/@dhis2-ui/tab/-/tab-8.14.7.tgz#6cc169fb97a2f227b0880e7f4c0cb5fe7b7eb20f" @@ -2421,17 +1914,6 @@ classnames "^2.3.1" prop-types "^15.7.2" -"@dhis2-ui/table@8.13.15": - version "8.13.15" - resolved "https://registry.yarnpkg.com/@dhis2-ui/table/-/table-8.13.15.tgz#b71680bef5209a4d30c47edb07edcb4a1e9def15" - integrity sha512-tJVQAA1pwji9AjyFr6oKWRzU01Vp/Lbk/5CQnrCqU9kh+uLIoyAfeyjF0kWGCB2uE37WrR5gC6/zOlSow3QqXw== - dependencies: - "@dhis2/prop-types" "^3.1.2" - "@dhis2/ui-constants" "8.13.15" - "@dhis2/ui-icons" "8.13.15" - classnames "^2.3.1" - prop-types "^15.7.2" - "@dhis2-ui/table@8.14.7": version "8.14.7" resolved "https://registry.yarnpkg.com/@dhis2-ui/table/-/table-8.14.7.tgz#769088964d52b2df952688f969f4143c782601df" @@ -2443,16 +1925,6 @@ classnames "^2.3.1" prop-types "^15.7.2" -"@dhis2-ui/tag@8.13.15": - version "8.13.15" - resolved "https://registry.yarnpkg.com/@dhis2-ui/tag/-/tag-8.13.15.tgz#cd6ccdb55bfba2ff102ae0cca0ae4fb1a9aea0b9" - integrity sha512-KzQYvRWEwOt2jkiv98tdyIH4Ibv2iUtoGo2Dik9Y0LAWRrPW5N8+x+/7Lr6G0ggBL10AhVrKOqA0PVxLYbFjMw== - dependencies: - "@dhis2/prop-types" "^3.1.2" - "@dhis2/ui-constants" "8.13.15" - classnames "^2.3.1" - prop-types "^15.7.2" - "@dhis2-ui/tag@8.14.7": version "8.14.7" resolved "https://registry.yarnpkg.com/@dhis2-ui/tag/-/tag-8.14.7.tgz#203d7c6d916791e6691ef84b8bdff7f78a22c61d" @@ -2463,21 +1935,6 @@ classnames "^2.3.1" prop-types "^15.7.2" -"@dhis2-ui/text-area@8.13.15": - version "8.13.15" - resolved "https://registry.yarnpkg.com/@dhis2-ui/text-area/-/text-area-8.13.15.tgz#48d21eb7093fae2a2a6667f28c996ddd83251d54" - integrity sha512-djZt2OQH1CBaTPOg4iAgllNLYfbcvMuSQ9AR/hr1mf3/85e9ZLc12enXN0AaVkSQopemHvH/KcgNhA8ogEGWhQ== - dependencies: - "@dhis2-ui/box" "8.13.15" - "@dhis2-ui/field" "8.13.15" - "@dhis2-ui/loader" "8.13.15" - "@dhis2-ui/status-icon" "8.13.15" - "@dhis2/prop-types" "^3.1.2" - "@dhis2/ui-constants" "8.13.15" - "@dhis2/ui-icons" "8.13.15" - classnames "^2.3.1" - prop-types "^15.7.2" - "@dhis2-ui/text-area@8.14.7": version "8.14.7" resolved "https://registry.yarnpkg.com/@dhis2-ui/text-area/-/text-area-8.14.7.tgz#3814d749713b4b83a1c7b9ba3738244f4b35ae89" @@ -2493,18 +1950,6 @@ classnames "^2.3.1" prop-types "^15.7.2" -"@dhis2-ui/tooltip@8.13.15": - version "8.13.15" - resolved "https://registry.yarnpkg.com/@dhis2-ui/tooltip/-/tooltip-8.13.15.tgz#22acbbf785c51fd9a924621ae9c50f7a58fa88a5" - integrity sha512-6igahGDmonAJJIqmQsm93UCb/mn2Qvivk8NWk6SFDTRVFj6g2I/5ugK5oEn0BQcbIC98bddIj+rBKO2Gzj7oPA== - dependencies: - "@dhis2-ui/popper" "8.13.15" - "@dhis2-ui/portal" "8.13.15" - "@dhis2/prop-types" "^3.1.2" - "@dhis2/ui-constants" "8.13.15" - classnames "^2.3.1" - prop-types "^15.7.2" - "@dhis2-ui/tooltip@8.14.7": version "8.14.7" resolved "https://registry.yarnpkg.com/@dhis2-ui/tooltip/-/tooltip-8.14.7.tgz#636037a67d0882eb12d089553a39f865fe914be7" @@ -2517,21 +1962,6 @@ classnames "^2.3.1" prop-types "^15.7.2" -"@dhis2-ui/transfer@8.13.15": - version "8.13.15" - resolved "https://registry.yarnpkg.com/@dhis2-ui/transfer/-/transfer-8.13.15.tgz#de7199c68664821c3653a0e123e9bc9cd22ec745" - integrity sha512-blcrk8S3YXwVwlPS7UUkdVq6eg48j4Uqi/nir3t2HbfcnJDl3EjQW22xv/nSHcvDi5rzZ6cflVHWLzp4lJdObg== - dependencies: - "@dhis2-ui/button" "8.13.15" - "@dhis2-ui/field" "8.13.15" - "@dhis2-ui/input" "8.13.15" - "@dhis2-ui/intersection-detector" "8.13.15" - "@dhis2-ui/loader" "8.13.15" - "@dhis2/prop-types" "^3.1.2" - "@dhis2/ui-constants" "8.13.15" - classnames "^2.3.1" - prop-types "^15.7.2" - "@dhis2-ui/transfer@8.14.7": version "8.14.7" resolved "https://registry.yarnpkg.com/@dhis2-ui/transfer/-/transfer-8.14.7.tgz#a098d178d4e0b880e4b9edddcada55b67168c3f5" @@ -2547,16 +1977,6 @@ classnames "^2.3.1" prop-types "^15.7.2" -"@dhis2-ui/user-avatar@8.13.15": - version "8.13.15" - resolved "https://registry.yarnpkg.com/@dhis2-ui/user-avatar/-/user-avatar-8.13.15.tgz#619150face6561ecd679385a9fdd099d53a6b86e" - integrity sha512-wo6hYzTYY3rTtwiR1iSGunk02h5rvMfWok5HfCrvV4Dy+aSiegygS1ZQ806Dttmq4s03sVvMuZUX15v7GIfE4w== - dependencies: - "@dhis2/prop-types" "^3.1.2" - "@dhis2/ui-constants" "8.13.15" - classnames "^2.3.1" - prop-types "^15.7.2" - "@dhis2-ui/user-avatar@8.14.7": version "8.14.7" resolved "https://registry.yarnpkg.com/@dhis2-ui/user-avatar/-/user-avatar-8.14.7.tgz#3cd54d5261ee7f10cee99a347f9f1bce7803dccf" @@ -2767,13 +2187,6 @@ workbox-routing "^6.1.5" workbox-strategies "^6.1.5" -"@dhis2/ui-constants@8.13.15": - version "8.13.15" - resolved "https://registry.yarnpkg.com/@dhis2/ui-constants/-/ui-constants-8.13.15.tgz#e1124e79f077aed6b3a9703b8a257701ec68717b" - integrity sha512-XrwKXNM0Xiv7O3E9vAukOJv8sD1QD9ruLOUS66+fs4pBLd0L+W6Gjr7LYG1B19rANXAShmfR+dqgD/pLVLAqsw== - dependencies: - prop-types "^15.7.2" - "@dhis2/ui-constants@8.14.7": version "8.14.7" resolved "https://registry.yarnpkg.com/@dhis2/ui-constants/-/ui-constants-8.14.7.tgz#bfab11cc3c8dc990f7592089ba9577f7a7c60411" @@ -2781,26 +2194,6 @@ dependencies: prop-types "^15.7.2" -"@dhis2/ui-forms@8.13.15": - version "8.13.15" - resolved "https://registry.yarnpkg.com/@dhis2/ui-forms/-/ui-forms-8.13.15.tgz#5098893acada5fb06d263ce271f556fc1bae1acf" - integrity sha512-+skh3quuARmzVlVJUYnN8EM5Dz9OLOsnh/GpCMXyiO4yjhajpbQ0QtMh/dFyQhCB3LzywDVaoge+kwJyrIS82Q== - dependencies: - "@dhis2-ui/button" "8.13.15" - "@dhis2-ui/checkbox" "8.13.15" - "@dhis2-ui/field" "8.13.15" - "@dhis2-ui/file-input" "8.13.15" - "@dhis2-ui/input" "8.13.15" - "@dhis2-ui/radio" "8.13.15" - "@dhis2-ui/select" "8.13.15" - "@dhis2-ui/switch" "8.13.15" - "@dhis2-ui/text-area" "8.13.15" - "@dhis2/prop-types" "^3.1.2" - classnames "^2.3.1" - final-form "^4.20.2" - prop-types "^15.7.2" - react-final-form "^6.5.3" - "@dhis2/ui-forms@8.14.7": version "8.14.7" resolved "https://registry.yarnpkg.com/@dhis2/ui-forms/-/ui-forms-8.14.7.tgz#fa9c8640cda7cb5417e1e62013d5a745c4098e25" @@ -2821,11 +2214,6 @@ prop-types "^15.7.2" react-final-form "^6.5.3" -"@dhis2/ui-icons@8.13.15": - version "8.13.15" - resolved "https://registry.yarnpkg.com/@dhis2/ui-icons/-/ui-icons-8.13.15.tgz#eb83784975d9686058ce71377928a0d4f726b517" - integrity sha512-d4A7oPgT6HKZgQDGaY8mX04EvQrnDmOv/L9n52JtnmEcPrPTqqhmCqdr/CQ8B/NP91nhFACDFBQ4ez4/YJVYKw== - "@dhis2/ui-icons@8.14.7": version "8.14.7" resolved "https://registry.yarnpkg.com/@dhis2/ui-icons/-/ui-icons-8.14.7.tgz#351d5890544a656c0838bba6979e9068ed051b97" @@ -14794,9 +14182,9 @@ zip-stream@^2.1.2: readable-stream "^3.4.0" zod@^3.22.2: - version "3.22.2" - resolved "https://registry.yarnpkg.com/zod/-/zod-3.22.2.tgz#3add8c682b7077c05ac6f979fea6998b573e157b" - integrity sha512-wvWkphh5WQsJbVk1tbx1l1Ly4yg+XecD+Mq280uBGt9wa5BKSWf4Mhp6GmrkPixhMxmabYY7RbzlwVP32pbGCg== + version "3.22.4" + resolved "https://registry.yarnpkg.com/zod/-/zod-3.22.4.tgz#f31c3a9386f61b1f228af56faa9255e845cf3fff" + integrity sha512-iC+8Io04lddc+mVqQ9AZ7OQ2MrUKGN+oIQyq1vemgt46jwCwLfhq7/pwnBnNXXXZb8VTVLKwp9EDkx+ryxIWmg== zustand@^4.4.0: version "4.4.0" From aceade928eba6d4ac7c0447ea88a55793d97f9ec Mon Sep 17 00:00:00 2001 From: Birk Johansson Date: Wed, 22 Nov 2023 16:30:07 +0100 Subject: [PATCH 12/14] feat(detailsPanel): add detailsPanel in list-view (#358) * feat(datastore): add hook for accessing datastore * fix(types): fix types for section handle * feat(constants): add constants for columns and translated properties * fix: update types for sectionlistcolumns * fix: add selector to usedatastore * feat(managecolumns): manage columns implementation * fix: integrate selectedcoluns with list * fix: mergecolumns * fix: managecolumns style * refactor: managecolumns * chore: add remeda util lib * refactor: refactor to config for listviews * refactor: manage list view * fix: respect order of selected columns * fix(datastore): always use put for datastore * refactor: allow arbitrary path as column * refactor: remove remeda, implement uniqueBy * fix: add uniqueBy * fix: add test for path * fix: add maxDepth to getFieldFilterFromPath * fix: fix some tests * fix: remove typepath file * refactor: cleanup * fix(constants): prevent circular dependency (#354) * fix(managelistview): enforce at least one column * refactor: rename DataStoreModelListView schema * refactor: rename useDataStoreValuesQuery * refactor(modelvale): simplify modelvalue * fix: fix imports * refactor: remove component from list config * fix: fix tests * refactor: minor cleanup * feat(detailpanel): initial detailsPanel implementation * refactor(detailspanel): cleanup details panel implementation, add error-boundary * fix: responsive layout with detailspanel * refactor: fix wrong name of sectionlistheader * refactor: minor cleanup * fix: fix id missing in details response * fix: add ui to resolution for alpha version * fix: fix type errors * refactor(details): make detailspanelcontent more composable * fix(details): use correct property for lastUpdated * fix(details): add lastupdatedby * fix(details): add createdBy, fallback to created if no lastUpdatedBy * fix(list): list header as grid-member * fix(test): fix failing test * chore(deps): update ui alpha version * fix(types): fix wrong types --------- Co-authored-by: Jan-Gerke Salomon --- global.d.ts | 3 - i18n/en.pot | 79 +- package.json | 5 +- src/app/sidebar/Sidebar.tsx | 6 +- src/app/sidebar/sidenav/Sidenav.module.css | 20 +- src/app/sidebar/sidenav/SidenavFilter.tsx | 17 +- src/app/sidebar/sidenav/index.tsx | 1 - .../ColorAndIconPicker/ColorPicker.tsx | 2 +- .../ColorAndIconPicker/IconPickerModal.tsx | 4 +- .../SearchableSingleSelect.tsx | 6 +- src/components/date/ClientDateTime.tsx | 38 + src/components/date/index.ts | 1 + .../AggregationLevelMultiSelect.tsx | 3 +- .../LegendSetTransfer/LegendSetTransfer.tsx | 4 +- .../sectionList/SectionList.module.css | 39 +- src/components/sectionList/SectionList.tsx | 3 +- ...Normal.tsx => SectionListHeaderNormal.tsx} | 8 +- src/components/sectionList/SectionListRow.tsx | 13 +- .../sectionList/SectionListWrapper.tsx | 77 +- .../detailsPanel/DefaultDetailsPanel.tsx | 112 +++ .../sectionList/detailsPanel/DetailItem.tsx | 39 + .../detailsPanel/DetailsPanel.module.css | 101 +++ .../sectionList/detailsPanel/DetailsPanel.tsx | 74 ++ .../sectionList/detailsPanel/index.ts | 3 + .../filters/IdentifiableFilter.tsx | 6 +- .../constants/translatedModelProperties.ts | 3 +- src/lib/dataStore/DataStore.ts | 1 + src/lib/date/index.ts | 1 + .../form/EditableFieldWrapper.tsx | 4 +- src/pages/dataElements/form/fields.tsx | 33 +- .../form/useIsFieldValueUnique.ts | 2 +- src/types/query.ts | 4 + yarn.lock | 850 +++++++++--------- 33 files changed, 1001 insertions(+), 561 deletions(-) create mode 100644 src/components/date/ClientDateTime.tsx create mode 100644 src/components/date/index.ts rename src/components/sectionList/{SelectionListHeaderNormal.tsx => SectionListHeaderNormal.tsx} (83%) create mode 100644 src/components/sectionList/detailsPanel/DefaultDetailsPanel.tsx create mode 100644 src/components/sectionList/detailsPanel/DetailItem.tsx create mode 100644 src/components/sectionList/detailsPanel/DetailsPanel.module.css create mode 100644 src/components/sectionList/detailsPanel/DetailsPanel.tsx create mode 100644 src/components/sectionList/detailsPanel/index.ts create mode 100644 src/lib/dataStore/DataStore.ts diff --git a/global.d.ts b/global.d.ts index 4c448a63..b165d406 100644 --- a/global.d.ts +++ b/global.d.ts @@ -6,9 +6,6 @@ declare module '@dhis2/d2-i18n' { export function exists(key: string): boolean } -declare module '@dhis2/ui' -declare module '@dhis2/ui-icons' - declare module '*.bmp' { const src: string export default src diff --git a/i18n/en.pot b/i18n/en.pot index 0d0bd2fc..b5bff325 100644 --- a/i18n/en.pot +++ b/i18n/en.pot @@ -5,8 +5,8 @@ msgstr "" "Content-Type: text/plain; charset=utf-8\n" "Content-Transfer-Encoding: 8bit\n" "Plural-Forms: nplurals=2; plural=(n != 1)\n" -"POT-Creation-Date: 2023-11-16T09:17:45.955Z\n" -"PO-Revision-Date: 2023-11-16T09:17:45.955Z\n" +"POT-Creation-Date: 2023-11-22T13:05:25.319Z\n" +"PO-Revision-Date: 2023-11-22T13:05:25.319Z\n" msgid "schemas" msgstr "schemas" @@ -93,6 +93,15 @@ msgstr "Option set" msgid "Actions" msgstr "Actions" +msgid "New" +msgstr "New" + +msgid "Download" +msgstr "Download" + +msgid "Manage Columns" +msgstr "Manage Columns" + msgid "There aren't any items that match your filter." msgstr "There aren't any items that match your filter." @@ -105,14 +114,44 @@ msgstr "An error occurred while loading the items." msgid "{{modelName}} management" msgstr "{{modelName}} management" -msgid "New" -msgstr "New" +msgid "Short name" +msgstr "Short name" -msgid "Download" -msgstr "Download" +msgid "Code" +msgstr "Code" -msgid "Manage Columns" -msgstr "Manage Columns" +msgid "Created by" +msgstr "Created by" + +msgid "Created" +msgstr "Created" + +msgid "Last updated by" +msgstr "Last updated by" + +msgid "Last updated" +msgstr "Last updated" + +msgid "Id" +msgstr "Id" + +msgid "API URL" +msgstr "API URL" + +msgid "API URL link" +msgstr "API URL link" + +msgid "Copy" +msgstr "Copy" + +msgid "Edit" +msgstr "Edit" + +msgid "Details" +msgstr "Details" + +msgid "Failed to load details" +msgstr "Failed to load details" msgid "Type to filter options" msgstr "Type to filter options" @@ -561,42 +600,18 @@ msgstr "Image" msgid "GeoJSON" msgstr "GeoJSON" -msgid "Code" -msgstr "Code" - -msgid "Created by" -msgstr "Created by" - msgid "Favorite" msgstr "Favorite" -msgid "Href" -msgstr "Href" - -msgid "Id" -msgstr "Id" - -msgid "Last updated by" -msgstr "Last updated by" - -msgid "Created" -msgstr "Created" - msgid "Domain type" msgstr "Domain type" -msgid "Last updated" -msgstr "Last updated" - msgid "Name" msgstr "Name" msgid "Sharing" msgstr "Sharing" -msgid "Short name" -msgstr "Short name" - msgid "Value type" msgstr "Value type" diff --git a/package.json b/package.json index aa8b421e..01a2bc0b 100644 --- a/package.json +++ b/package.json @@ -37,7 +37,7 @@ }, "dependencies": { "@dhis2/app-runtime": "^3.9.3", - "@dhis2/ui": "^8.14.7", + "@dhis2/ui": "^8.15.0-alpha.3", "@types/lodash": "^4.14.198", "lodash": "^4.17.21", "react-color": "^2.19.3", @@ -48,5 +48,8 @@ "use-query-params": "^2.2.1", "zod": "^3.22.2", "zustand": "^4.4.0" + }, + "resolutions": { + "@dhis2/ui": "8.15.0-alpha.3" } } diff --git a/src/app/sidebar/Sidebar.tsx b/src/app/sidebar/Sidebar.tsx index 6a18c617..9f14270d 100644 --- a/src/app/sidebar/Sidebar.tsx +++ b/src/app/sidebar/Sidebar.tsx @@ -1,4 +1,5 @@ import i18n from '@dhis2/d2-i18n' +import { InputEventPayload } from '@dhis2/ui' import React, { useEffect, useState, PropsWithChildren } from 'react' import { NavLink, useLocation, matchPath } from 'react-router-dom' import { HidePreventUnmount } from '../../components' @@ -10,7 +11,6 @@ import { SidenavParent, SidenavLink, SidenavFilter, - OnChangeInput, } from './sidenav' interface SidebarNavLinkProps { @@ -86,8 +86,8 @@ export const Sidebar = ({ className }: { className?: string }) => { const sidebarLinks = useSidebarLinks() const [filterValue, setFilterValue] = useState('') - const handleFilterChange = (input: OnChangeInput) => { - setFilterValue(input.value) + const handleFilterChange = (input: InputEventPayload) => { + setFilterValue(input.value ?? '') } const isFiltered = filterValue !== '' diff --git a/src/app/sidebar/sidenav/Sidenav.module.css b/src/app/sidebar/sidenav/Sidenav.module.css index 3a940498..42ef07cc 100644 --- a/src/app/sidebar/sidenav/Sidenav.module.css +++ b/src/app/sidebar/sidenav/Sidenav.module.css @@ -60,10 +60,9 @@ html { margin-left: auto; width: 16px; height: 16px; - transition: transform .1s linear; + transition: transform 0.1s linear; } .parent-is-open .sidenav-parent-chevron { - transform: rotate(180deg); } @@ -82,22 +81,27 @@ html { display: flex; align-items: center; } -.sidenav-link:hover,.sidenav-link a:hover { +.sidenav-link:hover, +.sidenav-link a:hover { background: var(--sidenav-dark-bg-hover); } -.sidenav-link:focus,.sidenav-link a:focus { +.sidenav-link:focus, +.sidenav-link a:focus { outline: 2px solid white; background: var(--sidenav-dark-bg-hover); outline-offset: -2px; } -.sidenav-link-disabled, .sidenav-link-disabled a { +.sidenav-link-disabled, +.sidenav-link-disabled a { cursor: not-allowed; color: var(--colors-grey500); } -.sidenav-link-disabled:hover,.sidenav-link-disabled:hover > a { +.sidenav-link-disabled:hover, +.sidenav-link-disabled:hover > a { background: var(--colors-grey900); } -.sidenav-link a.active, .sidenav-link :global(.active) { +.sidenav-link a.active, +.sidenav-link :global(.active) { font-weight: 600; background: var(--sidenav-dark-bg-selected); box-shadow: inset 6px 0px 0px 0px var(--colors-teal500); @@ -155,4 +159,4 @@ html { background: #131a22; border-color: var(--colors-grey700); box-shadow: var(--colors-grey400) 0px 0px 0px 2px inset; -} \ No newline at end of file +} diff --git a/src/app/sidebar/sidenav/SidenavFilter.tsx b/src/app/sidebar/sidenav/SidenavFilter.tsx index e358a2a4..87db8975 100644 --- a/src/app/sidebar/sidenav/SidenavFilter.tsx +++ b/src/app/sidebar/sidenav/SidenavFilter.tsx @@ -1,29 +1,20 @@ import i18n from '@dhis2/d2-i18n' -import { Input } from '@dhis2/ui' +import { Input, InputEventPayload, InputChangeHandler } from '@dhis2/ui' import React, { useState } from 'react' import styles from './Sidenav.module.css' -export type OnChangeInput = { - name?: string - value: string -} -export type OnChangeCallback = ( - input: OnChangeInput, - e: React.ChangeEvent -) => void - interface SidenavParentProps { - onChange?: OnChangeCallback + onChange?: InputChangeHandler } export const SidenavFilter = ({ onChange }: SidenavParentProps) => { const [value, setValue] = useState('') const handleChange = ( - input: OnChangeInput, + input: InputEventPayload, e: React.ChangeEvent ) => { - setValue(input.value) + setValue(input.value ?? '') if (onChange) { onChange(input, e) } diff --git a/src/app/sidebar/sidenav/index.tsx b/src/app/sidebar/sidenav/index.tsx index 37752603..f3c7955f 100644 --- a/src/app/sidebar/sidenav/index.tsx +++ b/src/app/sidebar/sidenav/index.tsx @@ -1,3 +1,2 @@ export * from './Sidenav' export { SidenavFilter } from './SidenavFilter' -export type { OnChangeInput, OnChangeCallback } from './SidenavFilter' diff --git a/src/components/ColorAndIconPicker/ColorPicker.tsx b/src/components/ColorAndIconPicker/ColorPicker.tsx index 34ee79e9..1fe97075 100644 --- a/src/components/ColorAndIconPicker/ColorPicker.tsx +++ b/src/components/ColorAndIconPicker/ColorPicker.tsx @@ -35,7 +35,7 @@ export function ColorPicker({
{showPicker && ( - setShowPicker(false)} transparent> + setShowPicker(false)} translucent>
- setSearchValue(value) + onChange={({ value }) => + setSearchValue(value || '') } />
diff --git a/src/components/SearchableSingleSelect/SearchableSingleSelect.tsx b/src/components/SearchableSingleSelect/SearchableSingleSelect.tsx index 5cbccdde..3a6c9013 100644 --- a/src/components/SearchableSingleSelect/SearchableSingleSelect.tsx +++ b/src/components/SearchableSingleSelect/SearchableSingleSelect.tsx @@ -134,9 +134,7 @@ export const SearchableSingleSelect = ({ - setFilterValue(value) - } + onChange={({ value }) => setFilterValue(value ?? '')} placeholder={i18n.t('Filter options')} />
@@ -154,7 +152,7 @@ export const SearchableSingleSelect = ({ ))} - {hasSelectedInOptionList && ( + {hasSelectedInOptionList && selected && ( { + // NOTE: options cannot change after initial render + // this is to prevent having to memo the options object in the consuming component + const [dateTimeFormatter] = useState(() => + options + ? new Intl.DateTimeFormat(selectedLocale, options) + : defaultDateTimeFormatter + ) + const { fromServerDate } = useTimeZoneConversion() + + if (!value) { + return null + } + + const clientDate = fromServerDate(value) + + return ( + + {dateTimeFormatter.format(clientDate)} + + ) +} diff --git a/src/components/date/index.ts b/src/components/date/index.ts new file mode 100644 index 00000000..57db9a65 --- /dev/null +++ b/src/components/date/index.ts @@ -0,0 +1 @@ +export * from './ClientDateTime' diff --git a/src/components/metadataFormControls/AggregationLevelMultiSelect/AggregationLevelMultiSelect.tsx b/src/components/metadataFormControls/AggregationLevelMultiSelect/AggregationLevelMultiSelect.tsx index 23e3c480..56fb4f4c 100644 --- a/src/components/metadataFormControls/AggregationLevelMultiSelect/AggregationLevelMultiSelect.tsx +++ b/src/components/metadataFormControls/AggregationLevelMultiSelect/AggregationLevelMultiSelect.tsx @@ -1,5 +1,5 @@ import i18n from '@dhis2/d2-i18n' -import { MultiSelect, MultiSelectOption } from '@dhis2/ui' +import { MultiSelect, MultiSelectField, MultiSelectOption } from '@dhis2/ui' import React, { forwardRef, useImperativeHandle } from 'react' import classes from './AggregationLevelMultiSelect.module.css' import { useOptionsQuery } from './useOptionsQuery' @@ -64,7 +64,6 @@ export const AggregationLevelMultiSelect = forwardRef( return ( { onChange({ selected }) diff --git a/src/components/metadataFormControls/LegendSetTransfer/LegendSetTransfer.tsx b/src/components/metadataFormControls/LegendSetTransfer/LegendSetTransfer.tsx index 10d799d4..3ef7484b 100644 --- a/src/components/metadataFormControls/LegendSetTransfer/LegendSetTransfer.tsx +++ b/src/components/metadataFormControls/LegendSetTransfer/LegendSetTransfer.tsx @@ -100,7 +100,8 @@ export const LegendSetTransfer = forwardRef(function LegendSetSelect( ) const adjustQueryParamsWithChangedFilter = useCallback( - ({ value }: { value: string }) => { + ({ value }: { value: string | undefined }) => { + value = value ?? '' setSearchTerm(value) pageRef.current = 1 refetch({ page: pageRef.current, filter: value }) @@ -131,7 +132,6 @@ export const LegendSetTransfer = forwardRef(function LegendSetSelect( options={displayOptions} selected={selected} loading={loading} - error={error} onChange={({ selected }: { selected: string[] }) => { const nextSelectedOptions = displayOptions.filter(({ value }) => selected.includes(value) diff --git a/src/components/sectionList/SectionList.module.css b/src/components/sectionList/SectionList.module.css index 1bead8ec..27a3d3e4 100644 --- a/src/components/sectionList/SectionList.module.css +++ b/src/components/sectionList/SectionList.module.css @@ -6,11 +6,44 @@ align-items: center; height: 48px; padding: var(--spacers-dp8); - margin-top: var(--spacers-dp8); gap: var(--spacers-dp8); } +.list { + grid-area: list; +} + +.listToolbarWrapper { + display: flex; + flex-direction: column; + grid-area: list; +} + +.listDetailsWrapper { + display: grid; + grid-template-columns: 1fr auto; + grid-template-rows: auto; + grid-template-areas: + 'header details' + 'list details'; + margin-top: var(--spacers-dp8); +} + +@media (max-width: 1200px) { + .listDetailsWrapper { + display: grid; + grid-template-rows: auto; + grid-template-columns: none; + grid-template-areas: + 'header' + 'list' + 'details'; + margin-top: var(--spacers-dp8); + } +} + .listHeaderNormal a { + grid-area: header; line-height: var(--spacers-dp16); } @@ -19,6 +52,10 @@ padding-bottom: var(--spacers-dp8); } +.listRow.active td { + background-color: var(--colors-grey200); +} + .listActions { display: flex; gap: var(--spacers-dp8); diff --git a/src/components/sectionList/SectionList.tsx b/src/components/sectionList/SectionList.tsx index e772d4dd..1b3bd378 100644 --- a/src/components/sectionList/SectionList.tsx +++ b/src/components/sectionList/SectionList.tsx @@ -9,6 +9,7 @@ import { } from '@dhis2/ui' import React, { PropsWithChildren } from 'react' import { CheckBoxOnChangeObject } from '../../types' +import css from './SectionList.module.css' import { SelectedColumns } from './types' type SectionListProps = { @@ -24,7 +25,7 @@ export const SectionList = ({ onSelectAll, }: PropsWithChildren) => { return ( - + diff --git a/src/components/sectionList/SelectionListHeaderNormal.tsx b/src/components/sectionList/SectionListHeaderNormal.tsx similarity index 83% rename from src/components/sectionList/SelectionListHeaderNormal.tsx rename to src/components/sectionList/SectionListHeaderNormal.tsx index 5603d49d..3e76e34b 100644 --- a/src/components/sectionList/SelectionListHeaderNormal.tsx +++ b/src/components/sectionList/SectionListHeaderNormal.tsx @@ -1,5 +1,5 @@ import i18n from '@dhis2/d2-i18n' -import { Button } from '@dhis2/ui' +import { Button, DataTableToolbar } from '@dhis2/ui' import { IconAdd24 } from '@dhis2/ui-icons' import React from 'react' import { Link } from 'react-router-dom' @@ -7,12 +7,12 @@ import { routePaths } from '../../lib' import { ManageListViewDialog } from './listView/ManageListViewDialog' import css from './SectionList.module.css' -export const SelectionListHeader = () => { +export const SectionListHeader = () => { const [manageColumnsOpen, setManageColumnsOpen] = React.useState(false) const handleClose = () => setManageColumnsOpen(false) return ( -
+
+ ) } diff --git a/src/components/sectionList/SectionListRow.tsx b/src/components/sectionList/SectionListRow.tsx index ac1ecae1..ba8cc217 100644 --- a/src/components/sectionList/SectionListRow.tsx +++ b/src/components/sectionList/SectionListRow.tsx @@ -1,5 +1,6 @@ import { DataTableRow, DataTableCell, Checkbox, Button } from '@dhis2/ui' import { IconEdit24, IconMore24 } from '@dhis2/ui-icons' +import cx from 'classnames' import React from 'react' import { Link } from 'react-router-dom' import { CheckBoxOnChangeObject } from '../../types' @@ -13,19 +14,24 @@ export type SectionListRowProps = { onSelect: (modelId: string, checked: boolean) => void selected: boolean renderColumnValue: (column: SelectedColumn) => React.ReactNode + onClick?: (modelData: GistModel) => void + active?: boolean } export function SectionListRow({ + active, selectedColumns, modelData, onSelect, + onClick, selected, renderColumnValue, }: SectionListRowProps) { return ( ({ /> {selectedColumns.map((selectedColumn) => ( - + onClick?.(modelData)} + > {renderColumnValue(selectedColumn)} ))} diff --git a/src/components/sectionList/SectionListWrapper.tsx b/src/components/sectionList/SectionListWrapper.tsx index c4e91a58..7f19ecd6 100644 --- a/src/components/sectionList/SectionListWrapper.tsx +++ b/src/components/sectionList/SectionListWrapper.tsx @@ -2,16 +2,18 @@ import { FetchError } from '@dhis2/app-runtime' import React, { useMemo, useState } from 'react' import { useSchemaFromHandle } from '../../lib' import { IdentifiableObject, GistCollectionResponse } from '../../types/models' +import { DetailsPanel, DefaultDetailsPanelContent } from './detailsPanel' import { FilterWrapper } from './filters/FilterWrapper' import { useModelListView } from './listView' import { ModelValue } from './modelValue/ModelValue' import { SectionList } from './SectionList' +import css from './SectionList.module.css' +import { SectionListHeader } from './SectionListHeaderNormal' import { SectionListLoader } from './SectionListLoader' import { SectionListEmpty, SectionListError } from './SectionListMessages' import { SectionListPagination } from './SectionListPagination' import { SectionListRow } from './SectionListRow' import { SectionListTitle } from './SectionListTitle' -import { SelectionListHeader } from './SelectionListHeaderNormal' type SectionListWrapperProps = { filterElement?: React.ReactElement @@ -27,6 +29,7 @@ export const SectionListWrapper = ({ const { columns: headerColumns } = useModelListView() const schema = useSchemaFromHandle() const [selectedModels, setSelectedModels] = useState>(new Set()) + const [detailsId, setDetailsId] = useState() const handleSelect = (id: string, checked: boolean) => { if (checked) { @@ -76,33 +79,51 @@ export const SectionListWrapper = ({
{filterElement} - - - - {data?.result.map((model) => ( - { - return ( - - ) - }} - /> - ))} - - +
+ + + + {data?.result.map((model) => ( + { + setDetailsId((prevDetailsId) => + prevDetailsId === id ? undefined : id + ) + }} + selected={selectedModels.has(model.id)} + active={model.id === detailsId} + renderColumnValue={({ path }) => { + return ( + + ) + }} + /> + ))} + + + + {detailsId && ( + setDetailsId(undefined)} + // reset component state when modelId changes + key={detailsId} + > + + + )} +
) } diff --git a/src/components/sectionList/detailsPanel/DefaultDetailsPanel.tsx b/src/components/sectionList/detailsPanel/DefaultDetailsPanel.tsx new file mode 100644 index 00000000..c4939f95 --- /dev/null +++ b/src/components/sectionList/detailsPanel/DefaultDetailsPanel.tsx @@ -0,0 +1,112 @@ +import { useDataQuery } from '@dhis2/app-runtime' +import i18n from '@dhis2/d2-i18n' +import React, { useRef } from 'react' +import { Schema, useSchemaFromHandle } from '../../../lib' +import { Query, WrapQueryResponse } from '../../../types' +import { BaseIdentifiableObject } from '../../../types/models' +import { ClientDateTime } from '../../date' +import { Loader } from '../../loading' +import { ApiUrlValue, DetailItem } from './DetailItem' +import { + DetailsList, + DetailsPanelButtons, + DetailsPanelContent, +} from './DetailsPanel' + +type DetailsPanelProps = { + modelId: string +} + +const defaultQueryFields = [ + 'code', + 'created', + 'lastUpdated', + 'lastUpdatedBy', + 'displayName', + 'id', + 'shortName', + 'href', + 'createdBy', +] as const + +const createDefaultDetailsQuery = ( + resource: string, + id: string, + schemaProperties: Schema['properties'] +): Query => ({ + result: { + resource, + id, + params: { + fields: defaultQueryFields + .filter((field) => !!schemaProperties[field]) + .concat('id'), + }, + }, +}) + +// fields that are not in BaseIdentifiableObject +type ExtraFields = { + shortName?: string +} + +type DetailsResponse = Pick< + BaseIdentifiableObject & ExtraFields, + (typeof defaultQueryFields)[number] +> + +export const DefaultDetailsPanelContent = ({ modelId }: DetailsPanelProps) => { + const schema = useSchemaFromHandle() + // used to prevent changing query after initial render + const queryRef = useRef( + createDefaultDetailsQuery(schema.plural, modelId, schema.properties) + ) + + const detailsQueryResponse = useDataQuery< + WrapQueryResponse + >(queryRef.current) + return ( + + + } + /> + + ) +} + +const DetailsContent = ({ data }: { data: DetailsResponse }) => { + return ( + + + + {data.shortName && ( + + {data.shortName} + + )} + {data.code} + + {data.createdBy.displayName} + + + + + + {data.lastUpdatedBy + ? data.lastUpdatedBy.displayName + : data.createdBy.displayName} + + + + + {data.id} + + + + + + ) +} diff --git a/src/components/sectionList/detailsPanel/DetailItem.tsx b/src/components/sectionList/detailsPanel/DetailItem.tsx new file mode 100644 index 00000000..32aef7e6 --- /dev/null +++ b/src/components/sectionList/detailsPanel/DetailItem.tsx @@ -0,0 +1,39 @@ +import i18n from '@dhis2/d2-i18n' +import { Button } from '@dhis2/ui' +import React from 'react' +import css from './DetailsPanel.module.css' + +type DetailItemProps = { + children: React.ReactNode + label: string +} + +export const DetailItem = ({ label, children }: DetailItemProps) => { + return ( +
+
{label}
+
{children}
+
+ ) +} + +export const ApiUrlValue = ({ value }: { value: string }) => { + const handleCopyToClipboard = () => { + navigator.clipboard.writeText(value) + } + return ( + + + {i18n.t('API URL link')} + + + + ) +} diff --git a/src/components/sectionList/detailsPanel/DetailsPanel.module.css b/src/components/sectionList/detailsPanel/DetailsPanel.module.css new file mode 100644 index 00000000..2f11bab3 --- /dev/null +++ b/src/components/sectionList/detailsPanel/DetailsPanel.module.css @@ -0,0 +1,101 @@ +.detailsPanel { + width: 300px; + margin-left: var(--spacers-dp8); + grid-area: details; +} + +@media (max-width: 1200px) { + .detailsPanel { + margin-top: var(--spacers-dp8); + width: 100%; + /* max-height: 500px; */ + margin-left: 0; + } + .detailsPanelCard { + height: auto !important; + } +} + +.detailsPanelCard { + padding: var(--spacers-dp16); +} + +.detailsPanelWrapper { + position: sticky; + top: var(--spacers-dp8); +} + +.detailsPanelHeader { + display: flex; + align-items: center; + justify-content: space-between; + font-weight: 600; + padding-bottom: var(--spacers-dp16); +} + +.detailsPanelHeaderTitle { + font-size: 16px; + color: var(--colors-grey900); + font-weight: 500; +} + +.detailsPanelTitle { + padding: 0; + margin: 0; + font-size: 18px; + font-weight: 500; + margin-bottom: var(--spacers-dp12); +} + +.detailsPanelCloseButton { + border: none; + background: transparent; + width: 24px; + height: 24px; + border-radius: 3px; + padding: 0; + margin-right: -4px; +} + +.detailsPanelCloseButton:hover { + background: var(--colors-grey300); +} + +.detailsList { + display: flex; + flex-direction: column; + margin-top: var(--spacers-dp8); +} + +.detailItem { + display: flex; + justify-content: space-between; + padding: var(--spacers-dp8) 0; + gap: var(--spacers-dp4); + border-bottom: 1px solid var(--colors-grey300); +} + +.detailItem a { + color: var(--colors-blue700); + text-decoration: underline; +} + +.detailItem:last-of-type { + border-bottom: none; +} + +.detailItemLabel { + flex-shrink: 1; + min-width: 120px; + font-size: 13px; + color: var(--colors-grey700); +} + +.detailItemValue { + font-size: 14px; + text-align: right; +} + +.detailItemValueButton { + margin-left: 4px; +} diff --git a/src/components/sectionList/detailsPanel/DetailsPanel.tsx b/src/components/sectionList/detailsPanel/DetailsPanel.tsx new file mode 100644 index 00000000..52d63885 --- /dev/null +++ b/src/components/sectionList/detailsPanel/DetailsPanel.tsx @@ -0,0 +1,74 @@ +import i18n from '@dhis2/d2-i18n' +import { Card, IconCross24, Button, ButtonStrip, NoticeBox } from '@dhis2/ui' +import React, { PropsWithChildren } from 'react' +import { ErrorBoundary } from 'react-error-boundary' +import { Link } from 'react-router-dom' +import css from './DetailsPanel.module.css' + +type DetailsPanelProps = { + children: React.ReactNode + onClose: () => void +} + +export const DetailsPanel = ({ children, onClose }: DetailsPanelProps) => { + return ( + + ) +} + +type DetailsContentProps = { + children: React.ReactNode + displayName: string +} + +export const DetailsPanelContent = ({ + children, + displayName, +}: DetailsContentProps) => { + return ( +
+
{displayName}
+ {children} +
+ ) +} + +export const DetailsPanelButtons = ({ modelId }: { modelId: string }) => ( + + + + + +) + +const DetailsPanelHeader = ({ onClose }: { onClose: () => void }) => ( +
+ {i18n.t('Details')} + +
+) + +export const DetailsList = ({ children }: PropsWithChildren) => ( +
{children}
+) + +const DetailsPanelError = () => { + return ( + + {i18n.t('Failed to load details')} + + ) +} diff --git a/src/components/sectionList/detailsPanel/index.ts b/src/components/sectionList/detailsPanel/index.ts new file mode 100644 index 00000000..8182951d --- /dev/null +++ b/src/components/sectionList/detailsPanel/index.ts @@ -0,0 +1,3 @@ +export * from './DetailsPanel' +export * from './DetailItem' +export * from './DefaultDetailsPanel' diff --git a/src/components/sectionList/filters/IdentifiableFilter.tsx b/src/components/sectionList/filters/IdentifiableFilter.tsx index 8a3e7a8e..af00f6f2 100644 --- a/src/components/sectionList/filters/IdentifiableFilter.tsx +++ b/src/components/sectionList/filters/IdentifiableFilter.tsx @@ -1,5 +1,5 @@ import i18n from '@dhis2/d2-i18n' -import { Input } from '@dhis2/ui' +import { Input, InputEventPayload } from '@dhis2/ui' import React, { useEffect, useState } from 'react' import { useDebounce } from '../../../lib' import { InputOnChangeObject } from '../../../types' @@ -26,7 +26,9 @@ export const IdentifiableFilter = () => { setValue(value.value)} + onChange={(value: InputEventPayload) => + setValue(value.value ?? '') + } value={value} dataTest="input-search-name" dense diff --git a/src/lib/constants/translatedModelProperties.ts b/src/lib/constants/translatedModelProperties.ts index 3289d314..5ef6c0bf 100644 --- a/src/lib/constants/translatedModelProperties.ts +++ b/src/lib/constants/translatedModelProperties.ts @@ -5,7 +5,7 @@ const TRANSLATED_PROPERTY: Record = { code: i18n.t('Code'), createdBy: i18n.t('Created by'), favorite: i18n.t('Favorite'), - href: i18n.t('Href'), + href: i18n.t('API URL'), id: i18n.t('Id'), lastUpdatedBy: i18n.t('Last updated by'), created: i18n.t('Created'), @@ -15,7 +15,6 @@ const TRANSLATED_PROPERTY: Record = { sharing: i18n.t('Sharing'), shortName: i18n.t('Short name'), valueType: i18n.t('Value type'), - user: i18n.t('Owner'), // user refers to the owner of the object zeroIsSignificant: i18n.t('Zero is significant'), } diff --git a/src/lib/dataStore/DataStore.ts b/src/lib/dataStore/DataStore.ts new file mode 100644 index 00000000..314dbe15 --- /dev/null +++ b/src/lib/dataStore/DataStore.ts @@ -0,0 +1 @@ +export class DataStore {} diff --git a/src/lib/date/index.ts b/src/lib/date/index.ts index 4213f2b5..2317ca30 100644 --- a/src/lib/date/index.ts +++ b/src/lib/date/index.ts @@ -1 +1,2 @@ export * from './relativeTime' +export { selectedLocale, relativeTimeLocale } from './locale' diff --git a/src/pages/dataElements/form/EditableFieldWrapper.tsx b/src/pages/dataElements/form/EditableFieldWrapper.tsx index 3610d3b8..a27ea3c9 100644 --- a/src/pages/dataElements/form/EditableFieldWrapper.tsx +++ b/src/pages/dataElements/form/EditableFieldWrapper.tsx @@ -18,10 +18,10 @@ export function EditableFieldWrapper({
{children}
- -
diff --git a/src/pages/dataElements/form/fields.tsx b/src/pages/dataElements/form/fields.tsx index a40ccc31..0d686370 100644 --- a/src/pages/dataElements/form/fields.tsx +++ b/src/pages/dataElements/form/fields.tsx @@ -4,8 +4,8 @@ import { Button, CheckboxFieldFF, Field, + RadioFieldFF, InputFieldFF, - Radio, SingleSelectFieldFF, TextAreaFieldFF, } from '@dhis2/ui' @@ -57,7 +57,7 @@ export function NameField() { )} // Do not pass checkIsValueTaken directly, otherwise memoization // won't work due to additional arguments being passed - validate={(name: string) => checkIsValueTaken(name)} + validate={(name: string | undefined) => checkIsValueTaken(name)} validateFields={[]} /> ) @@ -88,7 +88,9 @@ export function ShortNameField() { helpText={i18n.t('Often used in reports where space is limited')} // Do not pass checkIsValueTaken directly, otherwise memoization // won't work due to additional arguments being passed - validate={(shortName: string) => checkIsValueTaken(shortName)} + validate={(shortName: string | undefined) => + checkIsValueTaken(shortName) + } validateFields={[]} /> ) @@ -243,28 +245,17 @@ export function DomainField() { validationText={touched ? error : undefined} >
- - ) => { - aggregateInput.input.onChange(e) - }} + input={aggregateInput.input} + meta={aggregateInput.meta} /> - - - ) => { - trackerInput.input.onChange(e) - }} + label={DOMAIN_TYPE.TRACKER} + input={trackerInput.input} + meta={trackerInput.meta} />
diff --git a/src/pages/dataElements/form/useIsFieldValueUnique.ts b/src/pages/dataElements/form/useIsFieldValueUnique.ts index 8f4e65ac..2c810e58 100644 --- a/src/pages/dataElements/form/useIsFieldValueUnique.ts +++ b/src/pages/dataElements/form/useIsFieldValueUnique.ts @@ -37,7 +37,7 @@ export function useIsFieldValueUnique({ const validate = useMemo( () => - memoize(async (value: string) => { + memoize(async (value?: string) => { if (!value) { return undefined } diff --git a/src/types/query.ts b/src/types/query.ts index b4f9415f..033fa358 100644 --- a/src/types/query.ts +++ b/src/types/query.ts @@ -5,3 +5,7 @@ export type QueryResponse = ReturnType export type Query = Parameters[0] export type QueryRefetchFunction = QueryResponse['refetch'] + +export type WrapQueryResponse = { + [K in S]: T +} diff --git a/yarn.lock b/yarn.lock index 34a2574d..71a82886 100644 --- a/yarn.lock +++ b/yarn.lock @@ -1407,583 +1407,583 @@ debug "^3.1.0" lodash.once "^4.1.1" -"@dhis2-ui/alert@8.14.7": - version "8.14.7" - resolved "https://registry.yarnpkg.com/@dhis2-ui/alert/-/alert-8.14.7.tgz#53cdc9bbdceee01dd5b86d0615a1c57314e0e307" - integrity sha512-yOjXDCPOeOpokWr2Qzs9wwQ4P2NqwHaaUL/TKank4JCSLflbH7KSE3jxnzYWwfdTs5PO058N4rKUFOm5XUgLGA== +"@dhis2-ui/alert@8.15.0-alpha.3": + version "8.15.0-alpha.3" + resolved "https://registry.yarnpkg.com/@dhis2-ui/alert/-/alert-8.15.0-alpha.3.tgz#e36bec11a41c37480b31e7a18b953353b4b67b28" + integrity sha512-UpRdmY/H2csT92l8v78SAemMO0TQKt6Az4xXwarFJWd1H6pEAf1gB2uppdqF2UG+9+FEBpDssxpV9tw6+7Ao9A== dependencies: - "@dhis2-ui/portal" "8.14.7" + "@dhis2-ui/portal" "8.15.0-alpha.3" "@dhis2/prop-types" "^3.1.2" - "@dhis2/ui-constants" "8.14.7" - "@dhis2/ui-icons" "8.14.7" + "@dhis2/ui-constants" "8.15.0-alpha.3" + "@dhis2/ui-icons" "8.15.0-alpha.3" classnames "^2.3.1" prop-types "^15.7.2" -"@dhis2-ui/box@8.14.7": - version "8.14.7" - resolved "https://registry.yarnpkg.com/@dhis2-ui/box/-/box-8.14.7.tgz#1f7eabb6da21e67d95a49b5410af151cf61de92b" - integrity sha512-fmeygDJaGUvm1hs5uqLxDEo1Ar45F4AdXqFjoF1qOP8Kce83KnXw27iPy+YGN580xlXoB0nEUbG/nghvGKp/lA== +"@dhis2-ui/box@8.15.0-alpha.3": + version "8.15.0-alpha.3" + resolved "https://registry.yarnpkg.com/@dhis2-ui/box/-/box-8.15.0-alpha.3.tgz#5b01d6a645647f3c36d934c0f760eeeabdbc960d" + integrity sha512-6b4hN0GrP0htwQLuAjcdtAGkOwBvQnpAkgHrFz8VVv+DnebW8KIaxFNT2bk/V9dYk4wU9JguwgFgg8iI1JggBw== dependencies: "@dhis2/prop-types" "^3.1.2" - "@dhis2/ui-constants" "8.14.7" + "@dhis2/ui-constants" "8.15.0-alpha.3" classnames "^2.3.1" prop-types "^15.7.2" -"@dhis2-ui/button@8.14.7": - version "8.14.7" - resolved "https://registry.yarnpkg.com/@dhis2-ui/button/-/button-8.14.7.tgz#c241b42d0bbb98c594a8183f1bc432be4a5e23af" - integrity sha512-unqGp3/vFAOHqUE8JPfLiNfywt5e2RZOGiRlMhtTrF3sCTrFIsRoHn4NZ/vtm1g/Cx6ImtNIOL2UkiPHx+RLQQ== +"@dhis2-ui/button@8.15.0-alpha.3": + version "8.15.0-alpha.3" + resolved "https://registry.yarnpkg.com/@dhis2-ui/button/-/button-8.15.0-alpha.3.tgz#dbd7341364f99005d469ba5d2eb048073fe35987" + integrity sha512-I+G/LMUyc/n/zwPj4GELnj/Q/2VCq34e41P82Ado9ufn4zjsCTJhEum1qzZmZ2EJwCV1ZVRp49NSSB4+xWHAqA== dependencies: - "@dhis2-ui/layer" "8.14.7" - "@dhis2-ui/loader" "8.14.7" - "@dhis2-ui/popper" "8.14.7" + "@dhis2-ui/layer" "8.15.0-alpha.3" + "@dhis2-ui/loader" "8.15.0-alpha.3" + "@dhis2-ui/popper" "8.15.0-alpha.3" "@dhis2/prop-types" "^3.1.2" - "@dhis2/ui-constants" "8.14.7" - "@dhis2/ui-icons" "8.14.7" + "@dhis2/ui-constants" "8.15.0-alpha.3" + "@dhis2/ui-icons" "8.15.0-alpha.3" classnames "^2.3.1" prop-types "^15.7.2" -"@dhis2-ui/calendar@8.14.7": - version "8.14.7" - resolved "https://registry.yarnpkg.com/@dhis2-ui/calendar/-/calendar-8.14.7.tgz#16453c22bfe707f93e695318c6526c14137a4adf" - integrity sha512-+wbEcLvbHgEIPurucxFjg2sdF9JFFU1Q/r2J/xPKviDx3CLYJtsKS3i/3jEDV8UHvMr74JBX/WcX9MwVeZOEzw== +"@dhis2-ui/calendar@8.15.0-alpha.3": + version "8.15.0-alpha.3" + resolved "https://registry.yarnpkg.com/@dhis2-ui/calendar/-/calendar-8.15.0-alpha.3.tgz#c4b6337c38a6753ab487d814cf1c583493720956" + integrity sha512-KmSyIdC6pajp5ulfKI5d9VIEPyfGyR+Ns00aPUOWw4UeW82yHo9zFvSVdnnaHdQhe6qj14T8zEyVsPgvFS36Hw== dependencies: - "@dhis2-ui/button" "8.14.7" - "@dhis2-ui/card" "8.14.7" - "@dhis2-ui/input" "8.14.7" - "@dhis2-ui/layer" "8.14.7" - "@dhis2-ui/popper" "8.14.7" + "@dhis2-ui/button" "8.15.0-alpha.3" + "@dhis2-ui/card" "8.15.0-alpha.3" + "@dhis2-ui/input" "8.15.0-alpha.3" + "@dhis2-ui/layer" "8.15.0-alpha.3" + "@dhis2-ui/popper" "8.15.0-alpha.3" "@dhis2/multi-calendar-dates" "1.0.2" "@dhis2/prop-types" "^3.1.2" - "@dhis2/ui-constants" "8.14.7" - "@dhis2/ui-icons" "8.14.7" + "@dhis2/ui-constants" "8.15.0-alpha.3" + "@dhis2/ui-icons" "8.15.0-alpha.3" classnames "^2.3.1" prop-types "^15.7.2" -"@dhis2-ui/card@8.14.7": - version "8.14.7" - resolved "https://registry.yarnpkg.com/@dhis2-ui/card/-/card-8.14.7.tgz#ee944a2c86a8f3c397da32b2c0adf3ca956a06b8" - integrity sha512-OMmr2Z+nne9SJ8Lzei+ajBB/tV1cSgnZedDUd+ohbJPqsQ00cbDfU5hX4Um/vrjq6F9KxDNEzz+oyXCOdLEobw== +"@dhis2-ui/card@8.15.0-alpha.3": + version "8.15.0-alpha.3" + resolved "https://registry.yarnpkg.com/@dhis2-ui/card/-/card-8.15.0-alpha.3.tgz#d2f4631012df9cb19e8fa4c76edc0e0bbbea7964" + integrity sha512-iqWuXgx5fsS5w91hruc/D3ZtVtkoCORjyL5m9COcRHD2AM04q7jQhboOBu+Du28sz899Du9EueRgRxliyiw90Q== dependencies: "@dhis2/prop-types" "^3.1.2" - "@dhis2/ui-constants" "8.14.7" + "@dhis2/ui-constants" "8.15.0-alpha.3" classnames "^2.3.1" prop-types "^15.7.2" -"@dhis2-ui/center@8.14.7": - version "8.14.7" - resolved "https://registry.yarnpkg.com/@dhis2-ui/center/-/center-8.14.7.tgz#374d87df7570000549312d4d42d42f1ba8af1dfd" - integrity sha512-e1pjicJ2/h9D5acy31oXtIHHKtr13duaY8Tj/SK6A4Ytdry/U0Ay167B+UrGrE9ell7ChqaOa1Cqft/+9jjOSA== +"@dhis2-ui/center@8.15.0-alpha.3": + version "8.15.0-alpha.3" + resolved "https://registry.yarnpkg.com/@dhis2-ui/center/-/center-8.15.0-alpha.3.tgz#8b357e09d34eab83e93d9680fc14ee951c48ba07" + integrity sha512-syHIcA5Pq35B/opRY96I50kB7hlMHWrDK+5BayDiboCUCXWqwVAW/o73jG4zLJujjSkkEl546bgnJN6dureo4A== dependencies: "@dhis2/prop-types" "^3.1.2" - "@dhis2/ui-constants" "8.14.7" + "@dhis2/ui-constants" "8.15.0-alpha.3" classnames "^2.3.1" prop-types "^15.7.2" -"@dhis2-ui/checkbox@8.14.7": - version "8.14.7" - resolved "https://registry.yarnpkg.com/@dhis2-ui/checkbox/-/checkbox-8.14.7.tgz#ae96afc1063fddd642a72b37f7a18c8574e2311e" - integrity sha512-x6gIhNpo6UxW9KImVF0Ja4qUMp1NkhUSVrNsNTpGnar8ucBvEwh2FBrGmuQbJ2ba4SRY/lpRUD0RKqsorfAgjQ== +"@dhis2-ui/checkbox@8.15.0-alpha.3": + version "8.15.0-alpha.3" + resolved "https://registry.yarnpkg.com/@dhis2-ui/checkbox/-/checkbox-8.15.0-alpha.3.tgz#68a5f977f84a1266c4c659fa35a8397644dddbb9" + integrity sha512-QjdcGPJ932yOSV0ZKkSLk6feH3IPYRPH9gWurvByU+9xEIeApgyDjzfmf2UrL90TGtN94rZpJFy0ZtmkTRM2yg== dependencies: - "@dhis2-ui/field" "8.14.7" - "@dhis2-ui/required" "8.14.7" + "@dhis2-ui/field" "8.15.0-alpha.3" + "@dhis2-ui/required" "8.15.0-alpha.3" "@dhis2/prop-types" "^3.1.2" - "@dhis2/ui-constants" "8.14.7" + "@dhis2/ui-constants" "8.15.0-alpha.3" classnames "^2.3.1" prop-types "^15.7.2" -"@dhis2-ui/chip@8.14.7": - version "8.14.7" - resolved "https://registry.yarnpkg.com/@dhis2-ui/chip/-/chip-8.14.7.tgz#b57398e867d4a06847a33faad054ed67f2a195c0" - integrity sha512-XWIW9vJv98yMUWDIAO0lYMFbTRYOH74h1E54uJu59G1wt1Wq7KX3kwKpbcN6iL8735wAbJr24mCgx/0p19rKHA== +"@dhis2-ui/chip@8.15.0-alpha.3": + version "8.15.0-alpha.3" + resolved "https://registry.yarnpkg.com/@dhis2-ui/chip/-/chip-8.15.0-alpha.3.tgz#de5b59e8e0cb7b48cdd6f1da57ebec4fa9b13603" + integrity sha512-OQ/ssBOhdc6FTCdLm/V2ZycMoUIFrdbQMoKO37n2LxdB0C0+koEHt/uQky1sH2KEGpp1IwIsVFwyeN73dGbDuw== dependencies: "@dhis2/prop-types" "^3.1.2" - "@dhis2/ui-constants" "8.14.7" + "@dhis2/ui-constants" "8.15.0-alpha.3" classnames "^2.3.1" prop-types "^15.7.2" -"@dhis2-ui/cover@8.14.7": - version "8.14.7" - resolved "https://registry.yarnpkg.com/@dhis2-ui/cover/-/cover-8.14.7.tgz#c023217b961f2becc479566468a9a0df96cdb0c0" - integrity sha512-M3KnrnQD+JYoiw0VKwZ/vpWNuvdvs/ZzJH5McYAJ5pGkfnBh6N9Xwch5jvvcI0hpcCKcWgStQdiC8d96FvQ0Aw== +"@dhis2-ui/cover@8.15.0-alpha.3": + version "8.15.0-alpha.3" + resolved "https://registry.yarnpkg.com/@dhis2-ui/cover/-/cover-8.15.0-alpha.3.tgz#6ccf7966b62905b3800d4df6eb239cd74955242b" + integrity sha512-nTSlYaqyyddNkrfwscykXc5E4qUJ6H/2YGJDoJ4AKRing6i6GU+HAcXMuxx/YENqIHacBbQdESVtuxh/W0iyyg== dependencies: "@dhis2/prop-types" "^3.1.2" - "@dhis2/ui-constants" "8.14.7" + "@dhis2/ui-constants" "8.15.0-alpha.3" classnames "^2.3.1" prop-types "^15.7.2" -"@dhis2-ui/css@8.14.7": - version "8.14.7" - resolved "https://registry.yarnpkg.com/@dhis2-ui/css/-/css-8.14.7.tgz#8047e2ed197ec4ad03ad69e374a57e5b4eb914fe" - integrity sha512-zariDzf9LphEh9z1oRYFmSQpVJltLG5X2ZmicgmY7tAJgmMtmzhwLOrsKlVVuIXTQjlNyr9JXWVEFNCfLRXqQQ== +"@dhis2-ui/css@8.15.0-alpha.3": + version "8.15.0-alpha.3" + resolved "https://registry.yarnpkg.com/@dhis2-ui/css/-/css-8.15.0-alpha.3.tgz#88bdc044ba8428040a2f6f3d22b8bb647f2eea57" + integrity sha512-k0QppE50+CsQ0jyrkaVGdV7RYwFxQ+4AuM16XbWsjCJ17oZNvtLW3bGMlIobrp5MVsRuIycaCN/oPy0dD5MS+g== dependencies: "@dhis2/prop-types" "^3.1.2" - "@dhis2/ui-constants" "8.14.7" + "@dhis2/ui-constants" "8.15.0-alpha.3" classnames "^2.3.1" prop-types "^15.7.2" -"@dhis2-ui/divider@8.14.7": - version "8.14.7" - resolved "https://registry.yarnpkg.com/@dhis2-ui/divider/-/divider-8.14.7.tgz#ba3ae31e5e62b610ca9f2e7e041ad623116a73fd" - integrity sha512-rP64+oQyhLjszHd9Q/UBEgyaZkbUO30IBrINt/rwhH9pQDpYakcvF3Yor2AmO9DsbgaTWXJlGRQwEbpNir7kDw== +"@dhis2-ui/divider@8.15.0-alpha.3": + version "8.15.0-alpha.3" + resolved "https://registry.yarnpkg.com/@dhis2-ui/divider/-/divider-8.15.0-alpha.3.tgz#5433cb0cb16360aed018fe66cfb5f24dfdb40cfc" + integrity sha512-t9wolXFldgPbn3Qwzkj1KVg2UKXl9gNkK+73rM/ah/5VsA0C2vrQB+5MWXrohlylcME8/9QuOuvVvfO0DiHKRA== dependencies: "@dhis2/prop-types" "^3.1.2" - "@dhis2/ui-constants" "8.14.7" + "@dhis2/ui-constants" "8.15.0-alpha.3" classnames "^2.3.1" prop-types "^15.7.2" -"@dhis2-ui/field@8.14.7": - version "8.14.7" - resolved "https://registry.yarnpkg.com/@dhis2-ui/field/-/field-8.14.7.tgz#48e27a454b65ddc09b8025a8cfd64505e8448975" - integrity sha512-+Kn56y7/lUMmax7IDqZU3NruzgvWr+WWu02ZNKJ0kIYS7AMbG3q5eesF7DR0OpKMLfoFWxeXCcMcmE9VPtdVDQ== +"@dhis2-ui/field@8.15.0-alpha.3": + version "8.15.0-alpha.3" + resolved "https://registry.yarnpkg.com/@dhis2-ui/field/-/field-8.15.0-alpha.3.tgz#1402e4ad6cd81879587335599364f71d7e80c6e9" + integrity sha512-2xpSn0JOK1cPCpauBIf+J4VYtsWQ1tUvCSmxDSz+lgqnS36+Wvenf4jIicNaYn8Lkl5qDxGVcc0AiCRgPB7suQ== dependencies: - "@dhis2-ui/box" "8.14.7" - "@dhis2-ui/help" "8.14.7" - "@dhis2-ui/label" "8.14.7" + "@dhis2-ui/box" "8.15.0-alpha.3" + "@dhis2-ui/help" "8.15.0-alpha.3" + "@dhis2-ui/label" "8.15.0-alpha.3" "@dhis2/prop-types" "^3.1.2" - "@dhis2/ui-constants" "8.14.7" + "@dhis2/ui-constants" "8.15.0-alpha.3" classnames "^2.3.1" prop-types "^15.7.2" -"@dhis2-ui/file-input@8.14.7": - version "8.14.7" - resolved "https://registry.yarnpkg.com/@dhis2-ui/file-input/-/file-input-8.14.7.tgz#c9278b157bd5ef457a99988c627557c280154388" - integrity sha512-+WQhskA1ulSKfD2t/dvSuXQ83ETx6N3r7ogtmlsj/zrh3eEN+bKkGvRCA/SvfqvJfblyk5/GpnjcwffZh45Twg== +"@dhis2-ui/file-input@8.15.0-alpha.3": + version "8.15.0-alpha.3" + resolved "https://registry.yarnpkg.com/@dhis2-ui/file-input/-/file-input-8.15.0-alpha.3.tgz#42b233aaa39605238b7e981cb1ed50c7ceb4bea7" + integrity sha512-skuMhUsqxPdnTuCyxcVy0EmV6S2nhs0qKoVC3LATX6btTTgO9AEVxJVcz93mtu1j3Bwg+qYnW25hA23jWtj8zg== dependencies: - "@dhis2-ui/button" "8.14.7" - "@dhis2-ui/field" "8.14.7" - "@dhis2-ui/label" "8.14.7" - "@dhis2-ui/loader" "8.14.7" - "@dhis2-ui/status-icon" "8.14.7" + "@dhis2-ui/button" "8.15.0-alpha.3" + "@dhis2-ui/field" "8.15.0-alpha.3" + "@dhis2-ui/label" "8.15.0-alpha.3" + "@dhis2-ui/loader" "8.15.0-alpha.3" + "@dhis2-ui/status-icon" "8.15.0-alpha.3" "@dhis2/prop-types" "^3.1.2" - "@dhis2/ui-constants" "8.14.7" - "@dhis2/ui-icons" "8.14.7" + "@dhis2/ui-constants" "8.15.0-alpha.3" + "@dhis2/ui-icons" "8.15.0-alpha.3" classnames "^2.3.1" prop-types "^15.7.2" -"@dhis2-ui/header-bar@8.14.7": - version "8.14.7" - resolved "https://registry.yarnpkg.com/@dhis2-ui/header-bar/-/header-bar-8.14.7.tgz#e7291dee9c6a5d4f95f6665fe47ae8695af102fc" - integrity sha512-4KdF595jQL0kYea6qgTW0ak95AZKi9NKpuhKTC0TPvYxBVUBxD7fPTKARV2syuwmlRGNU4pVzcJ6vne1dU7l0Q== - dependencies: - "@dhis2-ui/box" "8.14.7" - "@dhis2-ui/button" "8.14.7" - "@dhis2-ui/card" "8.14.7" - "@dhis2-ui/center" "8.14.7" - "@dhis2-ui/divider" "8.14.7" - "@dhis2-ui/input" "8.14.7" - "@dhis2-ui/layer" "8.14.7" - "@dhis2-ui/loader" "8.14.7" - "@dhis2-ui/logo" "8.14.7" - "@dhis2-ui/menu" "8.14.7" - "@dhis2-ui/modal" "8.14.7" - "@dhis2-ui/user-avatar" "8.14.7" +"@dhis2-ui/header-bar@8.15.0-alpha.3": + version "8.15.0-alpha.3" + resolved "https://registry.yarnpkg.com/@dhis2-ui/header-bar/-/header-bar-8.15.0-alpha.3.tgz#36a12a9e89708a59ffdb26acb1d2033e1ef00b5f" + integrity sha512-69mqM5cP2vY+3MdBL50U0xLGqb2oG+G2kAGoXen/dmbP5ftuPlI7gGrfTcNBRnk27u2a+Z4+ZbaOcmf6ED+uyw== + dependencies: + "@dhis2-ui/box" "8.15.0-alpha.3" + "@dhis2-ui/button" "8.15.0-alpha.3" + "@dhis2-ui/card" "8.15.0-alpha.3" + "@dhis2-ui/center" "8.15.0-alpha.3" + "@dhis2-ui/divider" "8.15.0-alpha.3" + "@dhis2-ui/input" "8.15.0-alpha.3" + "@dhis2-ui/layer" "8.15.0-alpha.3" + "@dhis2-ui/loader" "8.15.0-alpha.3" + "@dhis2-ui/logo" "8.15.0-alpha.3" + "@dhis2-ui/menu" "8.15.0-alpha.3" + "@dhis2-ui/modal" "8.15.0-alpha.3" + "@dhis2-ui/user-avatar" "8.15.0-alpha.3" "@dhis2/prop-types" "^3.1.2" - "@dhis2/ui-constants" "8.14.7" - "@dhis2/ui-icons" "8.14.7" + "@dhis2/ui-constants" "8.15.0-alpha.3" + "@dhis2/ui-icons" "8.15.0-alpha.3" classnames "^2.3.1" moment "^2.29.1" prop-types "^15.7.2" -"@dhis2-ui/help@8.14.7": - version "8.14.7" - resolved "https://registry.yarnpkg.com/@dhis2-ui/help/-/help-8.14.7.tgz#fad62ccf6c0d93251b189638ce3a483964acb86a" - integrity sha512-7gw8KPBE5C4iQhne0GDJJ4XKh5dD/MQ1NPBkXNcUFklXy2018omWQc5/qauaIK7DavcdNcikFN5XDYjo2Ezzog== +"@dhis2-ui/help@8.15.0-alpha.3": + version "8.15.0-alpha.3" + resolved "https://registry.yarnpkg.com/@dhis2-ui/help/-/help-8.15.0-alpha.3.tgz#6367be5c2f242ae54db6944af96087f91837c7ae" + integrity sha512-Ccg1TQiQrv/PrKbz7kl0fwjOTZhgHc7tV2bpfhBoq3wlKGu/fqm9mMNslIXbM/IxWKOMn6O9e60FWxB2VhwChg== dependencies: "@dhis2/prop-types" "^3.1.2" - "@dhis2/ui-constants" "8.14.7" + "@dhis2/ui-constants" "8.15.0-alpha.3" classnames "^2.3.1" prop-types "^15.7.2" -"@dhis2-ui/input@8.14.7": - version "8.14.7" - resolved "https://registry.yarnpkg.com/@dhis2-ui/input/-/input-8.14.7.tgz#8ab3bb228932bd52a3fd06eb546dba5b7d76eb15" - integrity sha512-jW2x1SrRkM27gupt4AJvMvSXsGiq5FMCgwz8WeRbbazjrxy77vTHCmPFqc+40qV9OSCCxOT6gMo0nT598QJ5kA== +"@dhis2-ui/input@8.15.0-alpha.3": + version "8.15.0-alpha.3" + resolved "https://registry.yarnpkg.com/@dhis2-ui/input/-/input-8.15.0-alpha.3.tgz#06f17b4f61e4398a224500b9f0f2920493c263bc" + integrity sha512-GVaxojlqNklUTVeO2I9h+KH5lBDdh+Kp92s2pxk5eUU2t3zKQNCfQFrqyNO5yB7Gfh89kzxmeas+ANGay0Qr1g== dependencies: - "@dhis2-ui/box" "8.14.7" - "@dhis2-ui/field" "8.14.7" - "@dhis2-ui/input" "8.14.7" - "@dhis2-ui/loader" "8.14.7" - "@dhis2-ui/status-icon" "8.14.7" + "@dhis2-ui/box" "8.15.0-alpha.3" + "@dhis2-ui/field" "8.15.0-alpha.3" + "@dhis2-ui/input" "8.15.0-alpha.3" + "@dhis2-ui/loader" "8.15.0-alpha.3" + "@dhis2-ui/status-icon" "8.15.0-alpha.3" "@dhis2/prop-types" "^3.1.2" - "@dhis2/ui-constants" "8.14.7" - "@dhis2/ui-icons" "8.14.7" + "@dhis2/ui-constants" "8.15.0-alpha.3" + "@dhis2/ui-icons" "8.15.0-alpha.3" classnames "^2.3.1" prop-types "^15.7.2" -"@dhis2-ui/intersection-detector@8.14.7": - version "8.14.7" - resolved "https://registry.yarnpkg.com/@dhis2-ui/intersection-detector/-/intersection-detector-8.14.7.tgz#4b510471b5b1434da8ca31eb7c8df789a29cdc2c" - integrity sha512-AA21wPKn7xVirJ/L3O+I/rXL1GHTshpLqMCetdPudJFHShEjeG3g+Nn2B3La01PsZWedN5EGrML+dlqEylKxYg== +"@dhis2-ui/intersection-detector@8.15.0-alpha.3": + version "8.15.0-alpha.3" + resolved "https://registry.yarnpkg.com/@dhis2-ui/intersection-detector/-/intersection-detector-8.15.0-alpha.3.tgz#3f8c9a1616dac7ffce307929fd49209f1415682a" + integrity sha512-kHfrKlT9IPVpH+TvIOlE1uCnZgYaOevVw9p9gdb7mCKd/SfAVgZP/bz3epQ4KfkeXdthqi+QQsNtaHtb92usCA== dependencies: "@dhis2/prop-types" "^3.1.2" - "@dhis2/ui-constants" "8.14.7" + "@dhis2/ui-constants" "8.15.0-alpha.3" classnames "^2.3.1" prop-types "^15.7.2" -"@dhis2-ui/label@8.14.7": - version "8.14.7" - resolved "https://registry.yarnpkg.com/@dhis2-ui/label/-/label-8.14.7.tgz#64cce8f2cb3433fdc5feffbeda1d7d58b4d63df4" - integrity sha512-iWmKRDsXKbWMolYL6aTeGZtg0Tf1Cz7qfcGYFaE3ofhOELrwcZS+s/ikgCy4lFWqm/KdtCHMNdiQugkMww51hQ== +"@dhis2-ui/label@8.15.0-alpha.3": + version "8.15.0-alpha.3" + resolved "https://registry.yarnpkg.com/@dhis2-ui/label/-/label-8.15.0-alpha.3.tgz#fd0425123e1c918f818e3c129818dca6b9534d38" + integrity sha512-nRuGn/Wi/C96/KbQupqxjqKGCxtt5WaHKRLO/07as2FWxkKcxGlHp+LrjkQplRTlBOE0GOinNwbtmt24ojx03Q== dependencies: - "@dhis2-ui/required" "8.14.7" + "@dhis2-ui/required" "8.15.0-alpha.3" "@dhis2/prop-types" "^3.1.2" - "@dhis2/ui-constants" "8.14.7" + "@dhis2/ui-constants" "8.15.0-alpha.3" classnames "^2.3.1" prop-types "^15.7.2" -"@dhis2-ui/layer@8.14.7": - version "8.14.7" - resolved "https://registry.yarnpkg.com/@dhis2-ui/layer/-/layer-8.14.7.tgz#ab95b3bdfe023e4ceaba93339e5fae165a148c1c" - integrity sha512-NEHFY7TTsNMQaGyZ+zBiGA3yqqUwBaZm6c/BGzNMC1gXfn2AM3aHJViKrJOuGb0eK1/FLBEMjjhVVAHnby0xcg== +"@dhis2-ui/layer@8.15.0-alpha.3": + version "8.15.0-alpha.3" + resolved "https://registry.yarnpkg.com/@dhis2-ui/layer/-/layer-8.15.0-alpha.3.tgz#34620dc3d0d389583f7faf6a8331d62be7ca9c82" + integrity sha512-yS6wyubprzihI0BlqFxgLW2lRFGpL0rm0uUghUAlVXPFTZje8B2R7eW4mNGhZELOXQoq/MK2eY8XXZt1BKiIDQ== dependencies: - "@dhis2-ui/portal" "8.14.7" + "@dhis2-ui/portal" "8.15.0-alpha.3" "@dhis2/prop-types" "^3.1.2" - "@dhis2/ui-constants" "8.14.7" + "@dhis2/ui-constants" "8.15.0-alpha.3" classnames "^2.3.1" prop-types "^15.7.2" -"@dhis2-ui/legend@8.14.7": - version "8.14.7" - resolved "https://registry.yarnpkg.com/@dhis2-ui/legend/-/legend-8.14.7.tgz#56f595da523e8a1dbf1728e8beaf2cff75536612" - integrity sha512-UsX9TMIPIE45ltM0U0X5YwyrFvIJlpdplk3LQmZF2tL7hs6kwp6hWMGpOxQ//Gx9ptrKs6DynrvC2y2jMvCf1Q== +"@dhis2-ui/legend@8.15.0-alpha.3": + version "8.15.0-alpha.3" + resolved "https://registry.yarnpkg.com/@dhis2-ui/legend/-/legend-8.15.0-alpha.3.tgz#bdbeac4b2e48399794533583022a3887a5d9a5c0" + integrity sha512-t4uzMhSj5AV5uBUAYMfM7EB6L6prIQTCoSHeVkKcuxxI6toD7wtGdgKNFH1eDJjtzH6Cdilk4E/a7qfDgUOB7w== dependencies: - "@dhis2-ui/required" "8.14.7" + "@dhis2-ui/required" "8.15.0-alpha.3" "@dhis2/prop-types" "^3.1.2" - "@dhis2/ui-constants" "8.14.7" + "@dhis2/ui-constants" "8.15.0-alpha.3" classnames "^2.3.1" prop-types "^15.7.2" -"@dhis2-ui/loader@8.14.7": - version "8.14.7" - resolved "https://registry.yarnpkg.com/@dhis2-ui/loader/-/loader-8.14.7.tgz#77b951c45f72de4488704d59e8da8d86b680f0e8" - integrity sha512-E45+OU/f3EJNdR3KUJrruVmNjZyra6v1+yQamWgrIukJGB1OsCeRxLT2oVCXFvbHjT4ULAYnJm+zQuIVT0AiMA== +"@dhis2-ui/loader@8.15.0-alpha.3": + version "8.15.0-alpha.3" + resolved "https://registry.yarnpkg.com/@dhis2-ui/loader/-/loader-8.15.0-alpha.3.tgz#77a3ffcdc60e1192218a6c909913164476581ecf" + integrity sha512-mOhgf3d1ZInPCu5UpYc0RtXZ+4c49+4A8o2wT1L9mIkq683wN9j9NjSgXtaRsYdLHOT2B5CaRWfXCoX8qpAIMg== dependencies: "@dhis2/prop-types" "^3.1.2" - "@dhis2/ui-constants" "8.14.7" + "@dhis2/ui-constants" "8.15.0-alpha.3" classnames "^2.3.1" prop-types "^15.7.2" -"@dhis2-ui/logo@8.14.7": - version "8.14.7" - resolved "https://registry.yarnpkg.com/@dhis2-ui/logo/-/logo-8.14.7.tgz#3b0c28badf9d30c2c22df8688782d1650da28973" - integrity sha512-wyyNALhGw0+50rHYJUFTsE9eBwYLHkpGjDd8psb8ildUE+AUzGpJ9YUhjRbZkR26mxn2ieO5U1SPhbbjJDY4vQ== +"@dhis2-ui/logo@8.15.0-alpha.3": + version "8.15.0-alpha.3" + resolved "https://registry.yarnpkg.com/@dhis2-ui/logo/-/logo-8.15.0-alpha.3.tgz#86dc5dffe855ee0238ee94ff3d03593913e7073d" + integrity sha512-opADR07u7T7oO77V0D2133K542fPQVcMLV3yoFxR485ysbbt+5cO+z1EASWxUpFKpwJd6DT03RtiUvoYwCFQ8w== dependencies: "@dhis2/prop-types" "^3.1.2" - "@dhis2/ui-constants" "8.14.7" + "@dhis2/ui-constants" "8.15.0-alpha.3" classnames "^2.3.1" prop-types "^15.7.2" -"@dhis2-ui/menu@8.14.7": - version "8.14.7" - resolved "https://registry.yarnpkg.com/@dhis2-ui/menu/-/menu-8.14.7.tgz#20c448da00c1fb543000c34914b49b3b135a633a" - integrity sha512-hZAqQSURJOjuT3jAU5JqNTlNEildaVsmNsO8q9103R0Jf5azv4KJ1UWJjj3gu5S/+kxWDZwD8c9UQRxINlsLMw== +"@dhis2-ui/menu@8.15.0-alpha.3": + version "8.15.0-alpha.3" + resolved "https://registry.yarnpkg.com/@dhis2-ui/menu/-/menu-8.15.0-alpha.3.tgz#02b26f098fdd2dd124b47799586cd571ff5795aa" + integrity sha512-a9ZHQSToOWdZmrlBNL6DvZFnFsw5Ftk1K7yYrCyQPu/DYsa6Svdm6/72s0wVhbbpjpWE9mEYkE62CMEMd1z9+w== dependencies: - "@dhis2-ui/card" "8.14.7" - "@dhis2-ui/divider" "8.14.7" - "@dhis2-ui/layer" "8.14.7" - "@dhis2-ui/popper" "8.14.7" - "@dhis2-ui/portal" "8.14.7" + "@dhis2-ui/card" "8.15.0-alpha.3" + "@dhis2-ui/divider" "8.15.0-alpha.3" + "@dhis2-ui/layer" "8.15.0-alpha.3" + "@dhis2-ui/popper" "8.15.0-alpha.3" + "@dhis2-ui/portal" "8.15.0-alpha.3" "@dhis2/prop-types" "^3.1.2" - "@dhis2/ui-constants" "8.14.7" - "@dhis2/ui-icons" "8.14.7" + "@dhis2/ui-constants" "8.15.0-alpha.3" + "@dhis2/ui-icons" "8.15.0-alpha.3" classnames "^2.3.1" prop-types "^15.7.2" -"@dhis2-ui/modal@8.14.7": - version "8.14.7" - resolved "https://registry.yarnpkg.com/@dhis2-ui/modal/-/modal-8.14.7.tgz#028dfd3a1f8e740f4d47a0ba6baec50184da05d4" - integrity sha512-+wKDtbNWTKlcvPFmtqUn4JI/4swiLIrLS37ZL1RbK11hRfyMH73YPDnRzxp+9quYuhkjP9VW/XCnJhwemWik6g== +"@dhis2-ui/modal@8.15.0-alpha.3": + version "8.15.0-alpha.3" + resolved "https://registry.yarnpkg.com/@dhis2-ui/modal/-/modal-8.15.0-alpha.3.tgz#c7e90bfb70af78286b9fd674f09992b58c7a80ad" + integrity sha512-4RVRARhLYGJfpO2upQ9Kdvv1VkPNuSfsCdUyGteQaNvzZudOSojfkJd/Mbs+FsPgnN74AH937Roo0MhX/PXTZw== dependencies: - "@dhis2-ui/card" "8.14.7" - "@dhis2-ui/center" "8.14.7" - "@dhis2-ui/layer" "8.14.7" - "@dhis2-ui/portal" "8.14.7" + "@dhis2-ui/card" "8.15.0-alpha.3" + "@dhis2-ui/center" "8.15.0-alpha.3" + "@dhis2-ui/layer" "8.15.0-alpha.3" + "@dhis2-ui/portal" "8.15.0-alpha.3" "@dhis2/prop-types" "^3.1.2" - "@dhis2/ui-constants" "8.14.7" - "@dhis2/ui-icons" "8.14.7" + "@dhis2/ui-constants" "8.15.0-alpha.3" + "@dhis2/ui-icons" "8.15.0-alpha.3" classnames "^2.3.1" prop-types "^15.7.2" -"@dhis2-ui/node@8.14.7": - version "8.14.7" - resolved "https://registry.yarnpkg.com/@dhis2-ui/node/-/node-8.14.7.tgz#90289ffb9ddc649bbc9702bf76d7eaab764a6885" - integrity sha512-l15xtf7F3khUxaMOjD4zGvc1SfWLY676gct0JzZRFNyTxkbe/SJ2N69j7b08jg5rNjfw693S5cgQSJSezMVyAQ== +"@dhis2-ui/node@8.15.0-alpha.3": + version "8.15.0-alpha.3" + resolved "https://registry.yarnpkg.com/@dhis2-ui/node/-/node-8.15.0-alpha.3.tgz#ca73a977c8969241e0074f02761bffdc41429d91" + integrity sha512-kee2hq0e1g21uRI0LAbfbV3pyRbVMm1/X5XwSnmc7fSsNg8c1FkRsgBlQLYk7j5+rfZ5+vASkjfvdnyXERJZKg== dependencies: - "@dhis2-ui/loader" "8.14.7" + "@dhis2-ui/loader" "8.15.0-alpha.3" "@dhis2/prop-types" "^3.1.2" - "@dhis2/ui-constants" "8.14.7" + "@dhis2/ui-constants" "8.15.0-alpha.3" classnames "^2.3.1" prop-types "^15.7.2" -"@dhis2-ui/notice-box@8.14.7": - version "8.14.7" - resolved "https://registry.yarnpkg.com/@dhis2-ui/notice-box/-/notice-box-8.14.7.tgz#deed5e78870e38774a472891a78b3933cbc72bf4" - integrity sha512-RiDlj9ah5eF2Gx3F+ozovsY0MeDVNd+qMP3NL16WhecN241gbfMGsERQB4JhV6wTPWkIKvJaepjlJfab0485pg== +"@dhis2-ui/notice-box@8.15.0-alpha.3": + version "8.15.0-alpha.3" + resolved "https://registry.yarnpkg.com/@dhis2-ui/notice-box/-/notice-box-8.15.0-alpha.3.tgz#bb46a17a59fddec2e0c494ca8a4a56b344dcad42" + integrity sha512-hzbWvmXReYel4z5/FiwqN/iEta51BHDHHosCl29UmR/FVMEZ5oM3tHWtB8YpDO+FCcJ6uKgU7zlv5xeHfYkg1A== dependencies: "@dhis2/prop-types" "^3.1.2" - "@dhis2/ui-constants" "8.14.7" - "@dhis2/ui-icons" "8.14.7" + "@dhis2/ui-constants" "8.15.0-alpha.3" + "@dhis2/ui-icons" "8.15.0-alpha.3" classnames "^2.3.1" prop-types "^15.7.2" -"@dhis2-ui/organisation-unit-tree@8.14.7": - version "8.14.7" - resolved "https://registry.yarnpkg.com/@dhis2-ui/organisation-unit-tree/-/organisation-unit-tree-8.14.7.tgz#49c078222c59b1b9c10129afc0bc5075bb58bbc8" - integrity sha512-1bYJ8nGQAz7uNqeoa2UIZ5yb6IlXTj33yua7OVeKKOczHItqM713wYMM64tEtHE8+fbzlfs78BbbSNmJMMw0iQ== +"@dhis2-ui/organisation-unit-tree@8.15.0-alpha.3": + version "8.15.0-alpha.3" + resolved "https://registry.yarnpkg.com/@dhis2-ui/organisation-unit-tree/-/organisation-unit-tree-8.15.0-alpha.3.tgz#d8821710498992a5e39b3238dff1b80a265a0dd9" + integrity sha512-1WyzHMxFZ+amK102xhFZf7QBoIIL+d9wmVS3e49uaqSMOOG98zRAiBBpaKLz3aKzoHJJEyPBouB6QSSndPQmBA== dependencies: - "@dhis2-ui/checkbox" "8.14.7" - "@dhis2-ui/loader" "8.14.7" - "@dhis2-ui/node" "8.14.7" + "@dhis2-ui/checkbox" "8.15.0-alpha.3" + "@dhis2-ui/loader" "8.15.0-alpha.3" + "@dhis2-ui/node" "8.15.0-alpha.3" "@dhis2/prop-types" "^3.1.2" - "@dhis2/ui-constants" "8.14.7" + "@dhis2/ui-constants" "8.15.0-alpha.3" classnames "^2.3.1" prop-types "^15.7.2" -"@dhis2-ui/pagination@8.14.7": - version "8.14.7" - resolved "https://registry.yarnpkg.com/@dhis2-ui/pagination/-/pagination-8.14.7.tgz#8f4a47cd6010e7860f95829155f3587e21d69dcc" - integrity sha512-B+vauERt11RIIeA8OBBfVKlO1/5/irZY6PNSG50FfebybeRnqOzKg5ghQFQ3tuO5bAE6G7wbeuuxTSsnstpYYA== +"@dhis2-ui/pagination@8.15.0-alpha.3": + version "8.15.0-alpha.3" + resolved "https://registry.yarnpkg.com/@dhis2-ui/pagination/-/pagination-8.15.0-alpha.3.tgz#1231768348f4ffcc12a843a09ccd916e10e3f865" + integrity sha512-3GXUq7CzQd5WEI2CpKIAa8kTKEy+FXMtSDvj7JkQrSPGULIji/9zOFN4PI+ofi2EbrR5vselI3ZfbY+t/+cMoQ== dependencies: - "@dhis2-ui/button" "8.14.7" - "@dhis2-ui/select" "8.14.7" + "@dhis2-ui/button" "8.15.0-alpha.3" + "@dhis2-ui/select" "8.15.0-alpha.3" "@dhis2/prop-types" "^3.1.2" - "@dhis2/ui-constants" "8.14.7" - "@dhis2/ui-icons" "8.14.7" + "@dhis2/ui-constants" "8.15.0-alpha.3" + "@dhis2/ui-icons" "8.15.0-alpha.3" classnames "^2.3.1" prop-types "^15.7.2" -"@dhis2-ui/popover@8.14.7": - version "8.14.7" - resolved "https://registry.yarnpkg.com/@dhis2-ui/popover/-/popover-8.14.7.tgz#373e66dbf780ccfeedf5b5b2e2de269bb98dc940" - integrity sha512-ajr0SEEOMDwCt+5FpV9Bxx3vRf6EGZbWtf7sfsBBrsEeCXGJtHT/qSNhy+R0LDEigCYbv0kCOnV5sAAnrDScMQ== +"@dhis2-ui/popover@8.15.0-alpha.3": + version "8.15.0-alpha.3" + resolved "https://registry.yarnpkg.com/@dhis2-ui/popover/-/popover-8.15.0-alpha.3.tgz#b5f7add9f977f758d016b9751146ebf1e2da22d5" + integrity sha512-nFk7fe3UM5CCG5EtizjrfPC8CzSVc26//8pIVVM34I/xo6sfMTU9r1a0QHYH2oz90wuvHTST4JMI8auPaumC6A== dependencies: - "@dhis2-ui/layer" "8.14.7" - "@dhis2-ui/popper" "8.14.7" + "@dhis2-ui/layer" "8.15.0-alpha.3" + "@dhis2-ui/popper" "8.15.0-alpha.3" "@dhis2/prop-types" "^3.1.2" - "@dhis2/ui-constants" "8.14.7" + "@dhis2/ui-constants" "8.15.0-alpha.3" classnames "^2.3.1" prop-types "^15.7.2" -"@dhis2-ui/popper@8.14.7": - version "8.14.7" - resolved "https://registry.yarnpkg.com/@dhis2-ui/popper/-/popper-8.14.7.tgz#0101fa266f867f8db3986d07c27b62e000f3e766" - integrity sha512-5A2+Db2G5lIzWua+op5AHhawO6EcAqO7O9GRTB/YIyxknwtUpg+7EGYVpifjOCM4Did5HINP9bGUZvvQjP+IfQ== +"@dhis2-ui/popper@8.15.0-alpha.3": + version "8.15.0-alpha.3" + resolved "https://registry.yarnpkg.com/@dhis2-ui/popper/-/popper-8.15.0-alpha.3.tgz#cc9f96b037059031ae0fba5e8bfe204908899667" + integrity sha512-LUvswFITFUwf3gvknsJq+6PTmSnO6rR69Et5aSILs1nR3Alz+yCpFZxESJzZbLhZ4YdWpZvxf6vrkebquifXWw== dependencies: "@dhis2/prop-types" "^3.1.2" - "@dhis2/ui-constants" "8.14.7" + "@dhis2/ui-constants" "8.15.0-alpha.3" "@popperjs/core" "^2.10.1" classnames "^2.3.1" prop-types "^15.7.2" react-popper "^2.2.5" resize-observer-polyfill "^1.5.1" -"@dhis2-ui/portal@8.14.7": - version "8.14.7" - resolved "https://registry.yarnpkg.com/@dhis2-ui/portal/-/portal-8.14.7.tgz#ba050e3b0bd3c4702aa403a405e4b27e750d88c8" - integrity sha512-Xx9qwF1altpHlwLh0+CXTbaptw/4xVJjdj5ZwMKT1oiqyVLmWIsdYD6r8dK4undsikZOVT9bwYhCvYP6XoOusA== +"@dhis2-ui/portal@8.15.0-alpha.3": + version "8.15.0-alpha.3" + resolved "https://registry.yarnpkg.com/@dhis2-ui/portal/-/portal-8.15.0-alpha.3.tgz#09dda02b21dc64fa9546b88f008dc59cf8fc45eb" + integrity sha512-PES0DSPVSsf4dy/HEGfUB9QIuYJQ9kkXh894gEq0yE93ORweDr6Q/NFKNfZNUOXMk4lo6W4rEdOQHkhwcH6bsQ== dependencies: classnames "^2.3.1" prop-types "^15.7.2" -"@dhis2-ui/radio@8.14.7": - version "8.14.7" - resolved "https://registry.yarnpkg.com/@dhis2-ui/radio/-/radio-8.14.7.tgz#213cc85b1b7a9244f0c216318ae53d24567a9901" - integrity sha512-gD7ruV2pyhmlt3P/TRKmMAJzlcsZ9XRpwK0WmSoGzX8FKC41nWDq9A4ENxYrsvWdYKDpmsy00urfwXEHnpnNnA== +"@dhis2-ui/radio@8.15.0-alpha.3": + version "8.15.0-alpha.3" + resolved "https://registry.yarnpkg.com/@dhis2-ui/radio/-/radio-8.15.0-alpha.3.tgz#6eccd1980cc5c9c3641211524cec942198ced248" + integrity sha512-bcMAd8MmFiD2CFvHCOQbTqG2DzPEne4xf521RWTc9OX4dHSMuPNeZ9+PWIjBl49ERKGtkuhWfmimO7FdfGRoqQ== dependencies: "@dhis2/prop-types" "^3.1.2" - "@dhis2/ui-constants" "8.14.7" + "@dhis2/ui-constants" "8.15.0-alpha.3" classnames "^2.3.1" prop-types "^15.7.2" -"@dhis2-ui/required@8.14.7": - version "8.14.7" - resolved "https://registry.yarnpkg.com/@dhis2-ui/required/-/required-8.14.7.tgz#16edd4b38ddee35680753a908e72484c090e4ee2" - integrity sha512-/Kx8nHcmsNUNpgaFJOSAxCn9GrUPbsG3cmda/TiBT1sBq26LjNmoPU8x3YZxI1tNSOlPVo9lfuZxVX5NH1+UwQ== +"@dhis2-ui/required@8.15.0-alpha.3": + version "8.15.0-alpha.3" + resolved "https://registry.yarnpkg.com/@dhis2-ui/required/-/required-8.15.0-alpha.3.tgz#e790d9bfd4593b1d9c025d78f101227ced5db5ae" + integrity sha512-nQnbYq8s3BtQ1GmdpaErCOWk2kFBh0E9C70m9fg8lUyfSK84a+4PdmxW2ITJNWi6lTwjldEB7wtZePFE3jK4VQ== dependencies: "@dhis2/prop-types" "^3.1.2" - "@dhis2/ui-constants" "8.14.7" + "@dhis2/ui-constants" "8.15.0-alpha.3" classnames "^2.3.1" prop-types "^15.7.2" -"@dhis2-ui/segmented-control@8.14.7": - version "8.14.7" - resolved "https://registry.yarnpkg.com/@dhis2-ui/segmented-control/-/segmented-control-8.14.7.tgz#a80d0fb730a9f7c60b8939960216fd273da5c9c9" - integrity sha512-rAoAtlAjChT6pj8zkI6kpJtB9vXheQOJPSKekinZWJNakdllZWfj90rtwg2EP8FCQEI7AKJXk8vFoAz6N+CKxg== +"@dhis2-ui/segmented-control@8.15.0-alpha.3": + version "8.15.0-alpha.3" + resolved "https://registry.yarnpkg.com/@dhis2-ui/segmented-control/-/segmented-control-8.15.0-alpha.3.tgz#78b0376dda51a28ab9e06648f631f815ad379914" + integrity sha512-JkCZNLY2kVNAjVYI6LGtXwzfehLvx/EEiiwDAP2tirSSlO77IL7m66fVUi01fQ0teGnkH0PDpQzRdYk8bSevgg== dependencies: "@dhis2/prop-types" "^3.1.2" - "@dhis2/ui-constants" "8.14.7" + "@dhis2/ui-constants" "8.15.0-alpha.3" classnames "^2.3.1" prop-types "^15.7.2" -"@dhis2-ui/select@8.14.7": - version "8.14.7" - resolved "https://registry.yarnpkg.com/@dhis2-ui/select/-/select-8.14.7.tgz#7a1b3e188715b8d1216e2ba7a9ba623db20b81a9" - integrity sha512-gTnszx+6Bob+zNHIwyqMDIC/AXgYI/CCIPFdZ0UL0Bu1fz16aOEEe21iqVws0ebuiIcjVFNviho+LnBQAEbMxw== - dependencies: - "@dhis2-ui/box" "8.14.7" - "@dhis2-ui/button" "8.14.7" - "@dhis2-ui/card" "8.14.7" - "@dhis2-ui/checkbox" "8.14.7" - "@dhis2-ui/chip" "8.14.7" - "@dhis2-ui/field" "8.14.7" - "@dhis2-ui/input" "8.14.7" - "@dhis2-ui/layer" "8.14.7" - "@dhis2-ui/loader" "8.14.7" - "@dhis2-ui/popper" "8.14.7" - "@dhis2-ui/status-icon" "8.14.7" +"@dhis2-ui/select@8.15.0-alpha.3": + version "8.15.0-alpha.3" + resolved "https://registry.yarnpkg.com/@dhis2-ui/select/-/select-8.15.0-alpha.3.tgz#70fa2febb146beeccf1d87af727bf9cc26362ebf" + integrity sha512-qYrIZ61ml1dLhvkEFdX8G4cYy6dMdSLD79jgzyLu/+Z3+Wo+JIs6ilF7NmpfG9JI9ABF2jz5ORWDzOfLuJu2zw== + dependencies: + "@dhis2-ui/box" "8.15.0-alpha.3" + "@dhis2-ui/button" "8.15.0-alpha.3" + "@dhis2-ui/card" "8.15.0-alpha.3" + "@dhis2-ui/checkbox" "8.15.0-alpha.3" + "@dhis2-ui/chip" "8.15.0-alpha.3" + "@dhis2-ui/field" "8.15.0-alpha.3" + "@dhis2-ui/input" "8.15.0-alpha.3" + "@dhis2-ui/layer" "8.15.0-alpha.3" + "@dhis2-ui/loader" "8.15.0-alpha.3" + "@dhis2-ui/popper" "8.15.0-alpha.3" + "@dhis2-ui/status-icon" "8.15.0-alpha.3" "@dhis2/prop-types" "^3.1.2" - "@dhis2/ui-constants" "8.14.7" - "@dhis2/ui-icons" "8.14.7" + "@dhis2/ui-constants" "8.15.0-alpha.3" + "@dhis2/ui-icons" "8.15.0-alpha.3" classnames "^2.3.1" prop-types "^15.7.2" -"@dhis2-ui/selector-bar@8.14.7": - version "8.14.7" - resolved "https://registry.yarnpkg.com/@dhis2-ui/selector-bar/-/selector-bar-8.14.7.tgz#5ff9d34962f594ea9c7db1fea6d52d46aaba0c8e" - integrity sha512-HAhKYJIInnEu0DE2J+FZDJqSUMfEJSgA5HvKnbVSnOGnOV/ZCIUCo/2BruS7jCWccDJ2/av/UCPYpdqkvYu5fA== - dependencies: - "@dhis2-ui/button" "8.14.7" - "@dhis2-ui/card" "8.14.7" - "@dhis2-ui/layer" "8.14.7" - "@dhis2-ui/popper" "8.14.7" - "@dhis2/ui-constants" "8.14.7" - "@dhis2/ui-icons" "8.14.7" +"@dhis2-ui/selector-bar@8.15.0-alpha.3": + version "8.15.0-alpha.3" + resolved "https://registry.yarnpkg.com/@dhis2-ui/selector-bar/-/selector-bar-8.15.0-alpha.3.tgz#4bb1c1a132ba5e0b0893b26e878beff4453d9bcf" + integrity sha512-gkgbcDBXNO8P5A03YZNK3uG5ZTzJ/77obgj+39eszyksSOTG5gURxa/agdQjxom/K/Y8/9h26auXKfywGnwegA== + dependencies: + "@dhis2-ui/button" "8.15.0-alpha.3" + "@dhis2-ui/card" "8.15.0-alpha.3" + "@dhis2-ui/layer" "8.15.0-alpha.3" + "@dhis2-ui/popper" "8.15.0-alpha.3" + "@dhis2/ui-constants" "8.15.0-alpha.3" + "@dhis2/ui-icons" "8.15.0-alpha.3" "@testing-library/react" "^12.1.2" classnames "^2.3.1" prop-types "^15.7.2" -"@dhis2-ui/sharing-dialog@8.14.7": - version "8.14.7" - resolved "https://registry.yarnpkg.com/@dhis2-ui/sharing-dialog/-/sharing-dialog-8.14.7.tgz#f0423418f5bb9221c64e078a226991defbed08b2" - integrity sha512-vSZxdKWulijNXaEr2EwqhBamKV/XvoswODlk4MZLas6zCPjenYswH5eSpTjYr9/gN3HOgQB2T3umS4cfWS7UWg== - dependencies: - "@dhis2-ui/box" "8.14.7" - "@dhis2-ui/button" "8.14.7" - "@dhis2-ui/card" "8.14.7" - "@dhis2-ui/divider" "8.14.7" - "@dhis2-ui/input" "8.14.7" - "@dhis2-ui/layer" "8.14.7" - "@dhis2-ui/menu" "8.14.7" - "@dhis2-ui/modal" "8.14.7" - "@dhis2-ui/notice-box" "8.14.7" - "@dhis2-ui/popper" "8.14.7" - "@dhis2-ui/select" "8.14.7" - "@dhis2-ui/tab" "8.14.7" - "@dhis2-ui/tooltip" "8.14.7" - "@dhis2-ui/user-avatar" "8.14.7" +"@dhis2-ui/sharing-dialog@8.15.0-alpha.3": + version "8.15.0-alpha.3" + resolved "https://registry.yarnpkg.com/@dhis2-ui/sharing-dialog/-/sharing-dialog-8.15.0-alpha.3.tgz#41bf2ac3e7055f796f60a61d3b59bf45ebb234bd" + integrity sha512-1foTbRy9RU2771S6gW6riYqD8q8k38tARBBY+LrGgrkhn3SpCdJnLcZ3wWwHY055HQWqEnJKm8gV9yHJCF8B9g== + dependencies: + "@dhis2-ui/box" "8.15.0-alpha.3" + "@dhis2-ui/button" "8.15.0-alpha.3" + "@dhis2-ui/card" "8.15.0-alpha.3" + "@dhis2-ui/divider" "8.15.0-alpha.3" + "@dhis2-ui/input" "8.15.0-alpha.3" + "@dhis2-ui/layer" "8.15.0-alpha.3" + "@dhis2-ui/menu" "8.15.0-alpha.3" + "@dhis2-ui/modal" "8.15.0-alpha.3" + "@dhis2-ui/notice-box" "8.15.0-alpha.3" + "@dhis2-ui/popper" "8.15.0-alpha.3" + "@dhis2-ui/select" "8.15.0-alpha.3" + "@dhis2-ui/tab" "8.15.0-alpha.3" + "@dhis2-ui/tooltip" "8.15.0-alpha.3" + "@dhis2-ui/user-avatar" "8.15.0-alpha.3" "@dhis2/prop-types" "^3.1.2" - "@dhis2/ui-constants" "8.14.7" - "@dhis2/ui-icons" "8.14.7" + "@dhis2/ui-constants" "8.15.0-alpha.3" + "@dhis2/ui-icons" "8.15.0-alpha.3" "@react-hook/size" "^2.1.2" classnames "^2.3.1" prop-types "^15.7.2" -"@dhis2-ui/status-icon@8.14.7": - version "8.14.7" - resolved "https://registry.yarnpkg.com/@dhis2-ui/status-icon/-/status-icon-8.14.7.tgz#5ab427ff1d21e7ffdaaa943b4d12aaddcc0ab63a" - integrity sha512-Eyvi2pEW/zP1XU6lvRB55c6cY/7aJZ/ZiXjvxdnhlqS0cE+FZpAR9p9dEiWEYqA5bRjrhSIb80Fss67LDAtIPw== +"@dhis2-ui/status-icon@8.15.0-alpha.3": + version "8.15.0-alpha.3" + resolved "https://registry.yarnpkg.com/@dhis2-ui/status-icon/-/status-icon-8.15.0-alpha.3.tgz#bde4355183c95436fafcebec22693d864428a314" + integrity sha512-aw3jd4qyQeRZsT6cSplu49PrWPOMjGHeauzN898JuKyGWlQrjPRn61un0ALQSxDgRRoASFzGDUw5UN9FzHIK4Q== dependencies: - "@dhis2-ui/loader" "8.14.7" + "@dhis2-ui/loader" "8.15.0-alpha.3" "@dhis2/prop-types" "^3.1.2" - "@dhis2/ui-constants" "8.14.7" - "@dhis2/ui-icons" "8.14.7" + "@dhis2/ui-constants" "8.15.0-alpha.3" + "@dhis2/ui-icons" "8.15.0-alpha.3" classnames "^2.3.1" prop-types "^15.7.2" -"@dhis2-ui/switch@8.14.7": - version "8.14.7" - resolved "https://registry.yarnpkg.com/@dhis2-ui/switch/-/switch-8.14.7.tgz#28b58f266f7c26f42d2348fcbfbc7f6e113ec9f4" - integrity sha512-DIa9qOcrfnnf1bdk+JBsbqdIH4eg6cgVRBeCbhVny0PCoRM6a3UPIiSSqoe+H1oODouE3jN05u3Ecj/4gTGsaA== +"@dhis2-ui/switch@8.15.0-alpha.3": + version "8.15.0-alpha.3" + resolved "https://registry.yarnpkg.com/@dhis2-ui/switch/-/switch-8.15.0-alpha.3.tgz#af0b9f17183bf310588f728cb54d2cd4c09e565e" + integrity sha512-SgRT/ETs6vwFNIOlXJxPRKkF2/YUNel1H5NbDKZiSxJJso4g+x0AB9T86Fgfa9ffexda56sSLwWLrIcyniFDdg== dependencies: - "@dhis2-ui/field" "8.14.7" - "@dhis2-ui/required" "8.14.7" + "@dhis2-ui/field" "8.15.0-alpha.3" + "@dhis2-ui/required" "8.15.0-alpha.3" "@dhis2/prop-types" "^3.1.2" - "@dhis2/ui-constants" "8.14.7" + "@dhis2/ui-constants" "8.15.0-alpha.3" classnames "^2.3.1" prop-types "^15.7.2" -"@dhis2-ui/tab@8.14.7": - version "8.14.7" - resolved "https://registry.yarnpkg.com/@dhis2-ui/tab/-/tab-8.14.7.tgz#6cc169fb97a2f227b0880e7f4c0cb5fe7b7eb20f" - integrity sha512-sdX9DkcvjyYfmjI5iQ2Xe5LmvDH/PBcU0geZzbqhPs0vpkbiSVcXRnQ/daOI4TfaU7Ok9Bwlqb8mgFO0FJen0A== +"@dhis2-ui/tab@8.15.0-alpha.3": + version "8.15.0-alpha.3" + resolved "https://registry.yarnpkg.com/@dhis2-ui/tab/-/tab-8.15.0-alpha.3.tgz#7e92f8ec2796e2c97e862b67eb9692d4f07dab3e" + integrity sha512-qzv+Q0sOtGkZ/jZjrSfll1Gf936yTwM5uaGnMgBvzyzJDJMo2erbuaT4ZPlzyZrblAX75d9FB6OaaevNLbkLLA== dependencies: "@dhis2/prop-types" "^3.1.2" - "@dhis2/ui-constants" "8.14.7" - "@dhis2/ui-icons" "8.14.7" + "@dhis2/ui-constants" "8.15.0-alpha.3" + "@dhis2/ui-icons" "8.15.0-alpha.3" classnames "^2.3.1" prop-types "^15.7.2" -"@dhis2-ui/table@8.14.7": - version "8.14.7" - resolved "https://registry.yarnpkg.com/@dhis2-ui/table/-/table-8.14.7.tgz#769088964d52b2df952688f969f4143c782601df" - integrity sha512-6ThFeTLwE0HMBL+wbCYaKsSCixiYnLbyCOQrhNp9MStKEFWJHxEn+XuYmAusSczzad3tzMLrhjJIGvInQmPPxg== +"@dhis2-ui/table@8.15.0-alpha.3": + version "8.15.0-alpha.3" + resolved "https://registry.yarnpkg.com/@dhis2-ui/table/-/table-8.15.0-alpha.3.tgz#ecbe2432fe7170ca74be76ecfb2a7e7882f16de7" + integrity sha512-nJmH9elLeav1x7gCAK91U2n7kLHne0qOCGmFb9aiOTnEqRhQYG4X21yAWfFMEJm6VWEyqeHvEZPwT6aIBLsXZg== dependencies: "@dhis2/prop-types" "^3.1.2" - "@dhis2/ui-constants" "8.14.7" - "@dhis2/ui-icons" "8.14.7" + "@dhis2/ui-constants" "8.15.0-alpha.3" + "@dhis2/ui-icons" "8.15.0-alpha.3" classnames "^2.3.1" prop-types "^15.7.2" -"@dhis2-ui/tag@8.14.7": - version "8.14.7" - resolved "https://registry.yarnpkg.com/@dhis2-ui/tag/-/tag-8.14.7.tgz#203d7c6d916791e6691ef84b8bdff7f78a22c61d" - integrity sha512-9hhEgPwNQplvXRXB4Fo8eaDjWfCKTTF16LfTtZ7cK4x+/AWeTCjVYNux9pqhOCMWcEBS1BO0cXj4bQHPIawsmg== +"@dhis2-ui/tag@8.15.0-alpha.3": + version "8.15.0-alpha.3" + resolved "https://registry.yarnpkg.com/@dhis2-ui/tag/-/tag-8.15.0-alpha.3.tgz#da4a64759f9bc890b91f3747282c213feb07ef32" + integrity sha512-GD7a5CkiWmSxmCAGDKfN/EHqJ3HB+QKq0JfEsvxM7zdriRAwsnj7lyVhq1M27kZkd7wHIvQ74s/SpWke7xcsmg== dependencies: "@dhis2/prop-types" "^3.1.2" - "@dhis2/ui-constants" "8.14.7" + "@dhis2/ui-constants" "8.15.0-alpha.3" classnames "^2.3.1" prop-types "^15.7.2" -"@dhis2-ui/text-area@8.14.7": - version "8.14.7" - resolved "https://registry.yarnpkg.com/@dhis2-ui/text-area/-/text-area-8.14.7.tgz#3814d749713b4b83a1c7b9ba3738244f4b35ae89" - integrity sha512-FV13GtmpizL+PrxwpFRdBL1hIz6Y7X5GCtTrqWSZblg8wedetsC17AUzal3YjC4O7eNKOLq6Qhvawo+fyJ2O8w== +"@dhis2-ui/text-area@8.15.0-alpha.3": + version "8.15.0-alpha.3" + resolved "https://registry.yarnpkg.com/@dhis2-ui/text-area/-/text-area-8.15.0-alpha.3.tgz#82257ce06b00abbf7041817a0ab30e8275f52918" + integrity sha512-2ixjv72nGME5ew2oaVw2pOBY0OhbMDodbrvtD8+xBkOZXvhxmqVlt9Zybfwro4DWZexp44lZuT4jQ4YlBISkXw== dependencies: - "@dhis2-ui/box" "8.14.7" - "@dhis2-ui/field" "8.14.7" - "@dhis2-ui/loader" "8.14.7" - "@dhis2-ui/status-icon" "8.14.7" + "@dhis2-ui/box" "8.15.0-alpha.3" + "@dhis2-ui/field" "8.15.0-alpha.3" + "@dhis2-ui/loader" "8.15.0-alpha.3" + "@dhis2-ui/status-icon" "8.15.0-alpha.3" "@dhis2/prop-types" "^3.1.2" - "@dhis2/ui-constants" "8.14.7" - "@dhis2/ui-icons" "8.14.7" + "@dhis2/ui-constants" "8.15.0-alpha.3" + "@dhis2/ui-icons" "8.15.0-alpha.3" classnames "^2.3.1" prop-types "^15.7.2" -"@dhis2-ui/tooltip@8.14.7": - version "8.14.7" - resolved "https://registry.yarnpkg.com/@dhis2-ui/tooltip/-/tooltip-8.14.7.tgz#636037a67d0882eb12d089553a39f865fe914be7" - integrity sha512-OuBtZdFugjV78tzAqBlZfDpSViiUpFzHFFV5dwEgIXk1QgiDTVEpCHyZDUCXeBy5WWu+ZoKWwEEl5N4CHuzfow== +"@dhis2-ui/tooltip@8.15.0-alpha.3": + version "8.15.0-alpha.3" + resolved "https://registry.yarnpkg.com/@dhis2-ui/tooltip/-/tooltip-8.15.0-alpha.3.tgz#3d5c23378154f254668f542eccd09777d8e235b6" + integrity sha512-HrNmcpyrZYDFJoToPTDIIUDEQ9HW6pzEfYAXQ1unAOhaiox34boqY0XW733Zzhwduc68TkGDhmvMbW6M0azgCA== dependencies: - "@dhis2-ui/popper" "8.14.7" - "@dhis2-ui/portal" "8.14.7" + "@dhis2-ui/popper" "8.15.0-alpha.3" + "@dhis2-ui/portal" "8.15.0-alpha.3" "@dhis2/prop-types" "^3.1.2" - "@dhis2/ui-constants" "8.14.7" + "@dhis2/ui-constants" "8.15.0-alpha.3" classnames "^2.3.1" prop-types "^15.7.2" -"@dhis2-ui/transfer@8.14.7": - version "8.14.7" - resolved "https://registry.yarnpkg.com/@dhis2-ui/transfer/-/transfer-8.14.7.tgz#a098d178d4e0b880e4b9edddcada55b67168c3f5" - integrity sha512-6JJJgXNUDjDQ4KzupXT6TT9JlEkZUYZ2+i9/eJ10gLwqnq0MsbzigGVfcpOlO+W+SQyzLLsguxGudk8aBAf7GA== +"@dhis2-ui/transfer@8.15.0-alpha.3": + version "8.15.0-alpha.3" + resolved "https://registry.yarnpkg.com/@dhis2-ui/transfer/-/transfer-8.15.0-alpha.3.tgz#dca1f12fcda2227f2d07f0123fc7f05ba927abc9" + integrity sha512-cw9vLPJjgP6Lse8ifu2Km/UAd4JwN9EI81R/X228iWlQnpi4s4EO/ponVXU+wplb2VkNEooKmACGdRBODfKPZQ== dependencies: - "@dhis2-ui/button" "8.14.7" - "@dhis2-ui/field" "8.14.7" - "@dhis2-ui/input" "8.14.7" - "@dhis2-ui/intersection-detector" "8.14.7" - "@dhis2-ui/loader" "8.14.7" + "@dhis2-ui/button" "8.15.0-alpha.3" + "@dhis2-ui/field" "8.15.0-alpha.3" + "@dhis2-ui/input" "8.15.0-alpha.3" + "@dhis2-ui/intersection-detector" "8.15.0-alpha.3" + "@dhis2-ui/loader" "8.15.0-alpha.3" "@dhis2/prop-types" "^3.1.2" - "@dhis2/ui-constants" "8.14.7" + "@dhis2/ui-constants" "8.15.0-alpha.3" classnames "^2.3.1" prop-types "^15.7.2" -"@dhis2-ui/user-avatar@8.14.7": - version "8.14.7" - resolved "https://registry.yarnpkg.com/@dhis2-ui/user-avatar/-/user-avatar-8.14.7.tgz#3cd54d5261ee7f10cee99a347f9f1bce7803dccf" - integrity sha512-vsOVpMaq1kFzpyfkQzxW70ei7oqHEA1dirJXo3UeAzpt1MkZ945H4o3wLtga9oAhxqkILQef5WWMpTavuC+UuQ== +"@dhis2-ui/user-avatar@8.15.0-alpha.3": + version "8.15.0-alpha.3" + resolved "https://registry.yarnpkg.com/@dhis2-ui/user-avatar/-/user-avatar-8.15.0-alpha.3.tgz#5f0b045a2ac1b36c554b1faeb4cab3a5e33fa62a" + integrity sha512-JKtOj5sG63iRhYyDv8wBJQyvU1MY2+UJ2NiFeRNo/zbi8THDgRSUOdak9qYg+u9brAkU6Nx9r7O9jdBWhg8Ysg== dependencies: "@dhis2/prop-types" "^3.1.2" - "@dhis2/ui-constants" "8.14.7" + "@dhis2/ui-constants" "8.15.0-alpha.3" classnames "^2.3.1" prop-types "^15.7.2" @@ -2187,91 +2187,91 @@ workbox-routing "^6.1.5" workbox-strategies "^6.1.5" -"@dhis2/ui-constants@8.14.7": - version "8.14.7" - resolved "https://registry.yarnpkg.com/@dhis2/ui-constants/-/ui-constants-8.14.7.tgz#bfab11cc3c8dc990f7592089ba9577f7a7c60411" - integrity sha512-cQWQ49onb2AjL11NktOaSwxvFF4lge+QxNojNZS1b3RJDMQjHoHopl0tQhBnCSDnm3xF7XWxzI8QOTsF2ubdRA== +"@dhis2/ui-constants@8.15.0-alpha.3": + version "8.15.0-alpha.3" + resolved "https://registry.yarnpkg.com/@dhis2/ui-constants/-/ui-constants-8.15.0-alpha.3.tgz#32b7e6d3773ce9533b8c5d7cea15bf4543646bd8" + integrity sha512-S7NF5MBUc3hvlZwmirO/ZojAyAmlr0VFGo2LR4GqGzSyWqMOY/AkU/FlG6nnVzL67a/8dfgiKrVe6BAzKHD0Xg== dependencies: prop-types "^15.7.2" -"@dhis2/ui-forms@8.14.7": - version "8.14.7" - resolved "https://registry.yarnpkg.com/@dhis2/ui-forms/-/ui-forms-8.14.7.tgz#fa9c8640cda7cb5417e1e62013d5a745c4098e25" - integrity sha512-7T2jUxCN42ND1iZ6TabZTtfBzWsye5ByRcpm14pkY9h9HESfc/0Y81f5JPfnLf+3mAZjvVmxgTf2y0Rm+a1/Vw== - dependencies: - "@dhis2-ui/button" "8.14.7" - "@dhis2-ui/checkbox" "8.14.7" - "@dhis2-ui/field" "8.14.7" - "@dhis2-ui/file-input" "8.14.7" - "@dhis2-ui/input" "8.14.7" - "@dhis2-ui/radio" "8.14.7" - "@dhis2-ui/select" "8.14.7" - "@dhis2-ui/switch" "8.14.7" - "@dhis2-ui/text-area" "8.14.7" +"@dhis2/ui-forms@8.15.0-alpha.3": + version "8.15.0-alpha.3" + resolved "https://registry.yarnpkg.com/@dhis2/ui-forms/-/ui-forms-8.15.0-alpha.3.tgz#54e5efed91a144b375f4386c4119c82c7ac53cd8" + integrity sha512-abEpI+bcD61ubZTz7nrBi/hs2Clxe0j0ODDMFsXtnw4CXQl+2oJW1uegS4IwH2A61xh50kgrugdkyY89+Xy6wg== + dependencies: + "@dhis2-ui/button" "8.15.0-alpha.3" + "@dhis2-ui/checkbox" "8.15.0-alpha.3" + "@dhis2-ui/field" "8.15.0-alpha.3" + "@dhis2-ui/file-input" "8.15.0-alpha.3" + "@dhis2-ui/input" "8.15.0-alpha.3" + "@dhis2-ui/radio" "8.15.0-alpha.3" + "@dhis2-ui/select" "8.15.0-alpha.3" + "@dhis2-ui/switch" "8.15.0-alpha.3" + "@dhis2-ui/text-area" "8.15.0-alpha.3" "@dhis2/prop-types" "^3.1.2" classnames "^2.3.1" final-form "^4.20.2" prop-types "^15.7.2" react-final-form "^6.5.3" -"@dhis2/ui-icons@8.14.7": - version "8.14.7" - resolved "https://registry.yarnpkg.com/@dhis2/ui-icons/-/ui-icons-8.14.7.tgz#351d5890544a656c0838bba6979e9068ed051b97" - integrity sha512-fw8Ts0dWQO/178YWBkW8w2FXPBppiop/IyRKMYd7sVuaIGGwKmlvAQMb/+EyDa15qMMN+JdYLr4aJyoUji4Yyg== - -"@dhis2/ui@^8.12.3", "@dhis2/ui@^8.14.7": - version "8.14.7" - resolved "https://registry.yarnpkg.com/@dhis2/ui/-/ui-8.14.7.tgz#dcbfc5c5060341fe548820ece497ace6560ce00b" - integrity sha512-0bQTJe++Oz7ivLyQpVPUZJHxLSfrnVDmI7dWXIq25hmL5/PcCMiotYdymKmg4BkZQdRPi7F1C1x/HYXXyYUlsA== - dependencies: - "@dhis2-ui/alert" "8.14.7" - "@dhis2-ui/box" "8.14.7" - "@dhis2-ui/button" "8.14.7" - "@dhis2-ui/calendar" "8.14.7" - "@dhis2-ui/card" "8.14.7" - "@dhis2-ui/center" "8.14.7" - "@dhis2-ui/checkbox" "8.14.7" - "@dhis2-ui/chip" "8.14.7" - "@dhis2-ui/cover" "8.14.7" - "@dhis2-ui/css" "8.14.7" - "@dhis2-ui/divider" "8.14.7" - "@dhis2-ui/field" "8.14.7" - "@dhis2-ui/file-input" "8.14.7" - "@dhis2-ui/header-bar" "8.14.7" - "@dhis2-ui/help" "8.14.7" - "@dhis2-ui/input" "8.14.7" - "@dhis2-ui/intersection-detector" "8.14.7" - "@dhis2-ui/label" "8.14.7" - "@dhis2-ui/layer" "8.14.7" - "@dhis2-ui/legend" "8.14.7" - "@dhis2-ui/loader" "8.14.7" - "@dhis2-ui/logo" "8.14.7" - "@dhis2-ui/menu" "8.14.7" - "@dhis2-ui/modal" "8.14.7" - "@dhis2-ui/node" "8.14.7" - "@dhis2-ui/notice-box" "8.14.7" - "@dhis2-ui/organisation-unit-tree" "8.14.7" - "@dhis2-ui/pagination" "8.14.7" - "@dhis2-ui/popover" "8.14.7" - "@dhis2-ui/popper" "8.14.7" - "@dhis2-ui/portal" "8.14.7" - "@dhis2-ui/radio" "8.14.7" - "@dhis2-ui/required" "8.14.7" - "@dhis2-ui/segmented-control" "8.14.7" - "@dhis2-ui/select" "8.14.7" - "@dhis2-ui/selector-bar" "8.14.7" - "@dhis2-ui/sharing-dialog" "8.14.7" - "@dhis2-ui/switch" "8.14.7" - "@dhis2-ui/tab" "8.14.7" - "@dhis2-ui/table" "8.14.7" - "@dhis2-ui/tag" "8.14.7" - "@dhis2-ui/text-area" "8.14.7" - "@dhis2-ui/tooltip" "8.14.7" - "@dhis2-ui/transfer" "8.14.7" - "@dhis2-ui/user-avatar" "8.14.7" - "@dhis2/ui-constants" "8.14.7" - "@dhis2/ui-forms" "8.14.7" - "@dhis2/ui-icons" "8.14.7" +"@dhis2/ui-icons@8.15.0-alpha.3": + version "8.15.0-alpha.3" + resolved "https://registry.yarnpkg.com/@dhis2/ui-icons/-/ui-icons-8.15.0-alpha.3.tgz#8bf59f004c340bba8bd38e899b476b5e0d941777" + integrity sha512-NxN5W78u9UCP5YZjA/IiUt+jBAUzDb1mnk9edAfU5T8SEmmZnsoFKoMWtcgLL2Mx/Up+CN3wyTgEf55pD2Af2Q== + +"@dhis2/ui@8.15.0-alpha.3", "@dhis2/ui@^8.12.3", "@dhis2/ui@^8.15.0-alpha.3": + version "8.15.0-alpha.3" + resolved "https://registry.yarnpkg.com/@dhis2/ui/-/ui-8.15.0-alpha.3.tgz#6f70e05eba4a3de33376a3e1fd94618e8cd37fad" + integrity sha512-Ue6g8hT2ahoLhqoSgheREvjcWqzKgHa6TXQuq45Tp9Lyr7M6NfXTlrA8AqFyGACABQ7j4zmTAxtcmmpl8naKbQ== + dependencies: + "@dhis2-ui/alert" "8.15.0-alpha.3" + "@dhis2-ui/box" "8.15.0-alpha.3" + "@dhis2-ui/button" "8.15.0-alpha.3" + "@dhis2-ui/calendar" "8.15.0-alpha.3" + "@dhis2-ui/card" "8.15.0-alpha.3" + "@dhis2-ui/center" "8.15.0-alpha.3" + "@dhis2-ui/checkbox" "8.15.0-alpha.3" + "@dhis2-ui/chip" "8.15.0-alpha.3" + "@dhis2-ui/cover" "8.15.0-alpha.3" + "@dhis2-ui/css" "8.15.0-alpha.3" + "@dhis2-ui/divider" "8.15.0-alpha.3" + "@dhis2-ui/field" "8.15.0-alpha.3" + "@dhis2-ui/file-input" "8.15.0-alpha.3" + "@dhis2-ui/header-bar" "8.15.0-alpha.3" + "@dhis2-ui/help" "8.15.0-alpha.3" + "@dhis2-ui/input" "8.15.0-alpha.3" + "@dhis2-ui/intersection-detector" "8.15.0-alpha.3" + "@dhis2-ui/label" "8.15.0-alpha.3" + "@dhis2-ui/layer" "8.15.0-alpha.3" + "@dhis2-ui/legend" "8.15.0-alpha.3" + "@dhis2-ui/loader" "8.15.0-alpha.3" + "@dhis2-ui/logo" "8.15.0-alpha.3" + "@dhis2-ui/menu" "8.15.0-alpha.3" + "@dhis2-ui/modal" "8.15.0-alpha.3" + "@dhis2-ui/node" "8.15.0-alpha.3" + "@dhis2-ui/notice-box" "8.15.0-alpha.3" + "@dhis2-ui/organisation-unit-tree" "8.15.0-alpha.3" + "@dhis2-ui/pagination" "8.15.0-alpha.3" + "@dhis2-ui/popover" "8.15.0-alpha.3" + "@dhis2-ui/popper" "8.15.0-alpha.3" + "@dhis2-ui/portal" "8.15.0-alpha.3" + "@dhis2-ui/radio" "8.15.0-alpha.3" + "@dhis2-ui/required" "8.15.0-alpha.3" + "@dhis2-ui/segmented-control" "8.15.0-alpha.3" + "@dhis2-ui/select" "8.15.0-alpha.3" + "@dhis2-ui/selector-bar" "8.15.0-alpha.3" + "@dhis2-ui/sharing-dialog" "8.15.0-alpha.3" + "@dhis2-ui/switch" "8.15.0-alpha.3" + "@dhis2-ui/tab" "8.15.0-alpha.3" + "@dhis2-ui/table" "8.15.0-alpha.3" + "@dhis2-ui/tag" "8.15.0-alpha.3" + "@dhis2-ui/text-area" "8.15.0-alpha.3" + "@dhis2-ui/tooltip" "8.15.0-alpha.3" + "@dhis2-ui/transfer" "8.15.0-alpha.3" + "@dhis2-ui/user-avatar" "8.15.0-alpha.3" + "@dhis2/ui-constants" "8.15.0-alpha.3" + "@dhis2/ui-forms" "8.15.0-alpha.3" + "@dhis2/ui-icons" "8.15.0-alpha.3" prop-types "^15.7.2" "@eslint-community/eslint-utils@^4.2.0": From 2cc8801cc80b12586ad58f54e744f0bf0e177593 Mon Sep 17 00:00:00 2001 From: Birk Johansson Date: Wed, 22 Nov 2023 17:35:01 +0100 Subject: [PATCH 13/14] fix(attributes): fix deselecting attribute (#361) --- src/pages/dataElements/form/CustomAttributes.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/pages/dataElements/form/CustomAttributes.tsx b/src/pages/dataElements/form/CustomAttributes.tsx index 92539a68..4329151b 100644 --- a/src/pages/dataElements/form/CustomAttributes.tsx +++ b/src/pages/dataElements/form/CustomAttributes.tsx @@ -30,7 +30,7 @@ function CustomAttribute({ attribute, index }: CustomAttributeProps) { }) ) - if (required) { + if (!required) { options.unshift({ value: '', label: i18n.t('') }) } From 88e1a581db060287ef39fb96268a589cdb99b490 Mon Sep 17 00:00:00 2001 From: Birk Johansson Date: Thu, 23 Nov 2023 14:20:55 +0100 Subject: [PATCH 14/14] fix(form): fix editing style object (#362) * fix(form): fix editing style object * refactor(json-patch): refactor createJsonPathOperations, add tests * fix(test): add empty value in test * refactor(json-patch): align type names --- src/pages/dataElements/Edit.tsx | 2 +- .../edit/createJsonPatchOperations.spec.ts | 127 ++++++++++++++++++ .../edit/createJsonPatchOperations.ts | 71 ++++++---- 3 files changed, 173 insertions(+), 27 deletions(-) create mode 100644 src/pages/dataElements/edit/createJsonPatchOperations.spec.ts diff --git a/src/pages/dataElements/Edit.tsx b/src/pages/dataElements/Edit.tsx index 1051df0c..553b11d9 100644 --- a/src/pages/dataElements/Edit.tsx +++ b/src/pages/dataElements/Edit.tsx @@ -116,7 +116,7 @@ function usePatchDirtyFields() { const jsonPatchPayload = createJsonPatchOperations({ values, dirtyFields, - dataElement, + originalValue: dataElement, }) // We want the promise so we know when submitting is done. The promise diff --git a/src/pages/dataElements/edit/createJsonPatchOperations.spec.ts b/src/pages/dataElements/edit/createJsonPatchOperations.spec.ts new file mode 100644 index 00000000..9ab562d8 --- /dev/null +++ b/src/pages/dataElements/edit/createJsonPatchOperations.spec.ts @@ -0,0 +1,127 @@ +import { + sanitizeDirtyValueKeys, + createJsonPatchOperations, +} from './createJsonPatchOperations' + +describe('createJsonPatchOperations', () => { + describe('sanitizeDirtyValueKeys', () => { + it('should return the dirty values array as is', () => { + const actual = sanitizeDirtyValueKeys(['foo', 'bar']) + const expected = ['foo', 'bar'] + expect(actual).toEqual(expected) + }) + + it('should remove all attribute values changes and add a single "attributeValues"', () => { + const actual = sanitizeDirtyValueKeys([ + 'foo', + 'bar', + 'attributeValues[0].value', + 'attributeValues[1].value', + ]) + const expected = ['foo', 'bar', 'attributeValues'] + expect(actual).toEqual(expected) + }) + + it('should remove style.icon and style.color changes and add a single "style"', () => { + const actual = sanitizeDirtyValueKeys([ + 'foo', + 'bar', + 'style.color', + 'style.icon', + ]) + const expected = ['foo', 'bar', 'style'] + expect(actual).toEqual(expected) + }) + }) + + describe('createJsonPatchOperations', () => { + it('should return an empty array if no dirty fields', () => { + const actual = createJsonPatchOperations({ + dirtyFields: {}, + originalValue: { + id: 'foo', + attributeValues: [], + }, + values: { + attributeValues: [], + }, + }) + expect(actual).toEqual([]) + }) + + it('should return a json-patch payload for a single field', () => { + const actual = createJsonPatchOperations({ + dirtyFields: { + name: true, + }, + originalValue: { + id: 'foo', + name: 'bar', + attributeValues: [], + }, + values: { + name: 'baz', + attributeValues: [], + }, + }) + const expected = [ + { + op: 'replace', + path: '/name', + value: 'baz', + }, + ] + expect(actual).toEqual(expected) + }) + it('should return a json-patch payload with add if value does not exist in originalValue', () => { + const actual = createJsonPatchOperations({ + dirtyFields: { + name: true, + }, + originalValue: { + id: 'foo', + attributeValues: [], + }, + values: { + name: 'baz', + attributeValues: [], + }, + }) + const expected = [ + { + op: 'add', + path: '/name', + value: 'baz', + }, + ] + expect(actual).toEqual(expected) + }) + + it('should handle attributeValues', () => { + const actual = createJsonPatchOperations({ + dirtyFields: { + attributeValues: true, + }, + originalValue: { + id: 'foo', + name: 'bar', + attributeValues: [{ value: '', attribute: 'foo' }], + }, + values: { + name: 'baz', + attributeValues: [ + { value: 'INPUT', attribute: { id: 'foo' } }, + ], + }, + }) + const expected = [ + { + op: 'replace', + path: '/attributeValues', + value: [{ value: 'INPUT', attribute: { id: 'foo' } }], + }, + ] + expect(actual).toEqual(expected) + }) + }) +}) diff --git a/src/pages/dataElements/edit/createJsonPatchOperations.ts b/src/pages/dataElements/edit/createJsonPatchOperations.ts index 762a613e..97e152f2 100644 --- a/src/pages/dataElements/edit/createJsonPatchOperations.ts +++ b/src/pages/dataElements/edit/createJsonPatchOperations.ts @@ -1,50 +1,69 @@ import get from 'lodash/fp/get' import { JsonPatchOperation } from '../../../types' -import { DataElement } from '../../../types/generated' -import type { FormValues } from '../form' +import { Attribute, AttributeValue } from './../../../types/generated/models' -type DataElementKey = keyof DataElement +type PatchAttribute = { + id: Attribute['id'] +} + +type PatchAttributeValue = { + attribute: PatchAttribute + value: AttributeValue['value'] +} -interface FormatFormValuesArgs { - dataElement: DataElement - dirtyFields: { [name: string]: boolean } +type ModelWithAttributeValues = { + attributeValues: PatchAttributeValue[] +} + +interface FormatFormValuesArgs { + originalValue: unknown + dirtyFields: { [key in keyof FormValues]?: boolean } values: FormValues } -const sanitizeDirtyValueKeys = (keys: DataElementKey[]) => { - const attributeValuesDirty = keys.find((key) => - key.startsWith('attributeValues') +// these are removed from the dirtyKeys +// attributeValues is an array in the form, thus the key will be attributeValues[0] etc +// remove these, and replace with 'attributeValues' +// style.code should post to style, not style.code, because it's a complex object +const complexKeys = ['attributeValues', 'style'] as const +export const sanitizeDirtyValueKeys = (dirtyKeys: string[]) => { + const complexChanges = complexKeys.filter((complexKey) => + dirtyKeys.some((dirtyKey) => dirtyKey.startsWith(complexKey)) ) - if (!attributeValuesDirty) { - return keys - } + const dirtyKeysWithoutComplexKeys = dirtyKeys.filter( + (dirtyKey) => + !complexChanges.some((complexKey) => + dirtyKey.startsWith(complexKey) + ) + ) - return [ - ...keys.filter((key) => !key.startsWith('attributeValues')), - 'attributeValues' as DataElementKey, - ] + return dirtyKeysWithoutComplexKeys.concat(complexChanges) } -export function createJsonPatchOperations({ +export function createJsonPatchOperations< + FormValues extends ModelWithAttributeValues +>({ dirtyFields, - dataElement, + originalValue, values: unsanitizedValues, -}: FormatFormValuesArgs): JsonPatchOperation[] { +}: FormatFormValuesArgs): JsonPatchOperation[] { // Remove attribute values without a value const values = { ...unsanitizedValues, - attributeValues: unsanitizedValues.attributeValues.filter( - ({ value }) => !!value - ), + attributeValues: unsanitizedValues.attributeValues + .filter(({ value }) => !!value) + .map((value) => ({ + value: value.value, + attribute: { id: value.attribute.id }, + })), } - const dirtyFieldsKeys = Object.keys(dirtyFields) as DataElementKey[] - const adjustedDirtyFieldsKeys: DataElementKey[] = - sanitizeDirtyValueKeys(dirtyFieldsKeys) + const dirtyFieldsKeys = Object.keys(dirtyFields) + const adjustedDirtyFieldsKeys = sanitizeDirtyValueKeys(dirtyFieldsKeys) return adjustedDirtyFieldsKeys.map((name) => ({ - op: get(name, dataElement) ? 'replace' : 'add', + op: get(name, originalValue) ? 'replace' : 'add', path: `/${name.replace(/[.]/g, '/')}`, value: get(name, values) || '', }))