From 362c85b2d1b41582ebc8b9d177276f9ccacad7c2 Mon Sep 17 00:00:00 2001 From: Dan Head Date: Tue, 11 Dec 2018 17:35:34 +0000 Subject: [PATCH] Inline commenting functionality --- css/comment-respond.css | 33 +++++++++ css/comment.css | 4 ++ js/reply-to-comment.js | 47 ++++++++++++ js/reply-to-comment.spec.js | 139 ++++++++++++++++++++++++++++++++++++ js/windy.js | 5 ++ theme/comments.php | 5 +- theme/functions.php | 7 +- 7 files changed, 237 insertions(+), 3 deletions(-) create mode 100644 js/reply-to-comment.js create mode 100644 js/reply-to-comment.spec.js diff --git a/css/comment-respond.css b/css/comment-respond.css index 3e18eb3..93f0fba 100644 --- a/css/comment-respond.css +++ b/css/comment-respond.css @@ -4,12 +4,17 @@ padding-bottom: var(--space-sm); } +.comment-respond__title-text { + margin-right: var(--space-xs); +} + .comment-respond__text { font-size: 14px; } .comment-respond__item { margin-bottom: var(--space-sm); + max-width: 360px; } .comment-respond__label { @@ -21,6 +26,7 @@ background-color: #fff; border: 1px solid var(--color-near-black); padding: var(--space-xs); + width: 100%; } .comment-respond__input:focus { @@ -38,3 +44,30 @@ min-width: 100px; padding: var(--space-xs); } + +.comment-respond__cancel { + font-size: 12px; +} + +.comment-respond__cancel a, +.comment-respond__cancel a:visited { + position: relative; + color: var(--color-light-blue); +} + +.comment-respond__cancel a:after { + background-color: var(--color-light-blue); + bottom: -1px; + content: ''; + left: 0; + height: 2px; + opacity: 0; + position: absolute; + right: 0; + transition: opacity var(--transition-md) ease-out; +} + +.comment-respond__cancel a:focus:after, +.comment-respond__cancel a:hover:after { + opacity: 1; +} diff --git a/css/comment.css b/css/comment.css index b586fe7..4a17dc7 100644 --- a/css/comment.css +++ b/css/comment.css @@ -28,6 +28,10 @@ margin-bottom: var(--space-sm); } +.comment__reply.is-hidden { + display: none; +} + .comment__reply:focus, .comment__reply:hover { color: var(--color-light-blue); diff --git a/js/reply-to-comment.js b/js/reply-to-comment.js new file mode 100644 index 0000000..aa468a5 --- /dev/null +++ b/js/reply-to-comment.js @@ -0,0 +1,47 @@ +export default class ReplyToComment { + constructor(cfg = {}) { + if (!cfg.form || cfg.form.className !== 'comments__foot') { + return false; + } + if (!cfg.links || cfg.links.length === 0) { + return false; + } + this.form = cfg.form; + this.backupForm = cfg.form.querySelector('#respond').cloneNode(true); + this.links = Array.prototype.slice.call(cfg.links); + this.links.forEach((link) => { + link.addEventListener('click', e => this.handleLinkClick(e, link)); + }); + } + + insertForm(container, id, author) { + const form = this.backupForm.cloneNode(true); + const cancel = form.querySelector('#cancel-comment-reply-link'); + form.querySelector('.comment-respond__title-text').innerHTML = `Reply to ${author}`; + form.querySelector('#submit').value = 'Post reply'; + form.querySelector('#comment_parent').value = id; + cancel.style.display = 'inline-block'; + cancel.addEventListener('click', e => this.handleCancelClick(e, container)); + container.appendChild(form); + } + + handleCancelClick(e, container) { + e.preventDefault(); + container.parentNode.querySelector('.comment__reply').classList.remove('is-hidden'); + while (container.firstChild) { + container.removeChild(container.firstChild); + } + this.form.appendChild(this.backupForm); + } + + handleLinkClick(e, link) { + e.preventDefault(); + link.classList.add('is-hidden'); + while (this.form.firstChild) { + this.form.removeChild(this.form.firstChild); + } + const container = link.parentNode.querySelector('.comment__inline-reply'); + const { commentId, commentAuthor } = link.dataset; + this.insertForm(container, commentId, commentAuthor); + } +} diff --git a/js/reply-to-comment.spec.js b/js/reply-to-comment.spec.js new file mode 100644 index 0000000..a9cc956 --- /dev/null +++ b/js/reply-to-comment.spec.js @@ -0,0 +1,139 @@ +import ReplyToComment from './reply-to-comment'; + +const commentHtml = ` +
+
+
+ +
+
This is a comment.
+ +
`; + +const formHtml = ` +
+
+

+ + Leave a Reply + + +

+
+

Your email address will not be published.

+ + +
+ + +
+
+ + +
+
+ + +
+

+ + + +

+

+ +

+ +
+
+
+`; + +let comments; +let form; +let links; +let inlineReply; + +beforeEach(() => { + document.body.innerHTML = `${commentHtml}${formHtml}`; + form = document.querySelector('.comments__foot'); + links = document.querySelectorAll('.comment__reply'); + inlineReply = document.querySelector('.comment__inline-reply'); + comments = new ReplyToComment({ form, links }); +}); + +describe('ReplyToComment', () => { + it('Should initialise', () => { + expect(comments.form).toBeInstanceOf(HTMLDivElement); + expect(comments.links).toBeInstanceOf(Array); + }); + + it('Should fail to initialise if form is missing', () => { + expect(new ReplyToComment()).toEqual({}); + }); + + it('Should fail to initialise if there are no links', () => { + expect(new ReplyToComment({ + form: document.querySelector('#respond'), + })).toEqual({}); + expect(new ReplyToComment({ + form: document.querySelector('#respond'), + links: document.querySelectorAll('.no-links-here'), + })).toEqual({}); + }); + + it('Should remove the form from the DOM when a reply link is clicked', () => { + const event = new Event('click'); + comments.handleLinkClick(event, links[0]); + expect(form.innerHTML).toEqual(''); + }); + + it('Should hide the link when it is clicked', () => { + const event = new Event('click'); + comments.handleLinkClick(event, links[0]); + expect(links[0].classList.contains('is-hidden')).toBe(true); + }); + + it('Should insert the form into the inline reply container', () => { + comments.insertForm(inlineReply); + const inlineForm = inlineReply.querySelectorAll('form'); + expect(inlineForm.length).toEqual(1); + }); + + it('Should update the comment_parent hidden element', () => { + comments.insertForm(inlineReply, '123', 'Mr Smith'); + const { value } = inlineReply.querySelector('#comment_parent'); + expect(value).toEqual('123'); + }); + + it('Should add a cancel link to the inline form', () => { + comments.insertForm(inlineReply); + const el = inlineReply.querySelector('#cancel-comment-reply-link'); + expect(el.style.display).toEqual('inline-block'); + }); + + it('Should restore the reply link when the cancel link is clicked', () => { + const event = new Event('click'); + comments.insertForm(inlineReply); + comments.handleCancelClick(event, inlineReply); + expect(links[0].classList.contains('is-hidden')).toBe(false); + }); + + it('Should remove the inline form when the cancel link is clicked', () => { + const event = new Event('click'); + comments.insertForm(inlineReply); + comments.handleCancelClick(event, inlineReply); + expect(inlineReply.innerHTML).toEqual(''); + }); +}); + diff --git a/js/windy.js b/js/windy.js index 55b3bda..f48c9ad 100644 --- a/js/windy.js +++ b/js/windy.js @@ -1,6 +1,7 @@ import Menu from './menu'; import Navigation from './navigation'; import Image from './image'; +import ReplyToComment from './reply-to-comment'; const windy = { images: [], @@ -21,4 +22,8 @@ export default function init() { }); windy.menu.init(); windy.navigation.init(); + windy.comments = new ReplyToComment({ + form: document.querySelector('.comments__foot'), + links: document.querySelectorAll('.comment__reply'), + }); } diff --git a/theme/comments.php b/theme/comments.php index ff6f522..122825d 100644 --- a/theme/comments.php +++ b/theme/comments.php @@ -2,9 +2,12 @@ $form_settings = array( class_form => 'comment-respond__form', class_submit => 'comment-respond__submit', - comment_field => '

', + comment_field => '
', logged_in_as => '

' . sprintf( __( 'Logged in as %2$s. Log out?' ), admin_url( 'profile.php' ), $user_identity, wp_logout_url( apply_filters( 'the_permalink', get_permalink( ) ) ) ) . '

', title_reply_before => '

', + title_reply => 'Reply', + cancel_reply_before => '', + cancel_reply_after => '', comment_notes_before => '

' . __( 'Your email address will not be published.' ) . ( $req ? $required_text : '' ) . '

', fields => array( author => '
' . '
', diff --git a/theme/functions.php b/theme/functions.php index 93b9841..d171c45 100644 --- a/theme/functions.php +++ b/theme/functions.php @@ -1,8 +1,10 @@ comment_ID; + $post_id = $comment->comment_post_ID; + $reply_link = get_permalink($post_id) . '?replytocom='.$id.'#respond'; $author_email = $comment->comment_author_email; $author = $comment->comment_author; $avatar = get_avatar($comment, 128); @@ -19,10 +21,11 @@ class Windycoys_Walker_Comment extends Walker_Comment { $output .= ''; $output .= '
'.$content.'
'; $output .= '
'; - $output .= ''; + $output .= ''; $output .= ''; $output .= ''; $output .= 'Reply'; + $output .= '
'; $output .= '
'; } }