decidim-comments 0.0.6 → 0.0.7
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/README.md +33 -0
- data/app/assets/javascripts/decidim/comments/bundle.js +44 -44
- data/app/assets/javascripts/decidim/comments/bundle.js.map +1 -1
- data/app/assets/javascripts/decidim/comments/comments.js.erb +2 -0
- data/app/frontend/application/{apollo_client.js → apollo_client.ts} +5 -5
- data/app/frontend/application/application.component.test.tsx +36 -0
- data/app/frontend/application/application.component.tsx +37 -0
- data/app/frontend/application/icon.component.test.tsx +49 -0
- data/app/frontend/application/icon.component.tsx +35 -0
- data/app/frontend/comments/{add_comment_form.component.test.jsx → add_comment_form.component.test.tsx} +98 -92
- data/app/frontend/comments/{add_comment_form.component.jsx → add_comment_form.component.tsx} +152 -153
- data/app/frontend/comments/{comment.component.test.jsx → comment.component.test.tsx} +59 -71
- data/app/frontend/comments/{comment.component.jsx → comment.component.tsx} +114 -116
- data/app/frontend/comments/comment_order_selector.component.test.tsx +21 -0
- data/app/frontend/comments/comment_order_selector.component.tsx +88 -0
- data/app/frontend/comments/comment_thread.component.test.tsx +65 -0
- data/app/frontend/comments/comment_thread.component.tsx +70 -0
- data/app/frontend/comments/{comments.component.test.jsx → comments.component.test.tsx} +38 -81
- data/app/frontend/comments/{comments.component.jsx → comments.component.tsx} +49 -63
- data/app/frontend/comments/down_vote_button.component.test.tsx +39 -0
- data/app/frontend/comments/down_vote_button.component.tsx +89 -0
- data/app/frontend/comments/up_vote_button.component.test.tsx +39 -0
- data/app/frontend/comments/up_vote_button.component.tsx +89 -0
- data/app/frontend/comments/vote_button.component.tsx +36 -0
- data/app/frontend/comments/{vote_button_component.test.jsx → vote_button_component.test.tsx} +16 -20
- data/app/frontend/entry.ts +19 -0
- data/app/frontend/{comments → fragments}/add_comment_form_commentable.fragment.graphql +1 -1
- data/app/frontend/{comments → fragments}/add_comment_form_session.fragment.graphql +1 -1
- data/app/frontend/{comments → fragments}/comment.fragment.graphql +3 -1
- data/app/frontend/{comments → fragments}/comment_data.fragment.graphql +6 -3
- data/app/frontend/{comments → fragments}/comment_thread.fragment.graphql +3 -1
- data/app/frontend/{comments/down_vote.fragment.graphql → fragments/down_vote_button.fragment.graphql} +2 -2
- data/app/frontend/{comments/up_vote.fragment.graphql → fragments/up_vote_button.fragment.graphql} +2 -2
- data/app/frontend/{comments/add_comment_form.mutation.graphql → mutations/add_comment.mutation.graphql} +3 -1
- data/app/frontend/{comments → mutations}/down_vote.mutation.graphql +3 -1
- data/app/frontend/{comments → mutations}/up_vote.mutation.graphql +3 -1
- data/app/frontend/{comments → queries}/comments.query.graphql +4 -1
- data/app/frontend/support/{asset_url.js → asset_url.ts} +1 -1
- data/app/frontend/support/{generate_comments_data.js → generate_comments_data.ts} +11 -6
- data/app/frontend/support/{generate_user_data.js → generate_user_data.ts} +2 -2
- data/app/frontend/support/{generate_user_group_data.js → generate_user_group_data.ts} +2 -2
- data/app/frontend/support/graphql_transformer.js +32 -0
- data/app/frontend/support/load_translations.ts +44 -0
- data/app/frontend/support/{require_all.js → require_all.ts} +1 -1
- data/app/frontend/support/{resolve_graphql_query.js → resolve_graphql_query.ts} +7 -7
- data/app/frontend/support/schema.ts +119 -0
- data/config/locales/eu.yml +29 -5
- metadata +49 -51
- data/app/frontend/application/application.component.jsx +0 -37
- data/app/frontend/application/application.component.test.jsx +0 -33
- data/app/frontend/application/icon.component.jsx +0 -26
- data/app/frontend/application/icon.component.test.jsx +0 -53
- data/app/frontend/comments/comment_order_selector.component.jsx +0 -72
- data/app/frontend/comments/comment_order_selector.component.test.jsx +0 -20
- data/app/frontend/comments/comment_thread.component.jsx +0 -75
- data/app/frontend/comments/comment_thread.component.test.jsx +0 -83
- data/app/frontend/comments/down_vote_button.component.jsx +0 -98
- data/app/frontend/comments/down_vote_button.component.test.jsx +0 -48
- data/app/frontend/comments/featured_comment.component.jsx +0 -23
- data/app/frontend/comments/featured_comment.component.test.jsx +0 -15
- data/app/frontend/comments/up_vote_button.component.jsx +0 -98
- data/app/frontend/comments/up_vote_button.component.test.jsx +0 -48
- data/app/frontend/comments/vote_button.component.jsx +0 -32
- data/app/frontend/entry.js +0 -17
- data/app/frontend/entry.test.js +0 -31
- data/app/frontend/support/load_translations.js +0 -23
- data/app/frontend/support/stub_component.js +0 -29
@@ -0,0 +1,36 @@
|
|
1
|
+
import * as React from "react";
|
2
|
+
import Icon from "../application/icon.component";
|
3
|
+
|
4
|
+
interface VoteButtonProps {
|
5
|
+
buttonClassName: string;
|
6
|
+
iconName: string;
|
7
|
+
votes: number;
|
8
|
+
voteAction?: () => void;
|
9
|
+
disabled?: boolean;
|
10
|
+
selectedClass?: string;
|
11
|
+
}
|
12
|
+
|
13
|
+
const VoteButton: React.SFC<VoteButtonProps> = ({
|
14
|
+
buttonClassName,
|
15
|
+
iconName,
|
16
|
+
votes,
|
17
|
+
voteAction,
|
18
|
+
disabled,
|
19
|
+
selectedClass,
|
20
|
+
}) => (
|
21
|
+
<button
|
22
|
+
className={`${buttonClassName} ${selectedClass}`}
|
23
|
+
onClick={voteAction}
|
24
|
+
disabled={disabled}
|
25
|
+
>
|
26
|
+
<Icon name={iconName} iconExtraClassName="icon--small" />
|
27
|
+
{` ${votes}`}
|
28
|
+
</button>
|
29
|
+
);
|
30
|
+
|
31
|
+
VoteButton.defaultProps = {
|
32
|
+
selectedClass: "selected",
|
33
|
+
disabled: false,
|
34
|
+
};
|
35
|
+
|
36
|
+
export default VoteButton;
|
data/app/frontend/comments/{vote_button_component.test.jsx → vote_button_component.test.tsx}
RENAMED
@@ -1,43 +1,39 @@
|
|
1
|
-
|
2
|
-
import
|
3
|
-
|
4
|
-
import VoteButton from
|
5
|
-
import Icon from '../application/icon.component';
|
6
|
-
|
7
|
-
import stubComponent from '../support/stub_component';
|
1
|
+
import { shallow } from "enzyme";
|
2
|
+
import * as React from "react";
|
3
|
+
import Icon from "../application/icon.component";
|
4
|
+
import VoteButton from "./vote_button.component";
|
8
5
|
|
9
6
|
describe("<VoteButton />", () => {
|
10
|
-
const voteAction =
|
11
|
-
stubComponent(Icon);
|
7
|
+
const voteAction: jasmine.Spy = jasmine.createSpy("voteAction");
|
12
8
|
|
13
9
|
it("should render the number of votes passed as a prop", () => {
|
14
10
|
const wrapper = shallow(<VoteButton votes={10} buttonClassName="vote-button" iconName="vote-icon" voteAction={voteAction} />);
|
15
|
-
expect(wrapper.find(
|
11
|
+
expect(wrapper.find("button").text()).toMatch(/10/);
|
16
12
|
});
|
17
13
|
|
18
14
|
it("should render a button with the given buttonClassName", () => {
|
19
15
|
const wrapper = shallow(<VoteButton votes={10} buttonClassName="vote-button" iconName="vote-icon" voteAction={voteAction} />);
|
20
|
-
expect(wrapper.find(
|
16
|
+
expect(wrapper.find("button.vote-button").exists()).toBeTruthy();
|
21
17
|
});
|
22
18
|
|
23
19
|
it("should render a Icon component with the correct name prop", () => {
|
24
20
|
const wrapper = shallow(<VoteButton votes={10} buttonClassName="vote-button" iconName="vote-icon" voteAction={voteAction} />);
|
25
|
-
expect(wrapper.find(Icon)
|
21
|
+
expect(wrapper.find(Icon).prop("name")).toEqual("vote-icon");
|
26
22
|
});
|
27
23
|
|
28
24
|
it("should call the voteAction prop on click", () => {
|
29
25
|
const wrapper = shallow(<VoteButton votes={10} buttonClassName="vote-button" iconName="vote-icon" voteAction={voteAction} />);
|
30
|
-
wrapper.find(
|
31
|
-
expect(voteAction).
|
26
|
+
wrapper.find("button").simulate("click");
|
27
|
+
expect(voteAction).toHaveBeenCalled();
|
32
28
|
});
|
33
29
|
|
34
30
|
it("should disable the button based on the disabled prop", () => {
|
35
|
-
const wrapper = shallow(<VoteButton votes={10} buttonClassName="vote-button" iconName="vote-icon" voteAction={voteAction} disabled />);
|
36
|
-
expect(wrapper.find(
|
37
|
-
})
|
31
|
+
const wrapper = shallow(<VoteButton votes={10} buttonClassName="vote-button" iconName="vote-icon" voteAction={voteAction} disabled={true} />);
|
32
|
+
expect(wrapper.find("button").props()).toHaveProperty("disabled");
|
33
|
+
});
|
38
34
|
|
39
35
|
it("should render a button with the given selectedClass", () => {
|
40
|
-
const wrapper = shallow(<VoteButton votes={10} buttonClassName="vote-button" iconName="vote-icon" voteAction={voteAction} disabled selectedClass="is-vote-selected" />);
|
41
|
-
expect(wrapper.find(
|
42
|
-
})
|
36
|
+
const wrapper = shallow(<VoteButton votes={10} buttonClassName="vote-button" iconName="vote-icon" voteAction={voteAction} disabled={true} selectedClass="is-vote-selected" />);
|
37
|
+
expect(wrapper.find(".is-vote-selected").exists()).toBeTruthy();
|
38
|
+
});
|
43
39
|
});
|
@@ -0,0 +1,19 @@
|
|
1
|
+
import * as React from "react";
|
2
|
+
import * as ReactDOM from "react-dom";
|
3
|
+
|
4
|
+
import Comments, { CommentsApplicationProps } from "./comments/comments.component";
|
5
|
+
import loadTranslations from "./support/load_translations";
|
6
|
+
|
7
|
+
window.DecidimComments = window.DecidimComments || {};
|
8
|
+
|
9
|
+
window.DecidimComments.renderCommentsComponent = (nodeId: string, props: CommentsApplicationProps) => {
|
10
|
+
let node = window.$(`#${nodeId}`)[0];
|
11
|
+
|
12
|
+
ReactDOM.render(
|
13
|
+
React.createElement(Comments, props),
|
14
|
+
node,
|
15
|
+
);
|
16
|
+
};
|
17
|
+
|
18
|
+
// Load component locales from yaml files
|
19
|
+
loadTranslations();
|
@@ -1,3 +1,6 @@
|
|
1
|
+
#import "../fragments/up_vote_button.fragment.graphql"
|
2
|
+
#import "../fragments/down_vote_button.fragment.graphql"
|
3
|
+
|
1
4
|
fragment CommentData on Comment {
|
2
5
|
id
|
3
6
|
sgid
|
@@ -12,6 +15,6 @@ fragment CommentData on Comment {
|
|
12
15
|
acceptsNewComments
|
13
16
|
alignment
|
14
17
|
alreadyReported
|
15
|
-
...
|
16
|
-
...
|
17
|
-
}
|
18
|
+
...UpVoteButton
|
19
|
+
...DownVoteButton
|
20
|
+
}
|
@@ -1,7 +1,9 @@
|
|
1
|
+
#import "../fragments/comment_thread.fragment.graphql"
|
2
|
+
|
1
3
|
mutation addComment($commentableId: String!, $commentableType: String!, $body: String!, $alignment: Int, $userGroupId: ID) {
|
2
4
|
commentable(id: $commentableId, type: $commentableType) {
|
3
5
|
addComment(body: $body, alignment: $alignment, userGroupId: $userGroupId) {
|
4
6
|
...CommentThread
|
5
7
|
}
|
6
8
|
}
|
7
|
-
}
|
9
|
+
}
|
@@ -1,3 +1,6 @@
|
|
1
|
+
#import "../fragments/add_comment_form_session.fragment.graphql"
|
2
|
+
#import "../fragments/comment_thread.fragment.graphql"
|
3
|
+
#import "../fragments/add_comment_form_commentable.fragment.graphql"
|
1
4
|
query GetComments($commentableId: String!, $commentableType: String!, $orderBy: String) {
|
2
5
|
session {
|
3
6
|
user {
|
@@ -17,4 +20,4 @@ query GetComments($commentableId: String!, $commentableType: String!, $orderBy:
|
|
17
20
|
}
|
18
21
|
...AddCommentFormCommentable
|
19
22
|
}
|
20
|
-
}
|
23
|
+
}
|
@@ -1,4 +1,6 @@
|
|
1
|
-
import {
|
1
|
+
import { date, image, lorem, name, random } from "faker/locale/en";
|
2
|
+
|
3
|
+
import { CommentFragment } from "../support/schema";
|
2
4
|
|
3
5
|
/**
|
4
6
|
* Generate random comment data to emulate a database real content
|
@@ -6,16 +8,17 @@ import { random, name, date, image } from 'faker/locale/en';
|
|
6
8
|
* @returns {Object[]} - An array of objects representing comments data
|
7
9
|
*/
|
8
10
|
const generateCommentsData = (num = 1) => {
|
9
|
-
let commentsData = [];
|
11
|
+
let commentsData: CommentFragment[] = [];
|
10
12
|
|
11
13
|
for (let idx = 0; idx < num; idx += 1) {
|
12
14
|
commentsData.push({
|
13
15
|
id: random.uuid(),
|
14
|
-
|
16
|
+
type: "Decidim::Comments::Comment",
|
17
|
+
body: lorem.words(),
|
15
18
|
createdAt: date.past().toISOString(),
|
16
19
|
author: {
|
17
20
|
name: name.findName(),
|
18
|
-
avatarUrl: image.imageUrl()
|
21
|
+
avatarUrl: image.imageUrl(),
|
19
22
|
},
|
20
23
|
hasComments: false,
|
21
24
|
comments: [],
|
@@ -24,8 +27,10 @@ const generateCommentsData = (num = 1) => {
|
|
24
27
|
upVotes: random.number(),
|
25
28
|
upVoted: false,
|
26
29
|
downVotes: random.number(),
|
27
|
-
downVoted: false
|
28
|
-
|
30
|
+
downVoted: false,
|
31
|
+
sgid: random.uuid(),
|
32
|
+
alreadyReported: false,
|
33
|
+
});
|
29
34
|
}
|
30
35
|
|
31
36
|
return commentsData;
|
@@ -1,4 +1,4 @@
|
|
1
|
-
import { name } from
|
1
|
+
import { name } from "faker/locale/en";
|
2
2
|
|
3
3
|
/**
|
4
4
|
* Generate random user data to emulate a database real content
|
@@ -6,7 +6,7 @@ import { name } from 'faker/locale/en';
|
|
6
6
|
*/
|
7
7
|
const generateUserData = () => {
|
8
8
|
return {
|
9
|
-
name: name.findName()
|
9
|
+
name: name.findName(),
|
10
10
|
};
|
11
11
|
};
|
12
12
|
|
@@ -1,4 +1,4 @@
|
|
1
|
-
import {
|
1
|
+
import { company, random } from "faker/locale/en";
|
2
2
|
|
3
3
|
/**
|
4
4
|
* Generate random user group data to emulate a database real content
|
@@ -7,7 +7,7 @@ import { random, company } from 'faker/locale/en';
|
|
7
7
|
const generateUserGrouprData = () => {
|
8
8
|
return {
|
9
9
|
id: random.uuid(),
|
10
|
-
name: company.companyName()
|
10
|
+
name: company.companyName(),
|
11
11
|
};
|
12
12
|
};
|
13
13
|
|
@@ -0,0 +1,32 @@
|
|
1
|
+
/* eslint-env node */
|
2
|
+
const gql = require('graphql-tag');
|
3
|
+
|
4
|
+
// Takes `source` (the source GraphQL query string)
|
5
|
+
// and `doc` (the parsed GraphQL document) and tacks on
|
6
|
+
// the imported definitions.
|
7
|
+
const expandImports = (source) => {
|
8
|
+
const lines = source.split('\n');
|
9
|
+
let outputCode = "";
|
10
|
+
|
11
|
+
lines.some((line) => {
|
12
|
+
if (line[0] === '#' && line.slice(1).split(' ')[0] === 'import') {
|
13
|
+
const importFile = line.slice(1).split(' ')[1];
|
14
|
+
const parseDocument = `require(${importFile})`;
|
15
|
+
const appendDef = `doc.definitions = doc.definitions.concat(${parseDocument}.definitions);`;
|
16
|
+
outputCode += `${appendDef}\n`;
|
17
|
+
}
|
18
|
+
return (line.length !== 0 && line[0] !== '#');
|
19
|
+
});
|
20
|
+
|
21
|
+
return outputCode;
|
22
|
+
}
|
23
|
+
|
24
|
+
module.exports = {
|
25
|
+
process(src) {
|
26
|
+
const doc = gql`${src}`;
|
27
|
+
const outputCode = `var doc = ${JSON.stringify(doc)};`;
|
28
|
+
const importOutputCode = expandImports(src, doc);
|
29
|
+
|
30
|
+
return `${outputCode}\n${importOutputCode}\nmodule.exports = doc;`;
|
31
|
+
}
|
32
|
+
};
|
@@ -0,0 +1,44 @@
|
|
1
|
+
/* eslint-disable no-param-reassign */
|
2
|
+
import requireAll from "./require_all";
|
3
|
+
|
4
|
+
const { I18n } = require("react-i18nify");
|
5
|
+
|
6
|
+
/**
|
7
|
+
* Load components translations from yaml files and import them into
|
8
|
+
* react-i18ify system so they can be used via `I18n.t` method.
|
9
|
+
* @returns {Void} - Nothing
|
10
|
+
*/
|
11
|
+
const loadTranslations = () => {
|
12
|
+
const translationsContext = (<any> require).context("../../../config/locales/", true, /\.yml$/);
|
13
|
+
const translationFiles = requireAll(translationsContext);
|
14
|
+
|
15
|
+
const translations = translationsContext.keys().reduce((acc: any, key: string, index: number) => {
|
16
|
+
const match = key.match(/\.\/(.*)\.yml/);
|
17
|
+
|
18
|
+
if (match) {
|
19
|
+
let locale = match[1];
|
20
|
+
acc[locale] = translationFiles[index][locale].decidim;
|
21
|
+
}
|
22
|
+
|
23
|
+
return acc;
|
24
|
+
}, {});
|
25
|
+
|
26
|
+
I18n.setTranslations(translations);
|
27
|
+
};
|
28
|
+
|
29
|
+
/**
|
30
|
+
* Load components translations from a locale files and import them into
|
31
|
+
* react-i18ify system so they can be used via `I18n.t` method.
|
32
|
+
* @returns {Void} - Nothing
|
33
|
+
*/
|
34
|
+
export const loadLocaleTranslations = (locale: string) => {
|
35
|
+
const translationFile = require(`./../../../config/locales/${locale}.yml`);
|
36
|
+
const translations = Object.keys(translationFile).reduce((acc: any, key: string) => {
|
37
|
+
acc[locale] = translationFile[locale].decidim;
|
38
|
+
return acc;
|
39
|
+
}, {});
|
40
|
+
|
41
|
+
I18n.setTranslations(translations);
|
42
|
+
};
|
43
|
+
|
44
|
+
export default loadTranslations;
|
@@ -3,7 +3,7 @@
|
|
3
3
|
* @param {Object} requireContext - A webpack require context
|
4
4
|
* @returns {Object[]} - An array of webpack modules
|
5
5
|
*/
|
6
|
-
const requireAll = (requireContext) => {
|
6
|
+
const requireAll = (requireContext: any) => {
|
7
7
|
return requireContext.keys().map(requireContext);
|
8
8
|
};
|
9
9
|
|
@@ -1,5 +1,5 @@
|
|
1
|
-
import graphql, { filter } from
|
2
|
-
|
1
|
+
import graphql, { filter } from "graphql-anywhere";
|
2
|
+
|
3
3
|
/**
|
4
4
|
* A simple resolver which returns object properties to easily
|
5
5
|
* traverse a graphql response
|
@@ -7,9 +7,9 @@ import graphql, { filter } from 'graphql-anywhere';
|
|
7
7
|
* @param {Object} root - An object
|
8
8
|
* @returns {any} - An object's property value
|
9
9
|
*/
|
10
|
-
const resolver = (fieldName, root) => root[fieldName];
|
10
|
+
const resolver = (fieldName: string, root: any) => root[fieldName];
|
11
11
|
|
12
|
-
/**
|
12
|
+
/**
|
13
13
|
* A helper function to mock a graphql api request and return its
|
14
14
|
* result. The result can be filtered by the same query so it just
|
15
15
|
* returns a data subset.
|
@@ -17,7 +17,7 @@ const resolver = (fieldName, root) => root[fieldName];
|
|
17
17
|
* @param {options} options - An object with optional options
|
18
18
|
* @returns {Object} - The result of the query filtered or not
|
19
19
|
*/
|
20
|
-
const resolveGraphQLQuery = (document, options = {}) => {
|
20
|
+
const resolveGraphQLQuery = (document: any, options: any = {}) => {
|
21
21
|
const { filterResult, rootValue, context, variables } = options;
|
22
22
|
|
23
23
|
let result = graphql(
|
@@ -25,13 +25,13 @@ const resolveGraphQLQuery = (document, options = {}) => {
|
|
25
25
|
document,
|
26
26
|
rootValue,
|
27
27
|
context,
|
28
|
-
variables
|
28
|
+
variables,
|
29
29
|
);
|
30
30
|
|
31
31
|
if (filterResult) {
|
32
32
|
return filter(document, result);
|
33
33
|
}
|
34
34
|
return result;
|
35
|
-
}
|
35
|
+
};
|
36
36
|
|
37
37
|
export default resolveGraphQLQuery;
|