import { flatten, isEqual, map, uniq, xor } from 'lodash'; import { $q } from 'ngimport'; import React from 'react'; import type { Option } from 'react-select'; import type { IAccount } from './AccountService'; import { AccountService } from './AccountService'; import type { IFormInputProps } from '../presentation/forms/inputs'; import { ReactSelectInput } from '../presentation/forms/inputs/ReactSelectInput'; import { SelectInput } from '../presentation/forms/inputs/SelectInput'; import { createFakeReactSyntheticEvent } from '../presentation/forms/inputs/utils'; export interface IAccountSelectInputProps extends IFormInputProps { accounts: IAccount[] | string[]; provider: string; readOnly?: boolean; renderFilterableSelectThreshold?: number; } export interface IAccountSelectInputState { mergedAccounts: string[]; primaryAccounts: string[]; secondaryAccounts: string[]; } const isExpression = (account: string) => !!account && account.includes('${'); export class AccountSelectInput extends React.Component { public static defaultProps = { renderFilterableSelectThreshold: 6, }; public state: IAccountSelectInputState = { mergedAccounts: [], primaryAccounts: [], secondaryAccounts: [], }; private groupAccounts = (accounts: IAccount[] | string[]) => { const { name, value, onChange, provider } = this.props; if (!accounts || !accounts.length) { return; } if (isExpression(value)) { return; } const accountsAreObjects = Boolean((accounts[0] as IAccount).name); let getAccountDetails = $q.when([]); if (provider) { getAccountDetails = AccountService.getAllAccountDetailsForProvider(provider); } if (!provider && accountsAreObjects) { const providers = uniq(map(accounts as IAccount[], 'type')); getAccountDetails = $q .all(providers.map((p) => AccountService.getAllAccountDetailsForProvider(p))) .then((details) => flatten(details)); } getAccountDetails.then((details) => { const accountNames: string[] = accountsAreObjects ? map(accounts as IAccount[], 'name') : (accounts as any); let mergedAccounts = accountNames; let primaryAccounts: string[] = []; let secondaryAccounts: string[] = []; if (accountNames) { primaryAccounts = accountNames.sort(); } if (accountNames && accountNames.length && details.length) { primaryAccounts = accountNames .filter((account) => { return details.some((detail) => detail.name === account && detail.primaryAccount); }) .sort(); secondaryAccounts = xor(accountNames, primaryAccounts).sort(); mergedAccounts = flatten([primaryAccounts, secondaryAccounts]); } if (!mergedAccounts.includes(value)) { onChange(createFakeReactSyntheticEvent({ value: '', name })); } this.setState({ mergedAccounts, primaryAccounts, secondaryAccounts }); }); }; public componentDidMount() { this.groupAccounts(this.props.accounts); } public componentWillReceiveProps(nextProps: IAccountSelectInputProps) { if (!isEqual(nextProps.accounts, this.props.accounts)) { this.groupAccounts(nextProps.accounts); } } public render() { const { value, readOnly } = this.props; const { mergedAccounts } = this.state; const { primaryAccounts, secondaryAccounts } = this.state; const showSeparator = primaryAccounts.length > 0 && secondaryAccounts.length > 0; const options: Array> = [{ label: 'Select...', value: '', disabled: true }]; options.push(...primaryAccounts.map((a) => ({ label: a, value: a }))); if (showSeparator) { options.push({ label: '---------------', value: '-', disabled: true }); } options.push(...secondaryAccounts.map((a) => ({ label: a, value: a }))); if (isExpression(value)) { return (
Resolved at runtime from expression: {value}
); } if (readOnly) { return (

{value}

); } const { renderFilterableSelectThreshold, ...otherProps } = this.props; const useSimpleSelect = mergedAccounts.length < renderFilterableSelectThreshold; if (useSimpleSelect) { // When this select is used in Angular, the event is accessed in a $timeout, and React will have // re-rendered the input, setting its value (the event.target.value) back to the previous value // This can go away once we're out of Angular land. const handleChange = (e: React.ChangeEvent) => { e.persist(); this.props.onChange(e); }; return ( ); } else { return ( ); } } }