Skip to content

Commit

Permalink
feat(Browsing Issues): implement JQL filters search
Browse files Browse the repository at this point in the history
Search for issues with your own custom JQL!
Click the select over the sidebar and choose one of
your saved JIRA JQL filters. Edit and create new filters!

ISSUES CLOSED: #57, #48, #85
  • Loading branch information
ilya-lopukhin committed Apr 5, 2018
1 parent 4667376 commit 415bb76
Show file tree
Hide file tree
Showing 7 changed files with 244 additions and 106 deletions.
2 changes: 1 addition & 1 deletion app/actions/filters.js
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ export const createFilterRequest = ({ name, jql }) => ({
payload: { name, jql },
});

export const updateFilterRequest = (oldFilter, newJQLString) => ({
export const updateFilterRequest = ({ oldFilter, newJQLString }) => ({
type: types.UPDATE_FILTER_REQUEST,
payload: {
oldFilter,
Expand Down
99 changes: 4 additions & 95 deletions app/containers/Sidebar/IssuesSourcePicker/IssuesSourcePicker.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -21,37 +21,26 @@ import type {

import {
SingleSelect,
Flex,
} from 'components';

import { FieldTextStateless as FieldText } from '@atlaskit/field-text';

import {
getIssuesSourceOptions,
getSelectedSprintOption,
getSprintsOptions,
getIssuesSourceSelectedOption,
getUiState,
} from 'selectors';

import {
issuesActions,
sprintsActions,
filtersActions,
resourcesActions,
uiActions,
} from 'actions';

import InlineDialog from '@atlaskit/inline-dialog';

import Button from '@atlaskit/button';

import * as R from 'ramda';

import {
InputLabel,
IssuesSourceContainer,
} from './styled';
import JQLFilter from './JQLFilter';

import { IssuesSourceContainer } from './styled';

type Props = {
options: Array<any>,
Expand All @@ -61,10 +50,6 @@ type Props = {
projectsFetching: boolean,
sprintsFetching: boolean,
selectedSourceType: string,
newJQLFilterName: string | null,
newJQLFilterValue: string | null,
newJQLFilterErrors: Array<any>,
saveFilterDialogOpen: boolean,
dispatch: Dispatch,
};

Expand All @@ -76,10 +61,6 @@ const IssuesSourcePicker: StatelessFunctionalComponent<Props> = ({
projectsFetching,
sprintsFetching,
selectedSourceType,
newJQLFilterName,
newJQLFilterValue,
newJQLFilterErrors,
saveFilterDialogOpen,
dispatch,
}: Props): Node =>
<IssuesSourceContainer>
Expand Down Expand Up @@ -138,86 +119,14 @@ const IssuesSourcePicker: StatelessFunctionalComponent<Props> = ({
/>
}
{console.log(selectedOption)}
{ (selectedSourceType === 'filter') &&
<Flex column style={{ marginTop: 8 }}>
<FieldText
value={newJQLFilterValue || R.path(['meta', 'filter', 'jql'], selectedOption)}
isInvalid={newJQLFilterErrors.length > 0}
isLabelHidden
invalidMessage={newJQLFilterErrors.join(', ')}
onChange={(e) => {
if (e.target.value === R.path(['meta', 'filter', 'jql'], selectedOption)) {
dispatch(uiActions.setUiState('newJQLFilterValue', null));
} else {
dispatch(uiActions.setUiState('newJQLFilterValue', e.target.value));
}
}}
shouldFitContainer
/>
<Flex justifyContent="flex-end">
<InlineDialog
isOpen={saveFilterDialogOpen}
onClose={() => dispatch(uiActions.setUiState('saveFilterDialogOpen', false))}
content={
<Flex column>
<Flex alignItems="center">
<FieldText
placeholder="New filter name"
label="New filter name"
value={newJQLFilterName}
onChange={e => dispatch(uiActions.setUiState('newJQLFilterName', e.target.value))}
shouldFitContainer
/>
<Button
appearance="link"
onClick={() => dispatch(
filtersActions.createFilterRequest({
name: newJQLFilterName,
jql: newJQLFilterValue,
})
)}
>
Save
</Button>
</Flex>
</Flex>
}
>
<Button
appearance="link"
onClick={() => dispatch(uiActions.setUiState('saveFilterDialogOpen', !saveFilterDialogOpen))}
isDisabled={!newJQLFilterValue}
>
New
</Button>
</InlineDialog>
<Button
appearance="link"
isDisabled={!newJQLFilterValue}
>
Save
</Button>
<Button
appearance="subtle-link"
isDisabled={!newJQLFilterValue}
onClick={() => dispatch(uiActions.setUiState('newJQLFilterValue', null))}
>
Reset
</Button>
</Flex>
</Flex>
}
{selectedSourceType === 'filter' && <JQLFilter selectedFilter={selectedOption} />}
</IssuesSourceContainer>;

function mapStateToProps(state) {
return {
options: getIssuesSourceOptions(state),
selectedOption: getIssuesSourceSelectedOption(state),
selectedSourceType: getUiState('issuesSourceType')(state),
newJQLFilterName: getUiState('newJQLFilterName')(state),
newJQLFilterValue: getUiState('newJQLFilterValue')(state),
newJQLFilterErrors: getUiState('newJQLFilterErrors')(state),
saveFilterDialogOpen: getUiState('saveFilterDialogOpen')(state),

sprintsOptions: getSprintsOptions(state),
selectedSprintOption: getSelectedSprintOption(state),
Expand Down
147 changes: 147 additions & 0 deletions app/containers/Sidebar/IssuesSourcePicker/JQLFilter.jsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,147 @@
// @flow
import React from 'react';
import InlineDialog from '@atlaskit/inline-dialog';
import { connect } from 'react-redux';
import { FieldTextStateless as FieldText } from '@atlaskit/field-text';
import Spinner from '@atlaskit/spinner';
import Button from '@atlaskit/button';
import { Flex } from 'components';

import {
getUiState,
getResourceStatus,
} from 'selectors';

import {
filtersActions,
uiActions,
} from 'actions';

import type {
Dispatch,
SelectedOption,
} from 'types';

import * as R from 'ramda';

import { InputLabel } from './styled';

type Props = {
newJQLFilterName: string | null,
newJQLFilterValue: string | null,
newJQLFilterErrors: Array<any>,
newJQLFilterSaving: boolean,
newJQLFilterAdding: boolean,
saveFilterDialogOpen: boolean,
selectedFilter: SelectedOption,
dispatch: Dispatch,
}

const JQLFilter = ({
newJQLFilterName,
newJQLFilterValue,
newJQLFilterErrors,
newJQLFilterSaving,
newJQLFilterAdding,
saveFilterDialogOpen,
selectedFilter,
dispatch,
}: Props) => (
<Flex column style={{ marginTop: 8 }}>
<FieldText
value={newJQLFilterValue || R.path(['meta', 'filter', 'jql'], selectedFilter)}
isInvalid={newJQLFilterErrors.length > 0}
isLabelHidden
invalidMessage={newJQLFilterErrors.join(', ')}
onChange={(e) => {
dispatch(uiActions.setUiState('newJQLFilterErrors', []));
if (e.target.value === R.path(['meta', 'filter', 'jql'], selectedFilter)) {
dispatch(uiActions.setUiState('newJQLFilterValue', null));
} else {
dispatch(uiActions.setUiState('newJQLFilterValue', e.target.value));
}
}}
shouldFitContainer
/>
<Flex justifyContent="flex-end">
<InlineDialog
isOpen={saveFilterDialogOpen}
onClose={() => dispatch(uiActions.setUiState('saveFilterDialogOpen', false))}
content={
<Flex column>
<InputLabel>New filter name</InputLabel>
<Flex alignItems="center">
<FieldText
placeholder="New filter name"
isLabelHidden
value={newJQLFilterName}
onChange={e => dispatch(uiActions.setUiState('newJQLFilterName', e.target.value))}
shouldFitContainer
/>
<Button
appearance="link"
iconAfter={newJQLFilterAdding ? <Spinner /> : null}
onClick={() => dispatch(
filtersActions.createFilterRequest({
name: newJQLFilterName,
jql: newJQLFilterValue,
}),
)}
>
Save
</Button>
</Flex>
</Flex>
}
>
<Button
appearance="link"
onClick={() => dispatch(uiActions.setUiState('saveFilterDialogOpen', !saveFilterDialogOpen))}
isDisabled={!newJQLFilterValue}
>
New
</Button>
</InlineDialog>
<Button
appearance="link"
onClick={() => dispatch(
filtersActions.updateFilterRequest({
oldFilter: selectedFilter.meta.filter,
newJQLString: newJQLFilterValue,
}),
)}
isDisabled={!newJQLFilterValue}
iconAfter={newJQLFilterSaving ? <Spinner /> : null}
>
Save
</Button>
<Button
appearance="subtle-link"
isDisabled={!newJQLFilterValue}
onClick={() => dispatch(uiActions.setUiState('newJQLFilterValue', null))}
>
Reset
</Button>
</Flex>
</Flex>
);

const connector = connect(
state => ({
newJQLFilterName: getUiState('newJQLFilterName')(state),
newJQLFilterValue: getUiState('newJQLFilterValue')(state),
newJQLFilterSaving: getResourceStatus(
state,
'filters.requests.updateFilter.status',
).pending,
newJQLFilterAdding: getResourceStatus(
state,
'filters.requests.createFilter.status',
).pending,
newJQLFilterErrors: getUiState('newJQLFilterErrors')(state),
saveFilterDialogOpen: getUiState('saveFilterDialogOpen')(state),
}),
dispatch => ({ dispatch }),
);

export default connector(JQLFilter);
Loading

0 comments on commit 415bb76

Please sign in to comment.