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

PSP-6824 Property management view #3504

Merged
merged 18 commits into from
Oct 7, 2023
Merged
47 changes: 47 additions & 0 deletions source/frontend/src/components/common/MultiselectTextList.test.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
import Api_TypeCode from '@/models/api/TypeCode';
import { render, RenderOptions } from '@/utils/test-utils';

import { IMultiselectTextListProps, MultiselectTextList } from './MultiselectTextList';

const mockOptions: Api_TypeCode<string>[] = [
{ id: 'FOO', description: 'Foo' },
{ id: 'BAR', description: 'Bar' },
{ id: 'BAZ', description: 'Baz' },
];

describe('MultiselectTextList component', () => {
const setup = (
renderOptions?: RenderOptions & { props?: Partial<IMultiselectTextListProps> },
) => {
renderOptions = renderOptions ?? {};
const utils = render(
<MultiselectTextList
{...renderOptions.props}
values={renderOptions.props?.values ?? []}
displayValue={renderOptions.props?.displayValue ?? 'description'}
/>,
{
...renderOptions,
},
);

return { ...utils };
};

afterEach(() => {
jest.clearAllMocks();
});

it('renders as expected', () => {
const { asFragment } = setup();
expect(asFragment()).toMatchSnapshot();
});

it('displays existing values if they exist', () => {
const { getByText } = setup({ props: { values: mockOptions } });

expect(getByText(mockOptions[0].description!)).toBeVisible();
expect(getByText(mockOptions[1].description!)).toBeVisible();
expect(getByText(mockOptions[2].description!)).toBeVisible();
});
});
53 changes: 53 additions & 0 deletions source/frontend/src/components/common/MultiselectTextList.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
import Multiselect from 'multiselect-react-dropdown';
import React from 'react';

export interface IMultiselectTextListProps {
/** Values to show in the list */
values: any[];

/**
* Whether 'values' is an array of objects or not (true by default).
* Make it false to display flat array of string or number Ex. ['Test1', 'Test2']
*/
isObject?: boolean;

/** Property name in the object to display */
displayValue?: string;
}

export const MultiselectTextList: React.FC<IMultiselectTextListProps> = ({
values,
isObject = true,
displayValue,
}) => {
return (
<Multiselect
disable
disablePreSelectedValues
hidePlaceholder
placeholder=""
isObject={isObject}
selectedValues={values}
displayValue={displayValue ?? 'description'}
style={readOnlyStyle}
/>
);
};

const readOnlyStyle = {
multiselectContainer: {
opacity: 1,
},
searchBox: {
border: 'none',
padding: 0,
},
chips: {
opacity: 1,
background: '#F2F2F2',
borderRadius: '4px',
color: 'black',
fontSize: '16px',
marginRight: '1em',
},
};
2 changes: 1 addition & 1 deletion source/frontend/src/components/common/TabView.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ const StyledTabWrapper = styled.div`
position: relative;
}
border-radius: 0 0.4rem 0.4rem 0.4rem;
height: calc(100% - 2.4rem); // substract nav height
height: calc(100% - 2.5rem); // subtract nav height
overflow-y: auto;
background-color: ${props => props.theme.css.filterBackgroundColor};
}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP

exports[`MultiselectTextList component renders as expected 1`] = `
<DocumentFragment>
<div
class="Toastify"
/>
<div />
<div>
<div
class="multiselect-container multiSelectContainer disable_ms "
id="multiselectContainerReact"
style="opacity: 1;"
>
<div
class="search-wrapper searchWrapper "
style="padding: 0px;"
>
<input
autocomplete="off"
class="searchBox "
disabled=""
id="search_input"
name="search_name_input"
placeholder=""
type="text"
value=""
/>
</div>
<div
class="optionListContainer displayNone"
>
<ul
class="optionContainer"
>
<span
class="notFound"
>
No Options Available
</span>
</ul>
</div>
</div>
</div>
</DocumentFragment>
`;
Original file line number Diff line number Diff line change
Expand Up @@ -245,7 +245,7 @@ exports[`MapSelectorContainer component renders as expected when provided no pro

.c0 .tab-content {
border-radius: 0 0.4rem 0.4rem 0.4rem;
height: calc(100% - 2.4rem);
height: calc(100% - 2.5rem);
overflow-y: auto;
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -397,7 +397,7 @@ exports[`AddLeaseContainer component renders as expected 1`] = `

.c10 .tab-content {
border-radius: 0 0.4rem 0.4rem 0.4rem;
height: calc(100% - 2.4rem);
height: calc(100% - 2.5rem);
overflow-y: auto;
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -321,7 +321,7 @@ exports[`AcquisitionView component renders as expected 1`] = `

.c11 .tab-content {
border-radius: 0 0.4rem 0.4rem 0.4rem;
height: calc(100% - 2.4rem);
height: calc(100% - 2.5rem);
overflow-y: auto;
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -250,7 +250,7 @@ exports[`AcquisitionProperties component renders as expected 1`] = `

.c0 .tab-content {
border-radius: 0 0.4rem 0.4rem 0.4rem;
height: calc(100% - 2.4rem);
height: calc(100% - 2.5rem);
overflow-y: auto;
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -402,7 +402,7 @@ exports[`AddAcquisitionContainer component renders as expected 1`] = `

.c11 .tab-content {
border-radius: 0 0.4rem 0.4rem 0.4rem;
height: calc(100% - 2.4rem);
height: calc(100% - 2.5rem);
overflow-y: auto;
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -319,7 +319,7 @@ exports[`AddAcquisitionForm component renders as expected 1`] = `

.c8 .tab-content {
border-radius: 0 0.4rem 0.4rem 0.4rem;
height: calc(100% - 2.4rem);
height: calc(100% - 2.5rem);
overflow-y: auto;
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -225,7 +225,7 @@ exports[`AcquisitionFileTabs component matches snapshot 1`] = `

.c0 .tab-content {
border-radius: 0 0.4rem 0.4rem 0.4rem;
height: calc(100% - 2.4rem);
height: calc(100% - 2.5rem);
overflow-y: auto;
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -242,7 +242,7 @@ exports[`Project Tabs component matches snapshot 1`] = `

.c0 .tab-content {
border-radius: 0 0.4rem 0.4rem 0.4rem;
height: calc(100% - 2.4rem);
height: calc(100% - 2.5rem);
overflow-y: auto;
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@ export const PropertyManagementTabView: React.FunctionComponent<IPropertyManagem
<LoadingBackdrop show={loading} parentScreen={true} />

<PropertyManagementDetailContainer
property={property}
propertyId={property.id}
View={PropertyManagementDetailView}
setEditManagementState={setEditManagementState}
/>
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,87 @@
import { FormikProps } from 'formik';
import { createMemoryHistory } from 'history';
import { forwardRef } from 'react';

import { mockLookups } from '@/mocks/lookups.mock';
import { getMockApiPropertyManagement } from '@/mocks/propertyManagement.mock';
import { Api_PropertyManagement } from '@/models/api/Property';
import { lookupCodesSlice } from '@/store/slices/lookupCodes';
import { render, RenderOptions } from '@/utils/test-utils';

import {
IPropertyManagementDetailContainerProps,
PropertyManagementDetailContainer,
} from './PropertyManagementDetailContainer';
import { IPropertyManagementDetailViewProps } from './PropertyManagementDetailView';

const history = createMemoryHistory();
const storeState = {
[lookupCodesSlice.name]: { lookupCodes: mockLookups },
};

const mockGetApi = {
error: undefined,
response: undefined as Api_PropertyManagement | undefined,
execute: jest.fn(),
loading: false,
};

jest.mock('@/hooks/repositories/usePropertyManagementRepository', () => ({
usePropertyManagementRepository: () => {
return {
getPropertyManagement: mockGetApi,
};
},
}));

describe('PropertyManagementDetailContainer component', () => {
let viewProps: IPropertyManagementDetailViewProps;

const View = forwardRef<FormikProps<any>, IPropertyManagementDetailViewProps>((props, ref) => {
viewProps = props;
return <></>;
});

const setEditManagementState = jest.fn();

const setup = (
renderOptions?: RenderOptions & { props?: Partial<IPropertyManagementDetailContainerProps> },
) => {
renderOptions = renderOptions ?? {};
const utils = render(
<PropertyManagementDetailContainer
{...renderOptions.props}
propertyId={renderOptions.props?.propertyId ?? 1}
setEditManagementState={setEditManagementState}
View={View}
/>,
{
...renderOptions,
store: storeState,
history,
},
);

return {
...utils,
};
};

afterEach(() => {
jest.clearAllMocks();
});

it('fetches property management info from the api', () => {
mockGetApi.execute.mockResolvedValue(getMockApiPropertyManagement(1));
setup({ props: { propertyId: 1 } });
expect(mockGetApi.execute).toBeCalledWith(1);
});

it('passes property management info to child view as prop', async () => {
const apiManagement = getMockApiPropertyManagement(1);
mockGetApi.response = apiManagement;
setup({ props: { propertyId: 1 } });
expect(viewProps.isLoading).toBe(false);
expect(viewProps.propertyManagement).toBe(apiManagement);
});
});
Original file line number Diff line number Diff line change
@@ -1,45 +1,33 @@
import { useCallback, useEffect, useState } from 'react';
import { useCallback, useEffect } from 'react';

import { usePropertyManagementRepository } from '@/hooks/repositories/usePropertyManagementRepository';
import { Api_Property, Api_PropertyManagement } from '@/models/api/Property';

import { EditManagementState } from '../../../../PropertyViewSelector';
import { IPropertyManagementDetailViewProps } from './PropertyManagementDetailView';

interface IPropertyManagementDetailContainerProps {
property: Api_Property;
export interface IPropertyManagementDetailContainerProps {
propertyId: number;
setEditManagementState: (state: EditManagementState | null) => void;
View: React.FC<IPropertyManagementDetailViewProps>;
}

export const PropertyManagementDetailContainer: React.FunctionComponent<
IPropertyManagementDetailContainerProps
> = ({ property, setEditManagementState, View }) => {
const [propertyManagement, setPropertyManagement] = useState<Api_PropertyManagement>({
id: property.id ?? 0,
rowVersion: null,
managementPurposes: [],
additionalDetails: null,
isUtilitiesPayable: null,
isTaxesPayable: null,
isLeaseActive: false,
isLeaseExpired: false,
leaseExpiryDate: null,
});

> = ({ propertyId, setEditManagementState, View }) => {
const {
getPropertyManagement: { execute: getPropertyManagement, loading },
getPropertyManagement: {
execute: getPropertyManagement,
response: propertyManagement,
loading,
},
} = usePropertyManagementRepository();

const fetchPropertyManagement = useCallback(async () => {
if (!property.id) {
if (!propertyId) {
return;
}
const response = await getPropertyManagement(property.id);
if (response) {
setPropertyManagement(response);
}
}, [getPropertyManagement, property.id]);
await getPropertyManagement(propertyId);
}, [getPropertyManagement, propertyId]);

useEffect(() => {
fetchPropertyManagement();
Expand All @@ -48,7 +36,7 @@ export const PropertyManagementDetailContainer: React.FunctionComponent<
return (
<View
isLoading={loading}
propertyManagement={propertyManagement}
propertyManagement={propertyManagement ?? null}
setEditManagementState={setEditManagementState}
/>
);
Expand Down
Loading
Loading