Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

RN-1130: Update types for dashboard item presentation options #5380

Merged
merged 25 commits into from
Feb 15, 2024
Merged
Show file tree
Hide file tree
Changes from 17 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 3 additions & 2 deletions packages/tupaia-web/src/api/queries/useMapOverlayReport.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ import {
} from '@tupaia/ui-map-components';
import { get } from '../api';
import { EntityCode, ProjectCode } from '../../types';
import { Moment } from 'moment';

type SingleMapOverlayItem = TupaiaWebMapOverlaysRequest.TranslatedMapOverlay;

Expand Down Expand Up @@ -67,8 +68,8 @@ export const useMapOverlayReport = (
entityCode?: EntityCode,
mapOverlay?: SingleMapOverlayItem,
params?: {
startDate?: string;
endDate?: string;
startDate?: string | Moment;
endDate?: string | Moment;
},
keepPreviousData?: boolean,
) => {
Expand Down
9 changes: 4 additions & 5 deletions packages/tupaia-web/src/components/DateRangePicker.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,10 +5,9 @@

import React from 'react';
import { Moment } from 'moment';
import { GRANULARITIES } from '@tupaia/utils';
import { VizPeriodGranularity } from '@tupaia/types';
import { DateRangePicker as DateRangePickerComponent, TextButton } from '@tupaia/ui-components';
import styled from 'styled-components';
import { ValueOf } from '../types';

const Wrapper = styled.div`
margin-top: 0.5rem;
Expand Down Expand Up @@ -77,9 +76,9 @@ const DialogPaperComponent = styled.div`
`;

interface DateRangePickerProps {
startDate?: Moment;
endDate?: Moment;
granularity?: ValueOf<typeof GRANULARITIES>;
startDate?: Moment | string;
endDate?: Moment | string;
granularity?: `${VizPeriodGranularity}`;
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Using a type like ${.....} makes the dev-x a little nicer because when you hover over the value it shows all the string values it could be

onSetDates?: (startDate: string, endDate: string) => void;
minDate?: string;
maxDate?: string;
Expand Down
32 changes: 20 additions & 12 deletions packages/tupaia-web/src/features/DashboardItem/DashboardItem.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -9,8 +9,8 @@ import { Moment } from 'moment';
import { useParams } from 'react-router';
import { Typography } from '@material-ui/core';
import { getDefaultDates } from '@tupaia/utils';
import { MultiValueViewConfig } from '@tupaia/types';
import { DashboardItemConfig, DashboardItem as DashboardItemType } from '../../types';
import { DashboardItemConfig } from '@tupaia/types';
import { DashboardItem as DashboardItemType } from '../../types';
import { useReport } from '../../api/queries';
import { useDashboard } from '../Dashboard';
import { DashboardItemContent } from './DashboardItemContent';
Expand Down Expand Up @@ -50,17 +50,25 @@ const Title = styled(Typography).attrs({
line-height: 1.4;
`;

const getShowDashboardItemTitle = (config: DashboardItemConfig, legacy?: boolean) => {
const { presentationOptions, type, viewType, name } = config;
const getShowDashboardItemTitle = (config?: DashboardItemConfig, legacy?: boolean) => {
if (!config) return false;
const { type, name } = config;
if (!name) return false;
if (viewType === 'multiValue') {
// if report is legacy, show title because it won't have the config set
return (
(presentationOptions as MultiValueViewConfig['presentationOptions'])?.isTitleVisible || legacy
);
if (type === 'component') return false;
if (type === 'view') {
const { viewType } = config;
if (
viewType === 'multiValue' &&
'presentationOptions' in config &&
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It's a little annoying to have to type check for presentations every time they're used, and I did explore a util function but it ended up being not that useful since there are several different shapes of presentationOptions so I could never get the types exactly right in a generic function (mainly because if you have a union type and pass a key into a function as keyof the union type, it only gives you the common keys that apply to everything in that union type, not all the possible keys. Seemed more trouble than it was worth to make it work).

config.presentationOptions
) {
const { presentationOptions } = config;
// if report is legacy, show title because it won't have the config set
return presentationOptions?.isTitleVisible || legacy;
}
if (viewType?.includes('Download') || viewType === 'multiSingleValue') return false;
}
if (viewType?.includes('Download') || type === 'component' || viewType === 'multiSingleValue')
return false;

return true;
};

Expand Down Expand Up @@ -92,7 +100,7 @@ export const DashboardItem = ({ dashboardItem }: { dashboardItem: DashboardItemT
legacy: dashboardItem?.legacy,
});

const { config = {}, legacy } = dashboardItem;
const { config, legacy } = dashboardItem;

const showTitle = getShowDashboardItemTitle(config, legacy);

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,9 +5,9 @@

import React, { useContext } from 'react';
import { NoData } from '@tupaia/ui-components';
import { BaseReport } from '@tupaia/types';
import { BaseReport, DashboardItemConfig } from '@tupaia/types';
import { FetchErrorAlert } from '../../components';
import { DashboardItemReport, DashboardItemConfig } from '../../types';
import { DashboardItemReport } from '../../types';
import {
View,
Chart,
Expand All @@ -30,7 +30,7 @@ const DisplayComponents = {
NoDataAtLevelDashboard,
};

const getHasNoData = (report: DashboardItemReport, type: DashboardItemConfig['type']) => {
const getHasNoData = (report: DashboardItemReport, type?: DashboardItemConfig['type']) => {
// If there is no report, if means it is loading or there is an error, which is handled elsewhere
if (!report) return false;
if (type === 'view' || type === 'chart') {
Expand All @@ -44,9 +44,17 @@ const getHasNoData = (report: DashboardItemReport, type: DashboardItemConfig['ty
export const DashboardItemContent = () => {
const { config, report, isExport, isLoading, error, refetch } = useContext(DashboardItemContext);

const { type, componentName } = config || {};
const getComponentKey = () => {
if (config?.type === 'component' && config) {
const { componentName } = config;
if (componentName) {
return componentName;
}
}
return config?.type;
};

const componentKey = componentName || type;
const componentKey = getComponentKey();

const DisplayComponent = DisplayComponents[componentKey as keyof typeof DisplayComponents];

Expand All @@ -58,7 +66,7 @@ export const DashboardItemContent = () => {
return <DashboardItemLoader name={config?.name} isExport={isExport} />;

// if there is no data for the selected dates, then we want to show a message to the user
const showNoDataMessage = isLoading ? false : getHasNoData(report!, type);
const showNoDataMessage = isLoading ? false : getHasNoData(report!, config?.type);

return (
<>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,9 @@
*/

import { createContext } from 'react';
import { DashboardItem, DashboardItemConfig, DashboardItemReport } from '../../types';
import { UseQueryResult } from 'react-query';
import { DashboardItemConfig } from '@tupaia/types';
import { DashboardItem, DashboardItemReport } from '../../types';

type DashboardItemState = {
config?: DashboardItemConfig | null;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -26,8 +26,9 @@ export const DashboardItemDateDisplay = () => {

if (!config || !report || isExport || isLoading) return null;

const { type } = config;
const { period } = report!;
const { showPeriodRange } = config;
const showPeriodRange = type === 'chart' ? config?.showPeriodRange : null;

if (!period || !period?.latestAvailable) return null;
const showLatestAvailable = isEnlarged ? showPeriodRange === 'all' : !!showPeriodRange;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@
import React from 'react';
import styled from 'styled-components';
import { SpinningLoader } from '@tupaia/ui-components';
import { DashboardItemConfig } from '../../types';
import { DashboardItemConfig } from '@tupaia/types';

const LoadingContainer = styled.div<{
$isExporting?: boolean;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,6 @@ import {
URL_SEARCH_PARAMS,
ViewVizTypes,
} from '../../constants';
import { DashboardItemConfig } from '../../types';
import { DashboardItemContext } from './DashboardItemContext';

const ExpandableButton = styled(Button).attrs({
Expand Down Expand Up @@ -76,20 +75,24 @@ const EXPANDABLE_TYPES = [
export const ExpandItemButton = () => {
const { config, isEnlarged, isExport, report, reportCode } = useContext(DashboardItemContext);

const { viewType, type, periodGranularity } = config || ({} as DashboardItemConfig);
const { type, periodGranularity } = config || {};
const [urlSearchParams, setUrlSearchParams] = useSearchParams();

if (isEnlarged || isExport) return null;

const viewType = config?.type === 'view' ? config.viewType : undefined;

const getIsExpandable = () => {
if (periodGranularity) return true;
// always allow matrix to be expanded
else if (type === DashboardItemVizTypes.Matrix) return true;
if (type === DashboardItemVizTypes.Matrix) return true;

const comparisonType = viewType || type;
// only expand expandable types if they have data, if they don't have periodGranularity set
else if (EXPANDABLE_TYPES.includes(type) || (viewType && EXPANDABLE_TYPES.includes(viewType))) {
if (comparisonType && EXPANDABLE_TYPES.includes(comparisonType)) {
const { data } = report as ViewReport;
return data && data.length > 0;
} else if (viewType === ViewVizTypes.QRCode) {
} else if (comparisonType === ViewVizTypes.QRCode) {
const { data } = report as ViewReport;
return data && data.length > 1;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@
import React, { useContext } from 'react';
import styled from 'styled-components';
import { useSearchParams } from 'react-router-dom';
import { BaseReport, ViewConfig } from '@tupaia/types';
import { BaseReport, ChartPresentationOptions, ViewConfig } from '@tupaia/types';
import { URL_SEARCH_PARAMS } from '../../constants';
import { Modal } from '../../components';
import { Entity } from '../../types';
Expand Down Expand Up @@ -57,13 +57,28 @@ export const EnlargedDashboardItem = ({ entityName }: { entityName?: Entity['nam
const { reportCode, currentDashboardItem, isLoadingDashboards, reportData } =
useEnlargedDashboardItem();

const { type, presentationOptions } = currentDashboardItem?.config || {};
const { type } = currentDashboardItem?.config || {};

const {
exportWithLabels = false,
exportWithTable = true,
exportWithTableDisabled = false,
} = presentationOptions || {};
const getPresentationOptions = () => {
if (currentDashboardItem?.config && 'presentationOptions' in currentDashboardItem?.config) {
return currentDashboardItem?.config?.presentationOptions;
}
return {};
};

const presentationOptions = getPresentationOptions();

// not all dashboard item types have these export settings. Realistically, only charts use these
const getExportSetting = (setting: keyof ChartPresentationOptions, defaultValue: boolean) => {
if (presentationOptions && setting in presentationOptions) {
return presentationOptions[setting];
}
return defaultValue;
};

const exportWithLabels = getExportSetting('exportWithLabels', false);
const exportWithTable = getExportSetting('exportWithTable', true);
const exportWithTableDisabled = getExportSetting('exportWithTableDisabled', false);

if (!reportCode || (!isLoadingDashboards && !currentDashboardItem)) return null;

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -115,6 +115,32 @@ export const EnlargedDashboardVisual = ({

// today's date for export
const date = String(moment());

const getMergedConfig = () => {
// gauge charts don't have presentation options
if (config?.type !== 'chart' || config?.chartType === 'gauge') return config;
// only apply these changes to chart types, as they are not relevant to other types
if ('presentationOptions' in currentDashboardItem?.config) {
return {
...config,
presentationOptions: {
...config?.presentationOptions,
exportWithLabels,
exportWithTable,
},
};
}
return {
...config,
presentationOptions: {
exportWithLabels,
exportWithTable,
},
};
};

const mergedConfig = getMergedConfig();

return (
<Container $isExportMode={isExportMode}>
<TitleWrapper>
Expand Down Expand Up @@ -144,14 +170,7 @@ export const EnlargedDashboardVisual = ({
isEnlarged: true,
isExport: isPreview,
reportCode: currentDashboardItem?.reportCode,
config: {
...currentDashboardItem?.config,
presentationOptions: {
...currentDashboardItem?.config?.presentationOptions,
exportWithLabels,
exportWithTable,
},
},
config: mergedConfig,
}}
>
<DashboardItemContent />
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -31,11 +31,20 @@ export const ExportButton = () => {
const [urlSearchParams] = useSearchParams();
const { isExportMode, setIsExportMode } = useContext(ExportDashboardItemContext);
const { currentDashboardItem } = useEnlargedDashboardItem();
const { type, viewType } = currentDashboardItem?.config || {};
const displayType = viewType || type;
const getDisplayType = () => {
if (!currentDashboardItem?.config) return null;
const { config } = currentDashboardItem;
if (config.type === 'view') {
const { viewType } = config;
return viewType;
}
return config.type;
};
const displayType = getDisplayType();

// Only show export button if the current dashboard item is a chart, matrix or multi-value view AND it is not a drilldown
const canExport =
displayType &&
EXPORTABLE_TYPES.includes(displayType) &&
!urlSearchParams.get(URL_SEARCH_PARAMS.REPORT_DRILLDOWN_ID);

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ import React, { useContext, useRef } from 'react';
import styled from 'styled-components';
import { Typography } from '@material-ui/core';
import { Button as BaseButton, SpinningLoader } from '@tupaia/ui-components';
import { DashboardItemVizTypes, ViewVizTypes } from '../../constants';
import { ViewVizTypes } from '../../constants';
import { Entity } from '../../types';
import { DisplayOptionsSettings, ExportFormatSettings, ExportFormats } from '../ExportSettings';
import {
Expand Down Expand Up @@ -75,14 +75,27 @@ export const ExportDashboardItem = ({ entityName }: { entityName?: Entity['name'
},
];

const { type, viewType } = currentDashboardItem?.config ?? {};
const isChart = type === DashboardItemVizTypes.Chart;
const isChart = currentDashboardItem?.config?.type === 'chart';

// PNG export is not available for matrix reports
const availableExportOptions =
isChart || viewType === ViewVizTypes.MultiValue
? exportOptions
: exportOptions.filter(option => option.value !== ExportFormats.PNG);
const getHasPNGExportOption = () => {
if (!currentDashboardItem?.config) return false;
const { type } = currentDashboardItem?.config;
if (isChart) {
return true;
}
if (type === 'view') {
const { viewType } = currentDashboardItem?.config;
return viewType === ViewVizTypes.MultiValue;
}
return false;
};

const hasPNGExportOption = getHasPNGExportOption();

const availableExportOptions = hasPNGExportOption
? exportOptions
: exportOptions.filter(option => option.value !== ExportFormats.PNG);

return (
<Wrapper>
Expand Down
Loading
Loading