Merge pull request #80 from sartography/feature/process-model-search
Feature/process model search
This commit is contained in:
commit
965fa2871e
|
@ -15,15 +15,14 @@ describe('process-groups', () => {
|
|||
cy.createGroup(groupId, groupDisplayName);
|
||||
|
||||
cy.contains('Process Groups').click();
|
||||
cy.contains(groupId);
|
||||
cy.contains(groupId).click();
|
||||
cy.contains(groupDisplayName).click();
|
||||
cy.url().should('include', `process-groups/${groupId}`);
|
||||
cy.contains(`Process Group: ${groupId}`);
|
||||
cy.contains(`Process Group: ${groupDisplayName}`);
|
||||
|
||||
cy.contains('Edit process group').click();
|
||||
cy.get('input[name=display_name]').clear().type(newGroupDisplayName);
|
||||
cy.contains('Submit').click();
|
||||
cy.contains(`Process Group: ${groupId}`);
|
||||
cy.contains(`Process Group: ${newGroupDisplayName}`);
|
||||
|
||||
cy.contains('Edit process group').click();
|
||||
cy.get('input[name=display_name]').should(
|
||||
|
|
|
@ -58,7 +58,7 @@ describe('process-instances', () => {
|
|||
beforeEach(() => {
|
||||
cy.login();
|
||||
cy.navigateToProcessModel(
|
||||
'acceptance-tests-group-one',
|
||||
'Acceptance Tests Group One',
|
||||
'acceptance-tests-model-1'
|
||||
);
|
||||
});
|
||||
|
@ -165,7 +165,8 @@ describe('process-instances', () => {
|
|||
cy.getBySel(`process-instance-status-${processStatus}`).contains(
|
||||
processStatus
|
||||
);
|
||||
cy.get('button[aria-label=Remove]').click();
|
||||
// there should really only be one, but in CI there are sometimes more
|
||||
cy.get('button[aria-label=Remove]:first').click();
|
||||
}
|
||||
});
|
||||
|
||||
|
|
|
@ -10,10 +10,11 @@ describe('process-models', () => {
|
|||
const uuid = () => Cypress._.random(0, 1e6);
|
||||
const id = uuid();
|
||||
const groupId = 'acceptance-tests-group-one';
|
||||
const groupDisplayName = 'Acceptance Tests Group One';
|
||||
const modelDisplayName = `Test Model 2 ${id}`;
|
||||
const newModelDisplayName = `${modelDisplayName} edited`;
|
||||
const modelId = `test-model-2-${id}`;
|
||||
cy.contains(groupId).click();
|
||||
cy.contains(groupDisplayName).click();
|
||||
cy.createModel(groupId, modelId, modelDisplayName);
|
||||
cy.contains(`Process Group: ${groupId}`).click();
|
||||
cy.contains(modelId);
|
||||
|
@ -44,6 +45,7 @@ describe('process-models', () => {
|
|||
const uuid = () => Cypress._.random(0, 1e6);
|
||||
const id = uuid();
|
||||
const groupId = 'acceptance-tests-group-one';
|
||||
const groupDisplayName = 'Acceptance Tests Group One';
|
||||
const modelDisplayName = `Test Model 2 ${id}`;
|
||||
const modelId = `test-model-2-${id}`;
|
||||
|
||||
|
@ -51,7 +53,7 @@ describe('process-models', () => {
|
|||
const dmnFileName = `dmn_test_file_${id}`;
|
||||
const jsonFileName = `json_test_file_${id}`;
|
||||
|
||||
cy.contains(groupId).click();
|
||||
cy.contains(groupDisplayName).click();
|
||||
cy.createModel(groupId, modelId, modelDisplayName);
|
||||
cy.contains(`Process Group: ${groupId}`).click();
|
||||
cy.contains(modelId);
|
||||
|
@ -119,10 +121,11 @@ describe('process-models', () => {
|
|||
const uuid = () => Cypress._.random(0, 1e6);
|
||||
const id = uuid();
|
||||
const groupId = 'acceptance-tests-group-one';
|
||||
const groupDisplayName = 'Acceptance Tests Group One';
|
||||
const modelDisplayName = `Test Model 2 ${id}`;
|
||||
const modelId = `test-model-2-${id}`;
|
||||
cy.contains('Add a process group');
|
||||
cy.contains(groupId).click();
|
||||
cy.contains(groupDisplayName).click();
|
||||
cy.createModel(groupId, modelId, modelDisplayName);
|
||||
|
||||
// seeing if getBySel works better, because we are seeing tests fail in CI
|
||||
|
@ -160,7 +163,17 @@ describe('process-models', () => {
|
|||
});
|
||||
|
||||
it('can paginate items', () => {
|
||||
cy.contains('acceptance-tests-group-one').click();
|
||||
cy.contains('Acceptance Tests Group One').click();
|
||||
cy.basicPaginationTest();
|
||||
});
|
||||
|
||||
it('can allow searching for model', () => {
|
||||
cy.get('[name=process-model-selection]').click();
|
||||
cy.get('[name=process-model-selection]').type('model-3');
|
||||
cy.get(
|
||||
`[aria-label="acceptance-tests-group-one/acceptance-tests-model-3"]`
|
||||
).click();
|
||||
|
||||
cy.contains('Process Instances').click();
|
||||
});
|
||||
});
|
||||
|
|
|
@ -22,12 +22,12 @@ describe('tasks', () => {
|
|||
});
|
||||
|
||||
it('can complete and navigate a form', () => {
|
||||
const groupId = 'acceptance-tests-group-one';
|
||||
const groupDisplayName = 'Acceptance Tests Group One';
|
||||
const modelId = `acceptance-tests-model-2`;
|
||||
const completedTaskClassName = 'completed-task-highlight';
|
||||
const activeTaskClassName = 'active-task-highlight';
|
||||
|
||||
cy.navigateToProcessModel(groupId, modelId);
|
||||
cy.navigateToProcessModel(groupDisplayName, modelId);
|
||||
|
||||
// avoid reloading so we can click on the task link that appears on running the process instance
|
||||
cy.runPrimaryBpmnFile(false);
|
||||
|
@ -66,7 +66,7 @@ describe('tasks', () => {
|
|||
);
|
||||
|
||||
cy.contains('Task: get_user_generated_number_four');
|
||||
cy.navigateToProcessModel(groupId, modelId);
|
||||
cy.navigateToProcessModel(groupDisplayName, modelId);
|
||||
cy.getBySel('process-instance-list-link').click();
|
||||
cy.assertAtLeastOneItemInPaginatedResults();
|
||||
|
||||
|
@ -98,7 +98,7 @@ describe('tasks', () => {
|
|||
);
|
||||
cy.url().should('include', '/tasks');
|
||||
|
||||
cy.navigateToProcessModel(groupId, modelId);
|
||||
cy.navigateToProcessModel(groupDisplayName, modelId);
|
||||
cy.getBySel('process-instance-list-link').click();
|
||||
cy.assertAtLeastOneItemInPaginatedResults();
|
||||
|
||||
|
@ -110,7 +110,7 @@ describe('tasks', () => {
|
|||
|
||||
it('can paginate items', () => {
|
||||
cy.navigateToProcessModel(
|
||||
'acceptance-tests-group-one',
|
||||
'Acceptance Tests Group One',
|
||||
'acceptance-tests-model-2'
|
||||
);
|
||||
|
||||
|
|
|
@ -47,6 +47,10 @@ Cypress.Commands.add('login', (selector, ...args) => {
|
|||
|
||||
Cypress.Commands.add('logout', (selector, ...args) => {
|
||||
cy.getBySel('logout-button').click();
|
||||
|
||||
// otherwise we can click logout, quickly load the next page, and the javascript
|
||||
// doesn't have time to actually sign you out
|
||||
cy.contains('Sign in to your account');
|
||||
});
|
||||
|
||||
Cypress.Commands.add('createGroup', (groupId, groupDisplayName) => {
|
||||
|
@ -58,7 +62,7 @@ Cypress.Commands.add('createGroup', (groupId, groupDisplayName) => {
|
|||
cy.contains('Submit').click();
|
||||
|
||||
cy.url().should('include', `process-groups/${groupId}`);
|
||||
cy.contains(`Process Group: ${groupId}`);
|
||||
cy.contains(`Process Group: ${groupDisplayName}`);
|
||||
});
|
||||
|
||||
Cypress.Commands.add('createModel', (groupId, modelId, modelDisplayName) => {
|
||||
|
@ -83,15 +87,18 @@ Cypress.Commands.add('runPrimaryBpmnFile', (reload = true) => {
|
|||
}
|
||||
});
|
||||
|
||||
Cypress.Commands.add('navigateToProcessModel', (groupId, modelId) => {
|
||||
cy.navigateToAdmin();
|
||||
cy.contains(groupId).click();
|
||||
cy.contains(`Process Group: ${groupId}`);
|
||||
// https://stackoverflow.com/q/51254946/6090676
|
||||
cy.getBySel('process-model-show-link').contains(modelId).click();
|
||||
cy.url().should('include', `process-models/${groupId}/${modelId}`);
|
||||
cy.contains(`Process Model: ${modelId}`);
|
||||
});
|
||||
Cypress.Commands.add(
|
||||
'navigateToProcessModel',
|
||||
(groupDisplayName, modelDisplayName) => {
|
||||
cy.navigateToAdmin();
|
||||
cy.contains(groupDisplayName).click();
|
||||
cy.contains(`Process Group: ${groupDisplayName}`);
|
||||
// https://stackoverflow.com/q/51254946/6090676
|
||||
cy.getBySel('process-model-show-link').contains(modelDisplayName).click();
|
||||
// cy.url().should('include', `process-models/${groupDisplayName}/${modelDisplayName}`);
|
||||
cy.contains(`Process Model: ${modelDisplayName}`);
|
||||
}
|
||||
);
|
||||
|
||||
Cypress.Commands.add('basicPaginationTest', () => {
|
||||
cy.get('#pagination-page-dropdown')
|
||||
|
|
|
@ -12,6 +12,20 @@ test('renders home link', () => {
|
|||
expect(homeElement).toBeInTheDocument();
|
||||
});
|
||||
|
||||
test('renders hotCrumbs', () => {
|
||||
render(
|
||||
<BrowserRouter>
|
||||
<ProcessBreadcrumb
|
||||
hotCrumbs={[['Process Groups', '/admin'], [`Process Group: hey`]]}
|
||||
/>
|
||||
</BrowserRouter>
|
||||
);
|
||||
const homeElement = screen.getByText(/Process Groups/);
|
||||
expect(homeElement).toBeInTheDocument();
|
||||
const nextElement = screen.getByText(/Process Group: hey/);
|
||||
expect(nextElement).toBeInTheDocument();
|
||||
});
|
||||
|
||||
test('renders process group when given processGroupId', async () => {
|
||||
render(
|
||||
<BrowserRouter>
|
||||
|
|
|
@ -1,20 +1,44 @@
|
|||
import { Link } from 'react-router-dom';
|
||||
import Breadcrumb from 'react-bootstrap/Breadcrumb';
|
||||
import { BreadcrumbItem } from '../interfaces';
|
||||
|
||||
type OwnProps = {
|
||||
processModelId?: string;
|
||||
processGroupId?: string;
|
||||
linkProcessModel?: boolean;
|
||||
hotCrumbs?: BreadcrumbItem[];
|
||||
};
|
||||
|
||||
export default function ProcessBreadcrumb({
|
||||
processModelId,
|
||||
processGroupId,
|
||||
hotCrumbs,
|
||||
linkProcessModel = false,
|
||||
}: OwnProps) {
|
||||
let processGroupBreadcrumb = null;
|
||||
let processModelBreadcrumb = null;
|
||||
|
||||
if (hotCrumbs) {
|
||||
const lastItem = hotCrumbs.pop();
|
||||
if (lastItem === undefined) {
|
||||
return null;
|
||||
}
|
||||
const lastCrumb = <Breadcrumb.Item active>{lastItem[0]}</Breadcrumb.Item>;
|
||||
const leadingCrumbLinks = hotCrumbs.map((crumb) => {
|
||||
const valueLabel = crumb[0];
|
||||
const url = crumb[1];
|
||||
return (
|
||||
<Breadcrumb.Item key={valueLabel} linkAs={Link} linkProps={{ to: url }}>
|
||||
{valueLabel}
|
||||
</Breadcrumb.Item>
|
||||
);
|
||||
});
|
||||
return (
|
||||
<Breadcrumb>
|
||||
{leadingCrumbLinks}
|
||||
{lastCrumb}
|
||||
</Breadcrumb>
|
||||
);
|
||||
}
|
||||
if (processModelId) {
|
||||
if (linkProcessModel) {
|
||||
processModelBreadcrumb = (
|
||||
|
|
|
@ -9,3 +9,18 @@ export interface RecentProcessModel {
|
|||
processModelIdentifier: string;
|
||||
processModelDisplayName: string;
|
||||
}
|
||||
|
||||
export interface ProcessGroup {
|
||||
id: string;
|
||||
display_name: string;
|
||||
}
|
||||
|
||||
export interface ProcessModel {
|
||||
id: string;
|
||||
process_group_id: string;
|
||||
display_name: string;
|
||||
primary_file_name: string;
|
||||
}
|
||||
|
||||
// tuple of display value and URL
|
||||
export type BreadcrumbItem = [displayValue: string, url?: string];
|
||||
|
|
|
@ -18,7 +18,6 @@ export default function HomePage() {
|
|||
searchParams,
|
||||
PER_PAGE_FOR_TASKS_ON_HOME_PAGE
|
||||
);
|
||||
console.log('perPageYo', perPage);
|
||||
const setTasksFromResult = (result: any) => {
|
||||
setTasks(result.results);
|
||||
setPagination(result.pagination);
|
||||
|
|
|
@ -1,29 +1,50 @@
|
|||
import { useEffect, useState } from 'react';
|
||||
import { Link, useSearchParams } from 'react-router-dom';
|
||||
import { Button, Table } from 'react-bootstrap';
|
||||
import { Link, useNavigate, useSearchParams } from 'react-router-dom';
|
||||
import { Button, Form, InputGroup, Table } from 'react-bootstrap';
|
||||
import { Typeahead } from 'react-bootstrap-typeahead';
|
||||
import { Option } from 'react-bootstrap-typeahead/types/types';
|
||||
import ProcessBreadcrumb from '../components/ProcessBreadcrumb';
|
||||
import PaginationForTable from '../components/PaginationForTable';
|
||||
import HttpService from '../services/HttpService';
|
||||
import { getPageInfoFromSearchParams } from '../helpers';
|
||||
import { ProcessModel } from '../interfaces';
|
||||
|
||||
// Example process group json
|
||||
// {'admin': False, 'display_name': 'Test Workflows', 'display_order': 0, 'id': 'test_process_group'}
|
||||
// {'process_group_id': 'sure', 'display_name': 'Test Workflows', 'id': 'test_process_group'}
|
||||
export default function ProcessGroupList() {
|
||||
const navigate = useNavigate();
|
||||
const [searchParams] = useSearchParams();
|
||||
|
||||
const [processGroups, setProcessGroups] = useState([]);
|
||||
const [pagination, setPagination] = useState(null);
|
||||
const [processModeleSelectionOptions, setProcessModelSelectionOptions] =
|
||||
useState([]);
|
||||
|
||||
useEffect(() => {
|
||||
const setProcessGroupsFromResult = (result: any) => {
|
||||
setProcessGroups(result.results);
|
||||
setPagination(result.pagination);
|
||||
};
|
||||
const processResultForProcessModels = (result: any) => {
|
||||
const selectionArray = result.results.map((item: any) => {
|
||||
const label = `${item.process_group_id}/${item.id}`;
|
||||
Object.assign(item, { label });
|
||||
return item;
|
||||
});
|
||||
setProcessModelSelectionOptions(selectionArray);
|
||||
};
|
||||
|
||||
const { page, perPage } = getPageInfoFromSearchParams(searchParams);
|
||||
// for browsing
|
||||
HttpService.makeCallToBackend({
|
||||
path: `/process-groups?per_page=${perPage}&page=${page}`,
|
||||
successCallback: setProcessGroupsFromResult,
|
||||
});
|
||||
// for search box
|
||||
HttpService.makeCallToBackend({
|
||||
path: `/process-models?per_page=1000`,
|
||||
successCallback: processResultForProcessModels,
|
||||
});
|
||||
}, [searchParams]);
|
||||
|
||||
const buildTable = () => {
|
||||
|
@ -58,13 +79,16 @@ export default function ProcessGroupList() {
|
|||
let displayText = null;
|
||||
if (processGroups?.length > 0) {
|
||||
displayText = (
|
||||
<PaginationForTable
|
||||
page={page}
|
||||
perPage={perPage}
|
||||
pagination={pagination as any}
|
||||
tableToDisplay={buildTable()}
|
||||
path="/admin/process-groups"
|
||||
/>
|
||||
<>
|
||||
<h3>Browse</h3>
|
||||
<PaginationForTable
|
||||
page={page}
|
||||
perPage={perPage}
|
||||
pagination={pagination as any}
|
||||
tableToDisplay={buildTable()}
|
||||
path="/admin/process-groups"
|
||||
/>
|
||||
</>
|
||||
);
|
||||
} else {
|
||||
displayText = <p>No Groups To Display</p>;
|
||||
|
@ -72,13 +96,48 @@ export default function ProcessGroupList() {
|
|||
return displayText;
|
||||
};
|
||||
|
||||
const processModelSearchArea = () => {
|
||||
const processModelSearchOnChange = (selected: Option[]) => {
|
||||
const processModel = selected[0] as ProcessModel;
|
||||
navigate(
|
||||
`/admin/process-models/${processModel.process_group_id}/${processModel.id}`
|
||||
);
|
||||
};
|
||||
return (
|
||||
<form onSubmit={function hey() {}}>
|
||||
<h3>Search</h3>
|
||||
<Form.Group>
|
||||
<InputGroup>
|
||||
<InputGroup.Text className="text-nowrap">
|
||||
Process Model:{' '}
|
||||
</InputGroup.Text>
|
||||
<Typeahead
|
||||
style={{ width: 500 }}
|
||||
id="process-model-selection"
|
||||
labelKey="label"
|
||||
onChange={processModelSearchOnChange}
|
||||
// for cypress tests since data-qa does not work
|
||||
inputProps={{
|
||||
name: 'process-model-selection',
|
||||
}}
|
||||
options={processModeleSelectionOptions}
|
||||
placeholder="Choose a process model..."
|
||||
/>
|
||||
</InputGroup>
|
||||
</Form.Group>
|
||||
</form>
|
||||
);
|
||||
};
|
||||
|
||||
if (pagination) {
|
||||
return (
|
||||
<>
|
||||
<ProcessBreadcrumb />
|
||||
<ProcessBreadcrumb hotCrumbs={[['Process Groups']]} />
|
||||
<Button href="/admin/process-groups/new">Add a process group</Button>
|
||||
<br />
|
||||
<br />
|
||||
{processModelSearchArea()}
|
||||
<br />
|
||||
{processGroupsDisplayArea()}
|
||||
</>
|
||||
);
|
||||
|
|
|
@ -5,12 +5,13 @@ import ProcessBreadcrumb from '../components/ProcessBreadcrumb';
|
|||
import PaginationForTable from '../components/PaginationForTable';
|
||||
import HttpService from '../services/HttpService';
|
||||
import { getPageInfoFromSearchParams } from '../helpers';
|
||||
import { ProcessGroup } from '../interfaces';
|
||||
|
||||
export default function ProcessGroupShow() {
|
||||
const params = useParams();
|
||||
const [searchParams] = useSearchParams();
|
||||
|
||||
const [processGroup, setProcessGroup] = useState(null);
|
||||
const [processGroup, setProcessGroup] = useState<ProcessGroup | null>(null);
|
||||
const [processModels, setProcessModels] = useState([]);
|
||||
const [pagination, setPagination] = useState(null);
|
||||
|
||||
|
@ -35,14 +36,15 @@ export default function ProcessGroupShow() {
|
|||
}, [params, searchParams]);
|
||||
|
||||
const buildTable = () => {
|
||||
if (processGroup === null) {
|
||||
return null;
|
||||
}
|
||||
const rows = processModels.map((row) => {
|
||||
return (
|
||||
<tr key={(row as any).id}>
|
||||
<td>
|
||||
<Link
|
||||
to={`/admin/process-models/${(processGroup as any).id}/${
|
||||
(row as any).id
|
||||
}`}
|
||||
to={`/admin/process-models/${processGroup.id}/${(row as any).id}`}
|
||||
data-qa="process-model-show-link"
|
||||
>
|
||||
{(row as any).id}
|
||||
|
@ -72,7 +74,12 @@ export default function ProcessGroupShow() {
|
|||
const { page, perPage } = getPageInfoFromSearchParams(searchParams);
|
||||
return (
|
||||
<>
|
||||
<ProcessBreadcrumb processGroupId={(processGroup as any).id} />
|
||||
<ProcessBreadcrumb
|
||||
hotCrumbs={[
|
||||
['Process Groups', '/admin'],
|
||||
[`Process Group: ${processGroup.display_name}`],
|
||||
]}
|
||||
/>
|
||||
<ul>
|
||||
<Stack direction="horizontal" gap={3}>
|
||||
<Button
|
||||
|
|
Loading…
Reference in New Issue