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

Extend Component class instead of using x-interaction #238

Merged
merged 13 commits into from
Feb 4, 2019
Merged
Show file tree
Hide file tree
Changes from 9 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
9 changes: 5 additions & 4 deletions __tests__/snapshots.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ const renderer = require('react-test-renderer');
const fs = require('fs');
const path = require('path');
const glob = require('glob');
const { h } = require('../packages/x-engine');

const {packages} = require('../monorepo.json');

Expand All @@ -16,16 +17,16 @@ for(const pkg of packageDirs) {
const storiesDir = path.resolve(pkgDir, 'stories');

if(fs.existsSync(storiesDir)) {
const { package: pkg, stories, component } = require(storiesDir);
const { presets = {default: {}} } = require(pkgDir);
const { package: pkg, stories, component: Component } = require(storiesDir);
const { presets = { default: {} } } = require(pkgDir);
const name = path.basename(pkg.name);

describe(pkg.name, () => {
for (const { title, data } of stories) {
for (const [ preset, options ] of Object.entries(presets)) {
for (const [preset, options] of Object.entries(presets)) {
it(`renders a ${preset} ${title} ${name}`, () => {
const props = { ...data, ...options };
const tree = renderer.create(component(props)).toJSON();
const tree = renderer.create(h(Component, props)).toJSON();
expect(tree).toMatchSnapshot();
});
}
Expand Down
68 changes: 34 additions & 34 deletions components/x-topic-search/__tests__/x-topic-search.test.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -14,10 +14,14 @@ const alreadyFollowedTopics = [
{ uuid: 'Cat-Food-id', name: 'Cat Food' },
{ uuid: 'Cat-Toys-id', name: 'Cat Toys' }
];
const buildSearchUrl = term => `${apiUrl}?count=${maxSuggestions}&partial=${term}`;

describe('x-topic-search', () => {
const waitForApiResponse = () => new Promise(resolve => { setTimeout(resolve, 500); });
const buildSearchUrl = term => `${apiUrl}?count=${maxSuggestions}&partial=${term}`;
const enterSearchTerm = searchTerm => {
target.find('input').simulate('input', { target: { value: searchTerm }});

return new Promise(resolve => { setTimeout(resolve, 400); });
};
let target;

beforeEach(() => {
Expand Down Expand Up @@ -45,53 +49,54 @@ describe('x-topic-search', () => {
});

describe('given inputted text is shorter than minSearchLength', () => {
it('should not render result', () => {
const wordLessThanMin = searchTerm.slice(0, minSearchLength - 1);
const apiUrlWithResults = buildSearchUrl(wordLessThanMin);
const apiUrlWithResults = buildSearchUrl('a');

fetchMock.get(apiUrlWithResults, []);
fetchMock.get(apiUrlWithResults, []);

target.find('input').simulate('input', { target: { value: wordLessThanMin }});
beforeEach(() => {
return enterSearchTerm('a');
});

return waitForApiResponse().then(() => {
expect(fetchMock.called(apiUrlWithResults)).toBe(false);
expect(target.render().children('div')).toHaveLength(1);
});
it('does not make a request to the api or render any result', () => {
expect(fetchMock.called(apiUrlWithResults)).toBe(false);
expect(target.render().children('div')).toHaveLength(1);
});
});

describe('given searchTerm which has some topic suggestions to follow', () => {
const apiUrlWithResults = buildSearchUrl(searchTerm);
const topicSuggestions = [
const unfollowedTopicSuggestions = [
{ id: 'Dog-House-id', prefLabel: 'Dog House', url: 'Dog-House-url' },
{ id: 'Dog-Food-id', prefLabel: 'Dog Food', url: 'Dog-Food-url' },
{ id: 'Dog-Toys-id', prefLabel: 'Dog Toys', url: 'Dog-Toys-url' }
];

fetchMock.get(apiUrlWithResults, topicSuggestions);
fetchMock.get(apiUrlWithResults, unfollowedTopicSuggestions);

beforeEach(() => {
target.find('input').simulate('input', { target: { value: searchTerm } });

return waitForApiResponse();
return enterSearchTerm(searchTerm);
});

it('should render topics list with follow button', () => {
it('requests the topic suggestions with count set to maxSuggestions', () => {
expect(fetchMock.called(apiUrlWithResults)).toBe(true);
});

it('renders no more than the max number of suggestions', () => {
expect(target.render().children('div')).toHaveLength(2);
expect(target.render().find('li')).toHaveLength(maxSuggestions);
});

it('renders links and follow buttons for each suggestion', () => {
const suggestionsList = target.render().find('li');

expect(suggestionsList).toHaveLength(maxSuggestions);

topicSuggestions.forEach((topic, index) => {
unfollowedTopicSuggestions.forEach((topic, index) => {
const suggestion = suggestionsList.eq(index);

expect(suggestion.find('a').text()).toEqual(topic.prefLabel);
expect(suggestion.find('a').attr('href')).toEqual(topic.url);
expect(suggestion.find('button')).toHaveLength(1);
});
});
})
});

describe('given searchTerm which has no topic suggestions to follow', () => {
Expand All @@ -100,12 +105,10 @@ describe('x-topic-search', () => {
fetchMock.get(apiUrlNoResults, []);

beforeEach(() => {
target.find('input').simulate('input', { target: { value: searchTermNoResult } });

return waitForApiResponse();
return enterSearchTerm(searchTermNoResult);
});

it('should render no topic message', () => {
it('requests from the api and renders the no matching topics message', () => {
expect(fetchMock.called(apiUrlNoResults)).toBe(true);

const resultContainer = target.render().children('div').eq(1);
Expand All @@ -115,7 +118,7 @@ describe('x-topic-search', () => {
});
});

describe('given searchTerm which all the topics has been followed', () => {
describe('given searchTerm which results in all suggestions already followed', () => {
const apiUrlAllFollowed = buildSearchUrl(searchTermAllFollowed);

fetchMock.get(apiUrlAllFollowed, alreadyFollowedTopics.map(topic => ({
Expand All @@ -125,22 +128,19 @@ describe('x-topic-search', () => {
})));

beforeEach(() => {
target.find('input').simulate('input', { target: { value: searchTermAllFollowed } });

return waitForApiResponse();
return enterSearchTerm(searchTermAllFollowed);
});

it('should render already followed message with name of the topics', () => {
it('requests the suggestions from the api', () => {
expect(fetchMock.called(apiUrlAllFollowed)).toBe(true);
});

it('renders the "already followed" message with names of the topics', () => {
const resultContainer = target.render().children('div').eq(1);

expect(resultContainer).toHaveLength(1);
expect(resultContainer.text())
.toMatch(
`You already follow ${alreadyFollowedTopics[0].name}, ${alreadyFollowedTopics[1].name} and ${alreadyFollowedTopics[2].name}`
);
.toMatch(`You already follow ${alreadyFollowedTopics[0].name}, ${alreadyFollowedTopics[1].name} and ${alreadyFollowedTopics[2].name}`);
});
});

});
1 change: 0 additions & 1 deletion components/x-topic-search/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,6 @@
"dependencies": {
"@financial-times/x-engine": "file:../../packages/x-engine",
"@financial-times/x-follow-button": "0.0.11",
"@financial-times/x-interaction": "^1.0.0-beta.6",
"classnames": "^2.2.6",
"debounce-promise": "^3.1.0"
},
Expand Down
54 changes: 21 additions & 33 deletions components/x-topic-search/src/ResultContainer.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -9,49 +9,37 @@ import NoSuggestions from './NoSuggestions';
const arrayToSentence = followedSuggestions => {
const topicsLength = followedSuggestions.length;

if (topicsLength === 1) {
return <b>{ followedSuggestions[0].prefLabel }</b>;
} else {
return followedSuggestions
.map((topic, index) => {
if (index === topicsLength - 1) {
// the last topic
return <span key={ topic.id }>and <b>{ topic.prefLabel }</b></span>
} else if (index === topicsLength - 2) {
// one before the last topic
return <span key={ topic.id }><b>{ topic.prefLabel }</b> </span>;
} else {
return <span key={ topic.id }><b>{ topic.prefLabel }</b>, </span>;
}
})
}

return followedSuggestions.map((topic, index) => (
<span key={ topic.id }>
{topicsLength > 1 && index === topicsLength - 1 && ' and '}
<b>{topic.prefLabel}</b>
{index < topicsLength - 2 && ', '}
dan-searle marked this conversation as resolved.
Show resolved Hide resolved
</span>
));
};


export default ({ result, searchTerm, csrfToken, followedTopicIds }) => {

const hasFollowedSuggestions = result.followedSuggestions.length > 0;
const hasUnfollowedSuggestions = result.unfollowedSuggestions.length > 0;
export default ({ followedSuggestions, searchTerm, csrfToken, followedTopicIds, unfollowedSuggestions }) => {
const hasFollowedSuggestions = followedSuggestions.length > 0;
const hasUnfollowedSuggestions = unfollowedSuggestions.length > 0;

return (
<div className={ classNames(styles['result-container']) }>

{ hasUnfollowedSuggestions &&
{hasUnfollowedSuggestions &&
<SuggestionList
suggestions={ result.unfollowedSuggestions }
searchTerm={ searchTerm }
csrfToken={ csrfToken }
followedTopicIds={ followedTopicIds }/> }
suggestions={ unfollowedSuggestions }
searchTerm={ searchTerm }
csrfToken={ csrfToken }
followedTopicIds={ followedTopicIds }
/>}

{ !hasUnfollowedSuggestions && hasFollowedSuggestions &&
{!hasUnfollowedSuggestions && hasFollowedSuggestions &&
<div className={ classNames(styles["all-followed"]) } aria-live="polite">
You already follow { arrayToSentence(result.followedSuggestions) }
</div> }

{ !hasUnfollowedSuggestions && !hasFollowedSuggestions &&
<NoSuggestions searchTerm={ searchTerm }/> }
You already follow { arrayToSentence(followedSuggestions) }
</div>}

{!hasUnfollowedSuggestions && !hasFollowedSuggestions &&
<NoSuggestions searchTerm={ searchTerm }/>}
</div>
);
};
Loading