From 89c7f814a074dac4dd8942c2e010c4cceedd93b1 Mon Sep 17 00:00:00 2001 From: Mino Togna Date: Mon, 21 Oct 2024 10:27:04 +0200 Subject: [PATCH] 4033 - NationalData-DataSources Cycle 2029 (#4035) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * 4033 - NationalData-DataSources Cycle 2029: data/metadata clone (#4034) * 4033 - Do not import dataSources.text prop / Fix clone cycle props * 4033 - improve Objects.isEmpty + unit test * 4033 - fix DataSources grid * 4033 - Do not import dataSources.text value * 4033 - Dashboard cycle clone (#4039) * fix dashboard clone * Disable region level dashboard on all but fra 2020 * 4033 - NationalData->DataSources Cycle 2029 - Labels + clone props fix (#4048) * 4033: Fix DataSources labels * 4033: Use Objects.isNil to check props to clone --------- Co-authored-by: MirĂ³ Sorja --- .../CountryHome/FraHome/hooks/useSections.tsx | 4 +-- .../DataSources/DataSources.tsx | 8 +++-- src/i18n/resources/en/2020.json | 1 - src/i18n/resources/en/2025.json | 11 ------- src/i18n/resources/en/fra.json | 21 ++++++------- src/meta/assessment/cols.ts | 24 ++++++++++----- src/meta/assessment/rows.ts | 27 ++++++++++------- src/meta/assessment/sections.ts | 10 +++++-- src/meta/assessment/subSections.ts | 17 +++++++---- src/meta/assessment/tableSections.ts | 11 ++++--- src/meta/assessment/tables.ts | 23 +++++++------- .../assessment/cloneCycle/_cloneData.ts | 23 ++++++++++++-- src/utils/objects/index.ts | 3 +- src/utils/objects/isEmpty.test.ts | 30 +++++++++++++++++++ src/utils/objects/isEmpty.ts | 10 +++---- src/utils/objects/isNil.ts | 4 +++ 16 files changed, 148 insertions(+), 79 deletions(-) delete mode 100644 src/i18n/resources/en/2020.json delete mode 100644 src/i18n/resources/en/2025.json create mode 100644 src/utils/objects/isEmpty.test.ts create mode 100644 src/utils/objects/isNil.ts diff --git a/src/client/pages/CountryHome/FraHome/hooks/useSections.tsx b/src/client/pages/CountryHome/FraHome/hooks/useSections.tsx index 46a2fa1d89..945294083c 100644 --- a/src/client/pages/CountryHome/FraHome/hooks/useSections.tsx +++ b/src/client/pages/CountryHome/FraHome/hooks/useSections.tsx @@ -30,8 +30,8 @@ export const useSections = (): Array
=> { if (!cycle) return null - const isFra2025 = assessment.props.name === AssessmentNames.fra && cycle.name === '2025' - const showOverview = !isFra2025 || Areas.isISOCountry(countryIso) + const isFra2020 = assessment.props.name === AssessmentNames.fra && cycle.name === '2020' + const showOverview = isFra2020 || Areas.isISOCountry(countryIso) if (showOverview) { sections.push({ name: SectionNames.Country.Home.overview, component: Overview }) diff --git a/src/client/pages/Section/Descriptions/NationalDataDescriptions/DataSources/DataSources.tsx b/src/client/pages/Section/Descriptions/NationalDataDescriptions/DataSources/DataSources.tsx index d04a030d0c..27ad64a320 100644 --- a/src/client/pages/Section/Descriptions/NationalDataDescriptions/DataSources/DataSources.tsx +++ b/src/client/pages/Section/Descriptions/NationalDataDescriptions/DataSources/DataSources.tsx @@ -2,6 +2,8 @@ import './DataSources.scss' import React from 'react' import { useTranslation } from 'react-i18next' +import { Objects } from 'utils/objects' + import { CommentableDescriptionName } from 'meta/assessment' import { NationalDataDescription } from 'meta/assessment/description' @@ -30,7 +32,7 @@ export const DataSources: React.FC = (props: Props) => { const { nationalData } = props const { t } = useTranslation() - const { assessmentName, cycleName } = useCycleRouteParams() + const { assessmentName } = useCycleRouteParams() const { sectionName } = useSectionContext() const { dataSources, text } = useDataSourcesData({ sectionName }) const { dataSourcesLinked } = useGetDataSourcesLinked({ nationalData, sectionName }) @@ -39,8 +41,8 @@ export const DataSources: React.FC = (props: Props) => { const editable = useIsDescriptionEditable({ sectionName, name }) const { empty } = useDescriptionErrorState({ name, sectionName }) - const renderGrid = Boolean(dataSources?.length || dataSourcesLinked?.length || editable) - const keyPrefix = `${assessmentName}.${cycleName}.description.dataSource` + const renderGrid = Boolean(!Objects.isEmpty(dataSources) || !Objects.isEmpty(dataSourcesLinked) || editable) + const keyPrefix = `${assessmentName}.description.dataSource` return ( diff --git a/src/i18n/resources/en/2020.json b/src/i18n/resources/en/2020.json deleted file mode 100644 index 0967ef424b..0000000000 --- a/src/i18n/resources/en/2020.json +++ /dev/null @@ -1 +0,0 @@ -{} diff --git a/src/i18n/resources/en/2025.json b/src/i18n/resources/en/2025.json deleted file mode 100644 index 40857b3b93..0000000000 --- a/src/i18n/resources/en/2025.json +++ /dev/null @@ -1,11 +0,0 @@ -{ - "description": { - "dataSource": { - "comments": "$t(dataSource.comments)", - "referenceToTataSource": "$t(dataSource.referenceToTataSource)", - "typeOfDataSource": "$t(dataSource.typeOfDataSource)", - "variable": "$t(dataSource.fraVariable)", - "yearForDataSource": "$t(dataSource.yearForDataSource)" - } - } -} diff --git a/src/i18n/resources/en/fra.json b/src/i18n/resources/en/fra.json index 9757d4d2d0..cb864ad163 100644 --- a/src/i18n/resources/en/fra.json +++ b/src/i18n/resources/en/fra.json @@ -1,16 +1,4 @@ { - "2020": {}, - "2025": { - "description": { - "dataSource": { - "comments": "$t(dataSource.comments)", - "referenceToTataSource": "$t(dataSource.referenceToTataSource)", - "typeOfDataSource": "$t(dataSource.typeOfDataSource)", - "variable": "$t(dataSource.fraVariable)", - "yearForDataSource": "$t(dataSource.yearForDataSource)" - } - } - }, "area100Ha": "Area (1000 ha)", "area100HaYear": "Area (1000 ha/year)", "areaAffectedByFire": { @@ -94,6 +82,15 @@ "whatIsDefinition": "What is the national definition of \"Degraded forest\"?", "yearOfLatestAssessment": "Year of latest assessment" }, + "description": { + "dataSource": { + "comments": "$t(dataSource.comments)", + "referenceToTataSource": "$t(dataSource.referenceToTataSource)", + "typeOfDataSource": "$t(dataSource.typeOfDataSource)", + "variable": "$t(dataSource.fraVariable)", + "yearForDataSource": "$t(dataSource.yearForDataSource)" + } + }, "designatedManagementObjective": { "designatedManagementObjective2025": "$t(designatedManagementObjective.designatedManagementObjective)", "noDesignation": "No designation", diff --git a/src/meta/assessment/cols.ts b/src/meta/assessment/cols.ts index 906c8486fc..397f86b86d 100644 --- a/src/meta/assessment/cols.ts +++ b/src/meta/assessment/cols.ts @@ -1,4 +1,5 @@ import { TFunction } from 'i18next' +import { Objects } from 'utils/objects' import { Cycle } from 'meta/assessment/cycle' import { Labels } from 'meta/assessment/labels' @@ -12,16 +13,23 @@ const cloneProps = (props: { cycleSource: Cycle; cycleTarget: Cycle; col: Col }) const { uuid: cycleSourceUuid } = cycleSource const { uuid: cycleTargetUuid } = cycleTarget - const _props: Col['props'] = { ...col.props } + const _props: Col['props'] = Objects.cloneDeep(col.props) _props.cycles.push(cycleTargetUuid) - if (_props.calculateFn?.[cycleSourceUuid]) _props.calculateFn[cycleTargetUuid] = _props.calculateFn[cycleSourceUuid] - if (_props.classNames?.[cycleSourceUuid]) _props.classNames[cycleTargetUuid] = _props.classNames[cycleSourceUuid] - if (_props.labels?.[cycleSourceUuid]) _props.labels[cycleTargetUuid] = _props.labels[cycleSourceUuid] - if (_props.linkedNodes?.[cycleSourceUuid]) _props.linkedNodes[cycleTargetUuid] = _props.linkedNodes[cycleSourceUuid] - if (_props.style?.[cycleSourceUuid]) _props.style[cycleTargetUuid] = _props.style[cycleSourceUuid] - if (_props.validateFns?.[cycleSourceUuid]) _props.validateFns[cycleTargetUuid] = _props.validateFns[cycleSourceUuid] - if (_props.variableNo?.[cycleSourceUuid]) _props.variableNo[cycleTargetUuid] = _props.variableNo[cycleSourceUuid] + if (!Objects.isNil(_props.calculateFn?.[cycleSourceUuid])) + _props.calculateFn[cycleTargetUuid] = Objects.cloneDeep(_props.calculateFn[cycleSourceUuid]) + if (!Objects.isNil(_props.classNames?.[cycleSourceUuid])) + _props.classNames[cycleTargetUuid] = Objects.cloneDeep(_props.classNames[cycleSourceUuid]) + if (!Objects.isNil(_props.labels?.[cycleSourceUuid])) + _props.labels[cycleTargetUuid] = Objects.cloneDeep(_props.labels[cycleSourceUuid]) + if (!Objects.isNil(_props.linkedNodes?.[cycleSourceUuid])) + _props.linkedNodes[cycleTargetUuid] = Objects.cloneDeep(_props.linkedNodes[cycleSourceUuid]) + if (!Objects.isNil(_props.style?.[cycleSourceUuid])) + _props.style[cycleTargetUuid] = Objects.cloneDeep(_props.style[cycleSourceUuid]) + if (!Objects.isNil(_props.validateFns?.[cycleSourceUuid])) + _props.validateFns[cycleTargetUuid] = Objects.cloneDeep(_props.validateFns[cycleSourceUuid]) + if (!Objects.isNil(_props.variableNo?.[cycleSourceUuid])) + _props.variableNo[cycleTargetUuid] = Objects.cloneDeep(_props.variableNo[cycleSourceUuid]) return _props } diff --git a/src/meta/assessment/rows.ts b/src/meta/assessment/rows.ts index fc9a55896c..2761576712 100644 --- a/src/meta/assessment/rows.ts +++ b/src/meta/assessment/rows.ts @@ -1,3 +1,5 @@ +import { Objects } from 'utils/objects' + import { Cycle } from 'meta/assessment/cycle' import { Row } from 'meta/assessment/row' @@ -7,18 +9,23 @@ const cloneProps = (props: { cycleSource: Cycle; cycleTarget: Cycle; row: Row }) const { uuid: cycleSourceUuid } = cycleSource const { uuid: cycleTargetUuid } = cycleTarget - const _props: Row['props'] = { ...row.props } + const _props: Row['props'] = Objects.cloneDeep(row.props) _props.cycles.push(cycleTargetUuid) - if (_props.calculateFn?.[cycleSourceUuid]) _props.calculateFn[cycleTargetUuid] = _props.calculateFn[cycleSourceUuid] - if (_props.calculateIf?.[cycleSourceUuid]) _props.calculateIf[cycleTargetUuid] = _props.calculateIf[cycleSourceUuid] - if (_props.chart?.[cycleSourceUuid]) _props.chart[cycleTargetUuid] = _props.chart[cycleSourceUuid] - if (_props.excludeFromDataExport?.[cycleSourceUuid]) - _props.excludeFromDataExport[cycleTargetUuid] = _props.excludeFromDataExport[cycleSourceUuid] - if (_props.linkToSection?.[cycleSourceUuid]) - _props.linkToSection[cycleTargetUuid] = _props.linkToSection[cycleSourceUuid] - if (_props.validateFns?.[cycleSourceUuid]) _props.validateFns[cycleTargetUuid] = _props.validateFns[cycleSourceUuid] - if (_props.withReview?.[cycleSourceUuid]) _props.withReview[cycleTargetUuid] = _props.withReview[cycleSourceUuid] + if (!Objects.isNil(_props.calculateFn?.[cycleSourceUuid])) + _props.calculateFn[cycleTargetUuid] = Objects.cloneDeep(_props.calculateFn[cycleSourceUuid]) + if (!Objects.isNil(_props.calculateIf?.[cycleSourceUuid])) + _props.calculateIf[cycleTargetUuid] = Objects.cloneDeep(_props.calculateIf[cycleSourceUuid]) + if (!Objects.isNil(_props.chart?.[cycleSourceUuid])) + _props.chart[cycleTargetUuid] = Objects.cloneDeep(_props.chart[cycleSourceUuid]) + if (!Objects.isNil(_props.excludeFromDataExport?.[cycleSourceUuid])) + _props.excludeFromDataExport[cycleTargetUuid] = Objects.cloneDeep(_props.excludeFromDataExport[cycleSourceUuid]) + if (!Objects.isNil(_props.linkToSection?.[cycleSourceUuid])) + _props.linkToSection[cycleTargetUuid] = Objects.cloneDeep(_props.linkToSection[cycleSourceUuid]) + if (!Objects.isNil(_props.validateFns?.[cycleSourceUuid])) + _props.validateFns[cycleTargetUuid] = Objects.cloneDeep(_props.validateFns[cycleSourceUuid]) + if (!Objects.isNil(_props.withReview?.[cycleSourceUuid])) + _props.withReview[cycleTargetUuid] = Objects.cloneDeep(_props.withReview[cycleSourceUuid]) return _props } diff --git a/src/meta/assessment/sections.ts b/src/meta/assessment/sections.ts index c24db6037a..b4093e8c05 100644 --- a/src/meta/assessment/sections.ts +++ b/src/meta/assessment/sections.ts @@ -1,3 +1,5 @@ +import { Objects } from 'utils/objects' + import { Cycle } from 'meta/assessment/cycle' import { Section } from 'meta/assessment/section' @@ -7,11 +9,13 @@ const cloneProps = (props: { cycleSource: Cycle; cycleTarget: Cycle; section: Se const { uuid: cycleSourceUuid } = cycleSource const { uuid: cycleTargetUuid } = cycleTarget - const _props: Section['props'] = { ...section.props } + const _props: Section['props'] = Objects.cloneDeep(section.props) _props.cycles.push(cycleTargetUuid) - if (_props.anchors?.[cycleSourceUuid]) _props.anchors[cycleTargetUuid] = _props.anchors[cycleSourceUuid] - if (_props.labels?.[cycleSourceUuid]) _props.labels[cycleTargetUuid] = _props.labels[cycleSourceUuid] + if (!Objects.isNil(_props.anchors?.[cycleSourceUuid])) + _props.anchors[cycleTargetUuid] = Objects.cloneDeep(_props.anchors[cycleSourceUuid]) + if (!Objects.isNil(_props.labels?.[cycleSourceUuid])) + _props.labels[cycleTargetUuid] = Objects.cloneDeep(_props.labels[cycleSourceUuid]) return _props } diff --git a/src/meta/assessment/subSections.ts b/src/meta/assessment/subSections.ts index ec4b07812e..cd1693f0d6 100644 --- a/src/meta/assessment/subSections.ts +++ b/src/meta/assessment/subSections.ts @@ -1,3 +1,5 @@ +import { Objects } from 'utils/objects' + import { Cycle } from 'meta/assessment/cycle' import { Section, SubSection } from 'meta/assessment/section' import { Sections } from 'meta/assessment/sections' @@ -12,12 +14,17 @@ const cloneProps = (props: { cycleSource: Cycle; cycleTarget: Cycle; subSection: const section = subSection as Section const _props: SubSection['props'] = Sections.cloneProps({ cycleSource, cycleTarget, section }) as SubSection['props'] - _props.cycles.push(cycleTargetUuid) - if (_props.descriptions?.[cycleSourceUuid]) - _props.descriptions[cycleTargetUuid] = _props.descriptions[cycleSourceUuid] - if (_props.hidden?.[cycleSourceUuid]) _props.hidden[cycleTargetUuid] = _props.hidden[cycleSourceUuid] - if (_props.hints?.[cycleSourceUuid]) _props.hints[cycleTargetUuid] = _props.hints[cycleSourceUuid] + const descriptionsSource = Objects.cloneDeep(_props.descriptions?.[cycleSourceUuid]) + if (!Objects.isEmpty(descriptionsSource)) { + // delete nationalData->dataSources->text needed only in FRA 2025 + Objects.unset(descriptionsSource, ['nationalData', 'dataSources', 'text']) + _props.descriptions[cycleTargetUuid] = descriptionsSource + } + if (!Objects.isNil(_props.hidden?.[cycleSourceUuid])) + _props.hidden[cycleTargetUuid] = Objects.cloneDeep(_props.hidden[cycleSourceUuid]) + if (!Objects.isNil(_props.hints?.[cycleSourceUuid])) + _props.hints[cycleTargetUuid] = Objects.cloneDeep(_props.hints[cycleSourceUuid]) return _props } diff --git a/src/meta/assessment/tableSections.ts b/src/meta/assessment/tableSections.ts index 5b8213dcdd..33a1ab011c 100644 --- a/src/meta/assessment/tableSections.ts +++ b/src/meta/assessment/tableSections.ts @@ -1,3 +1,5 @@ +import { Objects } from 'utils/objects' + import { Cycle } from 'meta/assessment/cycle' import { TableSection } from 'meta/assessment/tableSection' @@ -11,12 +13,13 @@ const cloneProps = (props: { const { uuid: cycleSourceUuid } = cycleSource const { uuid: cycleTargetUuid } = cycleTarget - const _props: TableSection['props'] = { ...tableSection.props } + const _props: TableSection['props'] = Objects.cloneDeep(tableSection.props) _props.cycles.push(cycleTargetUuid) - if (_props.descriptions?.[cycleSourceUuid]) - _props.descriptions[cycleTargetUuid] = _props.descriptions[cycleSourceUuid] - if (_props.labels?.[cycleSourceUuid]) _props.labels[cycleTargetUuid] = _props.labels[cycleSourceUuid] + if (!Objects.isNil(_props.descriptions?.[cycleSourceUuid])) + _props.descriptions[cycleTargetUuid] = Objects.cloneDeep(_props.descriptions[cycleSourceUuid]) + if (!Objects.isNil(_props.labels?.[cycleSourceUuid])) + Objects.cloneDeep((_props.labels[cycleTargetUuid] = _props.labels[cycleSourceUuid])) return _props } diff --git a/src/meta/assessment/tables.ts b/src/meta/assessment/tables.ts index 0f969849ac..931047b966 100644 --- a/src/meta/assessment/tables.ts +++ b/src/meta/assessment/tables.ts @@ -1,3 +1,5 @@ +import { Objects } from 'utils/objects' + import { Cycle } from 'meta/assessment/cycle' import { Table } from 'meta/assessment/table' @@ -7,18 +9,19 @@ const cloneProps = (props: { cycleSource: Cycle; cycleTarget: Cycle; table: Tabl const { uuid: cycleSourceUuid } = cycleSource const { uuid: cycleTargetUuid } = cycleTarget - const _props: Table['props'] = { ...table.props } + const _props: Table['props'] = Objects.cloneDeep(table.props) _props.cycles.push(cycleTargetUuid) - if (_props.cellsExportAlways?.[cycleSourceUuid]) - _props.cellsExportAlways[cycleTargetUuid] = _props.cellsExportAlways[cycleSourceUuid] - if (_props.columnNames?.[cycleSourceUuid]) _props.columnNames[cycleTargetUuid] = _props.columnNames[cycleSourceUuid] - if (_props.columnsExport?.[cycleSourceUuid]) - _props.columnsExport[cycleTargetUuid] = _props.columnsExport[cycleSourceUuid] - if (_props.columnsExportAlways?.[cycleSourceUuid]) - _props.columnsExportAlways[cycleTargetUuid] = _props.columnsExportAlways[cycleSourceUuid] - if (_props.disableErrorMessage?.[cycleSourceUuid]) - _props.disableErrorMessage[cycleTargetUuid] = _props.disableErrorMessage[cycleSourceUuid] + if (!Objects.isNil(_props.cellsExportAlways?.[cycleSourceUuid])) + _props.cellsExportAlways[cycleTargetUuid] = Objects.cloneDeep(_props.cellsExportAlways[cycleSourceUuid]) + if (!Objects.isNil(_props.columnNames?.[cycleSourceUuid])) + _props.columnNames[cycleTargetUuid] = Objects.cloneDeep(_props.columnNames[cycleSourceUuid]) + if (!Objects.isNil(_props.columnsExport?.[cycleSourceUuid])) + _props.columnsExport[cycleTargetUuid] = Objects.cloneDeep(_props.columnsExport[cycleSourceUuid]) + if (!Objects.isNil(_props.columnsExportAlways?.[cycleSourceUuid])) + _props.columnsExportAlways[cycleTargetUuid] = Objects.cloneDeep(_props.columnsExportAlways[cycleSourceUuid]) + if (!Objects.isNil(_props.disableErrorMessage?.[cycleSourceUuid])) + _props.disableErrorMessage[cycleTargetUuid] = Objects.cloneDeep(_props.disableErrorMessage[cycleSourceUuid]) return _props } diff --git a/src/server/controller/assessment/cloneCycle/_cloneData.ts b/src/server/controller/assessment/cloneCycle/_cloneData.ts index ce5beab116..ae918b7306 100644 --- a/src/server/controller/assessment/cloneCycle/_cloneData.ts +++ b/src/server/controller/assessment/cloneCycle/_cloneData.ts @@ -1,3 +1,6 @@ +import { CommentableDescriptionName } from 'meta/assessment' +import { NodeExtType } from 'meta/nodeExt' + import { CloneProps } from 'server/controller/assessment/cloneCycle/types' import { BaseProtocol, Schemas } from 'server/db' @@ -16,8 +19,16 @@ export const cloneData = async (props: CloneProps, client: BaseProtocol): Promis select uuid, country_iso, row_uuid, col_uuid, value from ${schemaCycleSource}.node; - insert into ${schemaCycleTarget}.node_ext (country_iso, parent_uuid, props, type, uuid, value) - select country_iso, parent_uuid, props, type, uuid, value + insert into ${schemaCycleTarget}.node_ext ( + country_iso, parent_uuid, props, type, uuid, value + ) + select + country_iso, parent_uuid, props, type, uuid, + case + when type = '${NodeExtType.dashboard}' then + replace(value::text, '${cycleSource.uuid}', '${cycleTarget.uuid}')::jsonb + else value + end as value from ${schemaCycleSource}.node_ext; insert into ${schemaCycleTarget}.node_values_estimation (uuid, country_iso, table_uuid, created_at, method, variables) @@ -39,7 +50,13 @@ export const cloneData = async (props: CloneProps, client: BaseProtocol): Promis from ${schemaCycleSource}.original_data_point; insert into ${schemaCycleTarget}.descriptions (country_iso, section_name, name, value) - select country_iso, section_name, name, value + select country_iso, + section_name, + name, + case + when name = '${CommentableDescriptionName.dataSources}' then jsonb_delete(value, 'text') + else value + end -- // delete nationalData->dataSources->text needed only in FRA 2025 from ${schemaCycleSource}.descriptions; insert into ${schemaCycleTarget}.repository (uuid, country_iso, file_uuid, link, props) diff --git a/src/utils/objects/index.ts b/src/utils/objects/index.ts index 8fd719a439..b8b81f74da 100644 --- a/src/utils/objects/index.ts +++ b/src/utils/objects/index.ts @@ -5,8 +5,6 @@ import * as isEqual from 'lodash.isequal' // @ts-ignore import * as isFunction from 'lodash.isfunction' // @ts-ignore -import * as isNil from 'lodash.isnil' -// @ts-ignore import * as merge from 'lodash.merge' // @ts-ignore import * as pick from 'lodash.pick' @@ -17,6 +15,7 @@ import { camelize } from './camelize' import { getDiff } from './getDiff' import { getInPath } from './getInPath' import { isEmpty } from './isEmpty' +import { isNil } from './isNil' import { propertyOf } from './propertyOf' import { setInPath } from './setInPath' diff --git a/src/utils/objects/isEmpty.test.ts b/src/utils/objects/isEmpty.test.ts new file mode 100644 index 0000000000..705932c1fc --- /dev/null +++ b/src/utils/objects/isEmpty.test.ts @@ -0,0 +1,30 @@ +import { isEmpty } from 'utils/objects/isEmpty' + +const tests: Array<{ empty: boolean; value: any }> = [ + { empty: true, value: undefined }, + { empty: true, value: null }, + { empty: true, value: '' }, + { empty: true, value: ' ' }, + { empty: false, value: ' fdsafd a' }, + { empty: false, value: 'fdsafd ' }, + { empty: false, value: 'fdsafd' }, + { empty: false, value: { a: '' } }, + { empty: false, value: { a: '', b: '' } }, + { empty: false, value: { a: undefined } }, + { empty: true, value: {} }, + { empty: false, value: [1] }, + { empty: false, value: [1, 2, 3] }, + { empty: true, value: [] }, + { empty: false, value: [undefined] }, + { empty: false, value: [null] }, + { empty: false, value: [undefined, null] }, +] + +describe('isEmpty', () => { + tests.forEach(({ empty, value }) => { + test(`${JSON.stringify(value)}-${empty}`, () => { + const result = isEmpty(value) + expect(result).toEqual(empty) + }) + }) +}) diff --git a/src/utils/objects/isEmpty.ts b/src/utils/objects/isEmpty.ts index e3c3d05c10..3099944cb8 100644 --- a/src/utils/objects/isEmpty.ts +++ b/src/utils/objects/isEmpty.ts @@ -1,3 +1,5 @@ +import { isNil } from 'utils/objects/isNil' + /** * Determines if the specified value is null, empty string, NaN, empty object or empty array. * @@ -5,9 +7,7 @@ * @returns {boolean} True if the specified value is empty, false otherwise. */ export const isEmpty = (value: any): boolean => - value === undefined || - value === null || - value === '' || + isNil(value) || Number.isNaN(value) || - (value instanceof Object && Object.entries(value).length === 0) || - (Array.isArray(value) && value.length === 0) + (typeof value === 'object' && Object.keys(value).length === 0) || + (typeof value === 'string' && value.trim().length === 0) diff --git a/src/utils/objects/isNil.ts b/src/utils/objects/isNil.ts new file mode 100644 index 0000000000..b7214ed143 --- /dev/null +++ b/src/utils/objects/isNil.ts @@ -0,0 +1,4 @@ +// @ts-expect-error +import * as isNil from 'lodash.isnil' + +export { isNil }