Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
13 changes: 13 additions & 0 deletions src/Project.js
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ const Project = ({
}) => {
const [splitFinalColumns] = useLocalStorage('splitColumns');
const [slim, setSlim] = useLocalStorage('slim');
const [rejected] = useLocalStorage('rejected');

const keyMap = useMemo(
() => ({
Expand Down Expand Up @@ -115,6 +116,18 @@ const Project = ({
slim={slim}
/>
)}
{rejected && (
<Column
title="Rejected"
storyIds={storyIds}
stories={stories}
storyStates={['rejected']}
featureDropState="rejected"
bugDropState="rejected"
choreDropState={null}
slim={slim}
/>
)}
</div>
</ColumnContainer>
</section>
Expand Down
29 changes: 28 additions & 1 deletion src/ProjectContainer.js
Original file line number Diff line number Diff line change
Expand Up @@ -38,10 +38,37 @@ function reducer(state, action) {
case 'FETCH_BLOCKERS_SUCCESS': {
const storiesWithBlockers = { ...state.stories };

const matchStoryId = str =>
(str && str.match(/([0-9]){9}$/)[0]) || 'Unknown';

const getStoryBlockers = blockers => {
return blockers
.map(b => {
const storyId = matchStoryId(b.description);
const scopedStory = storiesWithBlockers[storyId];
return scopedStory
? {
id: scopedStory.id,
url: scopedStory.url,
name: scopedStory.name,
resolved: b.resolved
}
: {
id: storyId,
url: `https://www.pivotaltracker.com/story/show/${storyId}`,
name: '[Out of current iteration blocker]',
resolved: b.resolved
};
})
.filter(b => !b.resolved);
};

payload.forEach(story => {
if (story.blockers.length > 0) {
if (storiesWithBlockers[story.id]) {
storiesWithBlockers[story.id].blockers = story.blockers;
storiesWithBlockers[story.id].blockers = getStoryBlockers(
story.blockers
);
} else {
console.warn(
'Recieved blockers for unknow story, with id',
Expand Down
25 changes: 25 additions & 0 deletions src/Settings.js
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,9 @@ const Settings = () => {
);

const [slim, setSlim] = useLocalStorage('slim');
const [showRejectedColumn, setShowRejectedColumn] = useLocalStorage(
'rejected'
);

return (
<div>
Expand Down Expand Up @@ -60,6 +63,28 @@ const Settings = () => {
</p>
</div>
</div>

<div className="media">
<div className="media-left">
<label className="switch">
<input
type="checkbox"
checked={showRejectedColumn ? 'checked' : false}
onChange={() => setShowRejectedColumn(!showRejectedColumn)}
/>
<div></div>
</label>
</div>

<div className="media-content content" style={{ marginRight: '1rem' }}>
<h4>Rejected state column</h4>
<p>
Enable this setting to have Rejected state column.
<br />
Current state: <b>{showRejectedColumn ? 'enabled' : 'disabled'}</b>
</p>
</div>
</div>
</div>
);
};
Expand Down
5 changes: 2 additions & 3 deletions src/Story.js
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
import React from 'react';
import { Owners } from './Owners';
import {
BlockedTag,
BugTag,
ChoreTag,
EstimateTag,
Expand All @@ -10,6 +9,7 @@ import {
SlimTag
} from './Tags';
import { hasUnresolvedBlockers } from './FilterContainer';
import { StoryBlockers } from './StoryBlockers';

const renderTypeTag = type => {
switch (type) {
Expand Down Expand Up @@ -84,10 +84,9 @@ function Story({
<div className="media-right">
<Owners ownerIds={ownerIds} />
</div>

<BlockedTag visible={hasUnresolvedBlockers(blockers)} />
</div>
)}
<StoryBlockers blockers={blockers} />
</div>
</div>
);
Expand Down
20 changes: 5 additions & 15 deletions src/Story.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -142,31 +142,21 @@ describe('Story', () => {
expect(within(getByTestId('owners')).getByText('WT')).toBeInTheDocument();
});

describe('blocked tag', () => {
it('renders visible blocked tag if blocked', () => {
describe('story blockers', () => {
it('renders blockers if blocked', () => {
const { queryByTestId } = renderSubject({
blockers: [{ resolved: false }]
});

expect(queryByTestId('blocked-tag')).toBeVisible();
expect(queryByTestId('story-blockers')).toBeInTheDocument();
});

it('renders invisible blocked tag if not blocked', () => {
const { queryByTestId } = renderSubject({
blockers: [{ resolved: true }]
});

expect(queryByTestId('blocked-tag')).toBeInTheDocument();
expect(queryByTestId('blocked-tag')).not.toBeVisible();
});

it('renders invisible blocked tag if no blockers', () => {
it('does not render blockers if no blockers', () => {
const { queryByTestId } = renderSubject({
blockers: []
});

expect(queryByTestId('blocked-tag')).toBeInTheDocument();
expect(queryByTestId('blocked-tag')).not.toBeVisible();
expect(queryByTestId('story-blockers')).not.toBeInTheDocument();
});
});

Expand Down
33 changes: 33 additions & 0 deletions src/StoryBlockers.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
import React from 'react';
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
import { faBan } from '@fortawesome/free-solid-svg-icons';

function StoryBlockers({ blockers }) {
if (!blockers.length) return null;

return (
<div data-testid="story-blockers" className="content is-small">
<hr style={{ margin: '0px 0 10px' }} />
<span className="has-text-danger">
<FontAwesomeIcon icon={faBan} size="1x" /> Blockers:
</span>

<ul style={{ marginTop: '10px' }}>
{blockers.map((b, i) => (
<li key={`blocker-${i}`}>
<a
href={b.url}
className="is-small has-text-grey-dark has-hover-underline"
target="_blank"
rel="noopener noreferrer"
>
{b.name}
</a>
</li>
))}
</ul>
</div>
);
}

export { StoryBlockers };
50 changes: 39 additions & 11 deletions src/__snapshots__/ProjectContainer.test.js.snap
Original file line number Diff line number Diff line change
Expand Up @@ -146,13 +146,48 @@ exports[`ProjectContainer renders fetched and normalized data 1`] = `
</div>
</div>
</div>
</div>
<div
class="content is-small"
data-testid="story-blockers"
>
<hr
style="margin: 0px 0px 10px;"
/>
<span
class="tag is-warning"
data-testid="blocked-tag"
style="position: absolute; top: 10px; right: 0px; height: 1.75em; font-size: .65rem; opacity: 1; transition: opacity 1s; border-top-right-radius: 0; border-bottom-right-radius: 0;"
class="has-text-danger"
>
Blocked
<svg
aria-hidden="true"
class="svg-inline--fa fa-ban fa-w-16 fa-1x "
data-icon="ban"
data-prefix="fas"
focusable="false"
role="img"
viewBox="0 0 512 512"
xmlns="http://www.w3.org/2000/svg"
>
<path
d="M256 8C119.034 8 8 119.033 8 256s111.034 248 248 248 248-111.034 248-248S392.967 8 256 8zm130.108 117.892c65.448 65.448 70 165.481 20.677 235.637L150.47 105.216c70.204-49.356 170.226-44.735 235.638 20.676zM125.892 386.108c-65.448-65.448-70-165.481-20.677-235.637L361.53 406.784c-70.203 49.356-170.226 44.736-235.638-20.676z"
fill="currentColor"
/>
</svg>
Blockers:
</span>
<ul
style="margin-top: 10px;"
>
<li>
<a
class="is-small has-text-grey-dark has-hover-underline"
href="https://www.pivotaltracker.com/story/show/Unknown"
rel="noopener noreferrer"
target="_blank"
>
[Out of current iteration blocker]
</a>
</li>
</ul>
</div>
</div>
</div>
Expand Down Expand Up @@ -245,13 +280,6 @@ exports[`ProjectContainer renders fetched and normalized data 1`] = `
</div>
</div>
</div>
<span
class="tag is-warning"
data-testid="blocked-tag"
style="position: absolute; top: 10px; right: 0px; height: 1.75em; font-size: .65rem; opacity: 0; transition: opacity 1s; border-top-right-radius: 0; border-bottom-right-radius: 0;"
>
Blocked
</span>
</div>
</div>
</div>
Expand Down