Skip to content

Commit

Permalink
migrate projectDownload files to TypeScript, some missing typings, li…
Browse files Browse the repository at this point in the history
…nter fixes,

define LangString as alias of string
  • Loading branch information
magicznyleszek committed Jan 17, 2025
1 parent c18c3ba commit 14ffed8
Show file tree
Hide file tree
Showing 21 changed files with 627 additions and 322 deletions.
21 changes: 19 additions & 2 deletions jsapp/js/actions.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -96,8 +96,18 @@ interface GetExportDefinition extends Function {
}

interface GetExportCompletedDefinition extends Function {
(response: any): void;
listen: (callback: (response: any) => void) => Function;
(response: ExportDataResponse): void;
listen: (callback: (response: ExportDataResponse) => void) => Function;
}

interface GetExportSettingsDefinition extends Function {
(assetUid: string, options: {preselectLastSettings: boolean}): void;
completed: GetExportSettingsCompletedDefinition;
failed: GenericFailedDefinition;
}
interface GetExportSettingsCompletedDefinition extends Function {
(response: PaginatedResponse<ExportSetting>, passData: {}): void;
listen: (callback: (response: PaginatedResponse<ExportSetting>, passData?: {preselectLastSettings?: boolean}) => void) => Function;
}

interface TableUpdateSettingsDefinition extends Function {
Expand Down Expand Up @@ -256,6 +266,13 @@ export namespace actions {
const media: object;
const exports: {
getExport: GetExportDefinition;
getExports: GenericDefinition;
createExport: GenericDefinition;
deleteExport: GenericDefinition;
getExportSettings: GetExportSettingsDefinition;
updateExportSetting: GenericDefinition;
createExportSetting: GenericDefinition;
deleteExportSetting: GenericDefinition;
};
const dataShare: object;
}
4 changes: 2 additions & 2 deletions jsapp/js/assetUtils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -534,7 +534,7 @@ export function renderQuestionTypeIcon(
*/
export function injectSupplementalRowsIntoListOfRows(
asset: AssetResponse,
rows: string[],
rows: Set<string> | Array<string>,
) {
if (asset.content?.survey === undefined) {
throw new Error('Asset has no content');
Expand Down Expand Up @@ -713,7 +713,7 @@ export function getAssetSubmissionProcessingUrl(
) {
const processingUrl = getAssetProcessingUrl(assetUid);
if (processingUrl) {
return processingUrl + '?submission=' + submission
return processingUrl + '?submission=' + submission;
}
return undefined;
}
Expand Down
2 changes: 1 addition & 1 deletion jsapp/js/components/common/checkbox.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ interface CheckboxProps {
* `evt.stopPropagation()` and be happy.
*/
onClick?: (evt: React.MouseEvent<HTMLInputElement> | React.TouchEvent<HTMLInputElement>) => void;
label?: string;
label?: React.ReactNode;
/** Only needed if checkbox is in submittable form. */
name?: string;
'data-cy'?: string;
Expand Down
2 changes: 1 addition & 1 deletion jsapp/js/components/common/multiCheckbox.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@ interface MultiCheckboxProps {
* A MultiCheckbox generic component.
* Use optional `bem.MultiCheckbox__wrapper` to display a frame around it.
*/
export default function MultiCheckbox (props: MultiCheckboxProps) {
export default function MultiCheckbox(props: MultiCheckboxProps) {
function onChange(itemIndex: number, isChecked: boolean) {
const updatedList = props.items;
updatedList[itemIndex].checked = isChecked;
Expand Down
2 changes: 1 addition & 1 deletion jsapp/js/components/formSubScreens.js
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ const DataTable = React.lazy(() =>
);
const ProjectDownloads = React.lazy(() =>
import(
/* webpackPrefetch: true */ 'js/components/projectDownloads/projectDownloads'
/* webpackPrefetch: true */ 'js/components/projectDownloads/ProjectDownloads'
)
);
const FormGallery = React.lazy(() =>
Expand Down
6 changes: 3 additions & 3 deletions jsapp/js/components/locking/lockingConstants.ts
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,7 @@ export interface LockingRestrictionDefinition {
}

// all restrictions for questions and choices
export const QUESTION_RESTRICTIONS = [
export const QUESTION_RESTRICTIONS: LockingRestrictionDefinition[] = [
{name: LockingRestrictionName.choice_add, label: t('Add choice to question')},
{name: LockingRestrictionName.choice_delete, label: t('Remove choice from question')},
{name: LockingRestrictionName.choice_label_edit, label: t('Edit choice labels')},
Expand All @@ -62,7 +62,7 @@ export const QUESTION_RESTRICTIONS = [
];

// all restrictions for groups
export const GROUP_RESTRICTIONS = [
export const GROUP_RESTRICTIONS: LockingRestrictionDefinition[] = [
{name: LockingRestrictionName.group_delete, label: t('Delete entire group')},
{name: LockingRestrictionName.group_label_edit, label: t('Edit group labels')},
{name: LockingRestrictionName.group_question_add, label: t('Add question to group')},
Expand All @@ -74,7 +74,7 @@ export const GROUP_RESTRICTIONS = [
];

// all restrictions for form
export const FORM_RESTRICTIONS = [
export const FORM_RESTRICTIONS: LockingRestrictionDefinition[] = [
{name: LockingRestrictionName.form_appearance, label: t('Change form appearance')},
{name: LockingRestrictionName.form_meta_edit, label: t('Change form meta questions')},
{name: LockingRestrictionName.form_replace, label: t('Replace whole form')},
Expand Down
2 changes: 2 additions & 0 deletions jsapp/js/components/locking/lockingUtils.mocks.ts
Original file line number Diff line number Diff line change
Expand Up @@ -102,6 +102,8 @@ const minimalAssetResponse = {
'csv_legacy': 'http://kc.kobo.local/zefir/exports/aBcDe12345/csv/',
'zip_legacy': 'http://kc.kobo.local/zefir/exports/aBcDe12345/zip/',
'kml_legacy': 'http://kc.kobo.local/zefir/exports/aBcDe12345/kml/',
'geojson': 'http://kc.kobo.local/zefir/exports/aBcDe12345/geojson/',
'spss_labels': 'http://kc.kobo.local/zefir/exports/aBcDe12345/spss/',
'xls': 'http://kc.kobo.local/zefir/reports/aBcDe12345/export.xlsx',
'csv': 'http://kc.kobo.local/zefir/reports/aBcDe12345/export.csv',
},
Expand Down
Original file line number Diff line number Diff line change
@@ -1,40 +1,62 @@
// Libraries
import React from 'react';
import autoBind from 'react-autobind';
import bem from 'js/bem';

// Partial components
import ExportTypeSelector from 'js/components/projectDownloads/ExportTypeSelector';
import Button from 'js/components/common/button';

// Stores, hooks and utilities
import {actions} from 'js/actions';
import {downloadUrl} from 'js/utils';
import {
EXPORT_STATUSES,
DEFAULT_EXPORT_SETTINGS,
} from 'js/components/projectDownloads/exportsConstants';
import {getContextualDefaultExportFormat} from 'js/components/projectDownloads/exportsUtils';
import exportsStore from 'js/components/projectDownloads/exportsStore';
import ExportTypeSelector from 'js/components/projectDownloads/exportTypeSelector';
import ExportFetcher from 'js/components/projectDownloads/exportFetcher';
import Button from 'js/components/common/button';

// Constants and types
import {
ExportStatusName,
DEFAULT_EXPORT_SETTINGS,
type ExportTypeDefinition,
} from 'js/components/projectDownloads/exportsConstants';
import type {AssetResponse, ExportDataResponse} from 'jsapp/js/dataInterface';

interface AnonymousExportsProps {
asset: AssetResponse;
}

interface AnonymousExportsState {
selectedExportType: ExportTypeDefinition;
isPending: boolean;
exportUrl: string | null;
}

/**
* A compontent that ROUTES.FORM_DOWNLOADS route is displayint for not logged in
* users. It allows to select an export type and download a file.
* @prop {object} asset
*/
export default class AnonymousExports extends React.Component {
constructor(props){
export default class AnonymousExports extends React.Component<
AnonymousExportsProps,
AnonymousExportsState
> {
constructor(props: AnonymousExportsProps){
super(props);
this.state = {
selectedExportType: exportsStore.getExportType(),
isPending: false,
exportUrl: null,
};
this.unlisteners = [];
autoBind(this);
}

private unlisteners: Function[] = [];
private exportFetcher?: ExportFetcher;

componentDidMount() {
this.unlisteners.push(
exportsStore.listen(this.onExportsStoreChange),
actions.exports.createExport.completed.listen(this.onCreateExportCompleted),
actions.exports.getExport.completed.listen(this.onGetExportCompleted),
exportsStore.listen(this.onExportsStoreChange.bind(this), this),
actions.exports.createExport.completed.listen(this.onCreateExportCompleted.bind(this)),
actions.exports.getExport.completed.listen(this.onGetExportCompleted.bind(this)),
);
}

Expand All @@ -49,19 +71,22 @@ export default class AnonymousExports extends React.Component {
});
}

onCreateExportCompleted(exportData) {
onCreateExportCompleted(exportData: ExportDataResponse) {
this.fetchExport(exportData.uid);
}

onGetExportCompleted(exportData) {
onGetExportCompleted(exportData: ExportDataResponse) {
this.checkExportFetcher(exportData.uid, exportData.status);

if (exportData.status === EXPORT_STATUSES.complete) {
if (exportData.status === ExportStatusName.complete) {
this.setState({
isPending: false,
exportUrl: exportData.result,
}, () => {
if (this.state.exportUrl !== null) {
downloadUrl(this.state.exportUrl);
}
});
downloadUrl(this.state.exportUrl);
}
}

Expand Down Expand Up @@ -90,19 +115,19 @@ export default class AnonymousExports extends React.Component {
}
}

checkExportFetcher(exportUid, exportStatus) {
checkExportFetcher(exportUid: string, exportStatus: ExportStatusName) {
if (
exportStatus !== EXPORT_STATUSES.error &&
exportStatus !== EXPORT_STATUSES.complete &&
!this.fetchIntervalId
exportStatus !== ExportStatusName.error &&
exportStatus !== ExportStatusName.complete &&
!this.exportFetcher
) {
this.exportFetcher = new ExportFetcher(this.props.asset.uid, exportUid);
}

// clean up after it is completed
if (
exportStatus === EXPORT_STATUSES.error ||
exportStatus === EXPORT_STATUSES.complete
exportStatus === ExportStatusName.error ||
exportStatus === ExportStatusName.complete
) {
if (this.exportFetcher) {
this.exportFetcher.stop();
Expand All @@ -111,7 +136,7 @@ export default class AnonymousExports extends React.Component {
}
}

fetchExport(exportUid) {
fetchExport(exportUid: string) {
actions.exports.getExport(this.props.asset.uid, exportUid);
}

Expand Down
Original file line number Diff line number Diff line change
@@ -1,27 +1,37 @@
import React from 'react';
import autoBind from 'react-autobind';
import Select from 'react-select';
import bem from 'js/bem';
import {EXPORT_TYPES} from 'js/components/projectDownloads/exportsConstants';
import {EXPORT_TYPES, type ExportTypeDefinition} from 'js/components/projectDownloads/exportsConstants';
import exportsStore from 'js/components/projectDownloads/exportsStore';

interface ExportTypeSelectorProps {
disabled?: boolean;
/** Hides legacy options */
noLegacy?: boolean;
}

interface ExportTypeSelectorState {
selectedExportType: ExportTypeDefinition;
}

/**
* This is a selector that is handling the currently selected export type and
* is storing it in exportsStore.
* @prop {boolean} [disabled]
* @prop {boolean} [noLegacy] hides legacy options
*/
export default class ExportTypeSelector extends React.Component {
constructor(props){
export default class ExportTypeSelector extends React.Component<
ExportTypeSelectorProps,
ExportTypeSelectorState
> {
constructor(props: ExportTypeSelectorProps) {
super(props);
this.state = {selectedExportType: exportsStore.getExportType()};
this.unlisteners = [];
autoBind(this);
}

private unlisteners: Function[] = [];

componentDidMount() {
this.unlisteners.push(
exportsStore.listen(this.onExportsStoreChange),
exportsStore.listen(this.onExportsStoreChange.bind(this), this),
);
}

Expand All @@ -33,13 +43,17 @@ export default class ExportTypeSelector extends React.Component {
this.setState({selectedExportType: exportsStore.getExportType()});
}

onSelectedExportTypeChange(newValue) {
exportsStore.setExportType(newValue);
onSelectedExportTypeChange(newValue: ExportTypeDefinition | null) {
// It's not really possible to have `null` here, as Select requires a value
// to always be set.
if (newValue !== null) {
exportsStore.setExportType(newValue);
}
}

render() {
// make xls topmost (as most popular)
const exportTypesOptions = [
const exportTypesOptions: ExportTypeDefinition[] = [
EXPORT_TYPES.xls,
EXPORT_TYPES.csv,
EXPORT_TYPES.geojson,
Expand All @@ -60,10 +74,10 @@ export default class ExportTypeSelector extends React.Component {
{t('Select export type')}
</bem.ProjectDownloads__title>

<Select
<Select<ExportTypeDefinition>
value={this.state.selectedExportType}
options={exportTypesOptions}
onChange={this.onSelectedExportTypeChange}
onChange={this.onSelectedExportTypeChange.bind(this)}
className='kobo-select'
classNamePrefix='kobo-select'
menuPlacement='auto'
Expand Down
Loading

0 comments on commit 14ffed8

Please sign in to comment.