Skip to content
This repository has been archived by the owner on Aug 23, 2023. It is now read-only.

Commit

Permalink
Merge branch 'release/v1.0.61'
Browse files Browse the repository at this point in the history
  • Loading branch information
nnkogift committed Aug 9, 2022
2 parents ed146bd + 26abed1 commit 66fb5ea
Show file tree
Hide file tree
Showing 16 changed files with 357 additions and 285 deletions.
2 changes: 0 additions & 2 deletions .storybook/main.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,5 +6,3 @@ module.exports = {
builder: "@storybook/builder-vite",
},
};

export {};
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
{
"name": "@hisptz/react-ui",
"homepage": "https://hisptz.github.io/react-ui",
"version": "1.0.60",
"version": "1.0.61",
"description": "A collection of reusable complex DHIS2 react ui components.",
"license": "BSD-3-Clause",
"scripts": {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ function ChoroplethLegend(
<Divider margin={"0"} />
<div style={{ width: 150 }} className="legend-list pt-8">
{legends?.map((legend: any) => (
<LegendItem key={`${legend?.id}`} legend={legend} value={getLegendCount(legend, data)} />
<LegendItem key={`${legend?.color}-legend-list`} legend={legend} value={getLegendCount(legend, data)} />
))}
</div>
</div>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,6 @@ const highlightStyle = {

export default function Choropleth({ data }: { data: { orgUnit: MapOrgUnit; data?: number; dataItem: ThematicLayerDataItem } }) {
const { dataItem, orgUnit } = data;

return (
<>
<GeoJSON
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { useDataQuery } from "@dhis2/app-runtime";
import { isEmpty } from "lodash";
import { useMemo } from "react";
import { useEffect, useMemo } from "react";
import { MapOrgUnit } from "../../../../../interfaces";
import { getOrgUnitsSelection } from "../../../../../utils/map";
import { useMapOrganisationUnit, useMapPeriods } from "../../../../MapProvider/hooks";
Expand Down Expand Up @@ -29,18 +29,29 @@ export default function useThematicLayerData(layer: ThematicLayer): {
} {
const { orgUnits, orgUnitSelection } = useMapOrganisationUnit();
const { periods } = useMapPeriods() ?? {};
const ou = getOrgUnitsSelection(orgUnitSelection);
const pe = periods?.map((pe: any) => pe.id);
const dx = [layer.dataItem.id];
const ou = useMemo(() => getOrgUnitsSelection(orgUnitSelection), [orgUnitSelection]);
const pe = useMemo(() => periods?.map((pe: any) => pe.id), [periods]);
const dx = useMemo(() => [layer.dataItem.id], [layer]);

const { loading, data, error } = useDataQuery(analyticsQuery, {
const { loading, data, error, refetch } = useDataQuery(analyticsQuery, {
variables: {
ou,
pe,
dx,
legendSetId: layer?.dataItem?.legendSet?.id,
},
lazy: true,
});

useEffect(() => {
refetch({
ou,
pe,
dx,
legendSetId: layer?.dataItem?.legendSet?.id,
});
}, [ou, pe, dx]);

const formattedData = useMemo(() => {
if (data) {
const { analytics } = data as any;
Expand Down
18 changes: 18 additions & 0 deletions src/components/OrgUnitSelector/OrgUnitSelector.stories.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -80,6 +80,24 @@ WithAllOptions.args = {
searchable: true,
};

export const SelectionDisabledLevels = Template.bind({});
SelectionDisabledLevels.args = {
value: { orgUnits: [] },
onUpdate: (value) => {
console.log(value);
},
limitSelectionToLevels: [3, 2],
};

export const FilterByGroups = Template.bind({});
FilterByGroups.args = {
value: { orgUnits: [] },
onUpdate: (value) => {
console.log(value);
},
filterByGroups: ["RXL3lPSK8oG"],
};

export default {
title: "Components/Organisation Unit Selector",
component: OrgUnitSelector,
Expand Down
40 changes: 17 additions & 23 deletions src/components/OrgUnitSelector/OrgUnitSelector.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import { mount } from "@cypress/react";
import type { OrgUnitSelection } from "@hisptz/dhis2-utils";
import React from "react";
import OrgUnitDataProvider from "../../dataProviders/orgUnit";
import { OrgUnitSelectorValue } from "./types";
import OrgUnitSelector from "./index";

describe("Org Unit Selector", () => {
Expand Down Expand Up @@ -88,33 +89,26 @@ describe("Org Unit Selector", () => {
});

it("should return the selected org unit", function () {
// let selectedOrgUnit: OrgUnitSelectorValue;
// const onSelect = (orgUnitSelection: OrgUnitSelectorValue) => {
// selectedOrgUnit = orgUnitSelection;
// };
let selectedOrgUnit: OrgUnitSelectorValue;
const onSelect = (orgUnitSelection: OrgUnitSelectorValue) => {
selectedOrgUnit = orgUnitSelection;
};
mount(
<DHIS2Provider>
<OrgUnitSelector
value={{ orgUnits: [] }}
showLevels
onUpdate={() => {
return;
}}
/>
<OrgUnitSelector value={{ orgUnits: [] }} showLevels onUpdate={onSelect} />
</DHIS2Provider>
).then(() => {
return;
// cy.get("[data-test=dhis2-uicore-checkbox]", { timeout: 10000 })
// .click()
// .then(() => {
// expect(selectedOrgUnit.orgUnits?.length).to.equal(1);
// });
// cy.get("[data-test='levels-selector']").click();
// cy.get("[data-test='Facility-option']")
// .click()
// .then(() => {
// expect(selectedOrgUnit.levels?.length).to.equal(1);
// });
cy.get("[data-test=dhis2-uicore-checkbox]", { timeout: 10000 })
.click()
.then(() => {
expect(selectedOrgUnit.orgUnits?.length).to.equal(1);
});
cy.get("[data-test='levels-selector']").click();
cy.get("[data-test='Facility-option']")
.click()
.then(() => {
expect(selectedOrgUnit.levels?.length).to.equal(1);
});
});
});
});
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
import i18n from "@dhis2/d2-i18n";
import { MultiSelectField, MultiSelectOption } from "@dhis2/ui";
import type { OrgUnitSelection } from "@hisptz/dhis2-utils";
import { intersectionWith } from "lodash";
import React from "react";
import { useOrgUnitLevelsAndGroups } from "../../hooks";
import { onGroupSelect, onLevelSelect } from "../../utils";

export function LevelAndGroupSelector({
showLevels,
showGroups,
value,
onUpdate,
disableSelections,
}: {
showLevels?: boolean;
showGroups?: boolean;
value: OrgUnitSelection | undefined;
onUpdate: ((value: OrgUnitSelection) => void) | undefined;
disableSelections?: boolean;
}) {
const { groups, levels, error: levelsAndGroupsError, loading: levelsAndGroupsLoading } = useOrgUnitLevelsAndGroups();

const sanitizedSelectedLevels = intersectionWith(value?.levels, levels, (levelId, level) => levelId === level.id);
const sanitizedSelectedGroups = intersectionWith(value?.groups, groups, (groupId, group) => groupId === group.id);

return (
<div className="row pt-32" style={{ gap: 16 }}>
{showLevels && (
<div data-test="levels-selector" className="column w-100">
<MultiSelectField
disabled={disableSelections || levelsAndGroupsLoading}
clearable
loading={levelsAndGroupsLoading}
error={levelsAndGroupsError}
validationText={levelsAndGroupsError?.message}
onChange={onLevelSelect({ onUpdate, value })}
selected={levelsAndGroupsLoading ? [] : sanitizedSelectedLevels ?? []}
clearText={i18n.t("Clear")}
label={i18n.t("Select Level(s)")}>
{levels?.map(({ displayName, id }: any) => (
<MultiSelectOption dataTest={`${displayName}-option`} label={displayName} value={id} key={id} />
))}
</MultiSelectField>
</div>
)}
{showGroups && (
<div data-test="groups-selector" className="column w-100">
<MultiSelectField
disabled={disableSelections || levelsAndGroupsLoading}
clearable
loading={levelsAndGroupsLoading}
error={levelsAndGroupsError}
validationText={levelsAndGroupsError?.message}
onChange={onGroupSelect({ onUpdate, value })}
selected={levelsAndGroupsLoading ? [] : sanitizedSelectedGroups ?? []}
dataTest={"select-facility-group"}
clearText={i18n.t("Clear")}
label={i18n.t("Select Group(s)")}>
{groups?.map(({ displayName, id }) => (
<MultiSelectOption dataTest={`${displayName}-option`} label={displayName} value={id} key={id} />
))}
</MultiSelectField>
</div>
)}
</div>
);
}
87 changes: 87 additions & 0 deletions src/components/OrgUnitSelector/components/OrgUnitTree/index.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,87 @@
import i18n from "@dhis2/d2-i18n";
import { OrganisationUnitTree } from "@dhis2/ui";
import type { OrganisationUnit, OrgUnitSelection } from "@hisptz/dhis2-utils";
import { isEmpty } from "lodash";
import React from "react";
import { isOrgUnitSelected, onDeselectOrgUnit, onSelectOrgUnit } from "../../utils";

export function CustomOrgUnitNodeLabel({ node, limitSelectionToLevels }: { node: { displayName: string; level: number }; limitSelectionToLevels?: number[] }) {
const allowSelection = limitSelectionToLevels?.includes(node.level) ?? true;
return <div style={!allowSelection ? { opacity: 0.5 } : undefined}>{node.displayName}</div>;
}

export function OrgUnitTree({
value,
onUpdate,
disableSelections,
filter,
expanded,
handleExpand,
roots,
singleSelection,
keyword,
limitSelectionToLevels,
}: {
value: OrgUnitSelection | undefined;
onUpdate: ((value: OrgUnitSelection) => void) | undefined;
disableSelections?: boolean;
singleSelection?: boolean;
filter: string[];
expanded: string[];
handleExpand: (orgUnit: { path: string }) => void;
roots: OrganisationUnit[];
keyword?: string;
limitSelectionToLevels?: number[];
}) {
const selectedOrgUnits = value?.orgUnits ?? [];

const onSelect = (orgUnit: any) => {
if (limitSelectionToLevels !== undefined) {
if (!limitSelectionToLevels.includes(orgUnit.level)) {
return;
}
}

if (isOrgUnitSelected(selectedOrgUnits ?? [], orgUnit as OrganisationUnit)) {
onDeselectOrgUnit(orgUnit as OrganisationUnit, selectedOrgUnits, { onUpdate, value });
} else {
onSelectOrgUnit(orgUnit, selectedOrgUnits, { onUpdate, value });
}
};

return (
<div
style={
disableSelections
? {
opacity: 0.3,
cursor: "not-allowed",
overflow: "auto",
}
: { overflow: "auto", maxHeight: 400, height: 400 }
}>
{(keyword?.length ?? 0) > 3 && isEmpty(filter) ? (
<div className="column center align-items-center w-100 h-100">
<p>
{i18n.t("Could not find organisation units matching keyword ")}
<b>{keyword}</b>
</p>
</div>
) : (
<OrganisationUnitTree
forceReload
filter={filter}
disableSelection={disableSelections}
selected={selectedOrgUnits?.map((orgUnit) => orgUnit.path)}
expanded={expanded}
handleExpand={handleExpand}
handleCollapse={handleExpand}
renderNodeLabel={(props: any) => <CustomOrgUnitNodeLabel {...props} limitSelectionToLevels={limitSelectionToLevels} />}
roots={roots?.map((root) => root.id)}
onChange={onSelect}
singleSelection={singleSelection}
/>
)}
</div>
);
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
import i18n from "@dhis2/d2-i18n";
import { CheckboxField, colors } from "@dhis2/ui";
import { OrgUnitSelection } from "@hisptz/dhis2-utils";
import React from "react";
import { onUserOrUnitChange, onUserSubUnitsChange, onUserSubX2Units } from "../../utils";

export function OrgUnitUserOptions({ value, onUpdate }: { onUpdate: ((value: OrgUnitSelection) => void) | undefined; value: OrgUnitSelection | undefined }) {
const { userSubX2Unit, userOrgUnit, userSubUnit } = value ?? {};

return (
<div data-test="user-options-selector" style={{ background: colors.grey200 }} className="row space-between p-16">
<CheckboxField
dataTest={"user-org-unit"}
checked={userOrgUnit}
onChange={onUserOrUnitChange({ onUpdate, value })}
label={i18n.t("User organisation unit")}
/>
<CheckboxField
dataTest={"user-sub-org-unit"}
checked={userSubUnit}
onChange={onUserSubUnitsChange({ onUpdate, value })}
label={i18n.t("User sub-units")}
/>
<CheckboxField
dataTest={"user-sub-x2-org-unit"}
checked={userSubX2Unit}
onChange={onUserSubX2Units({ onUpdate, value })}
label={i18n.t("User sub-x2-units")}
/>
</div>
);
}
Loading

0 comments on commit 66fb5ea

Please sign in to comment.