Inline commenting functionality

This commit is contained in:
2018-12-11 17:35:34 +00:00
parent 0d4dc29741
commit 362c85b2d1
7 changed files with 237 additions and 3 deletions

View File

@@ -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;
}

View File

@@ -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);

47
js/reply-to-comment.js Normal file
View File

@@ -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);
}
}

139
js/reply-to-comment.spec.js Normal file
View File

@@ -0,0 +1,139 @@
import ReplyToComment from './reply-to-comment';
const commentHtml = `
<div class="comment">
<div class="comment__head">
<div class="comment__avatar"></div>
<div class="comment__metadata">
<span class="comment__author">Mr Smith</span>
<span class="comment__date">21st September, 2018 at 15:36</span>
</div>
</div>
<div class="comment__body">This is a comment.</div>
<div class="comment__foot">
<a class="comment__reply" data-comment-author="Mr Smith" data-comment-id="1234" href="?replytocom=1234#respond">
<span class="comment__reply-text">Reply</span>
</a>
<div class="comment__inline-reply"></div>
</div>
</div>`;
const formHtml = `
<div class="comments__foot">
<div id="respond" class="comment-respond">
<h3 id="reply-title" class="comment-respond__title">
<span class="comment-respond__title-text">
Leave a Reply
</span>
<div class="comment-respond__cancel">
<a rel="nofollow" id="cancel-comment-reply-link" href="#respond">Cancel reply</a>
</div>
</h3>
<form action="/wp-comments-post.php" method="post" id="commentform" class="comment-respond__form">
<p class="comment-respond__notes">Your email address will not be published.</p>
<label class="comment-respond__label" for="comment">Comment</label>
<textarea class="comment-respond__input" id="comment" name="comment" cols="25" rows="8" aria-required="true"></textarea>
<div class="comment-respond__item">
<label class="comment-respond__label" for="author">Name</label>
<input class="comment-respond__input" id="author" name="author" type="text" value="" size="30">
</div>
<div class="comment-respond__item">
<label class="comment-respond__label" for="email">Email</label>
<input class="comment-respond__input id="email" name="email" type="text" value="" size="30">
</div>
<div class="comment-respond__item">
<label class="comment-respond__label" for="url">Website</label>
<input class="comment-respond__input" id="url" name="url" type="text" value="" size="30">
</div>
<p class="form-submit">
<input name="submit" type="submit" id="submit" class="comment-respond__submit" value="Post Comment">
<input type="hidden" name="comment_post_ID" value="2683" id="comment_post_ID">
<input type="hidden" name="comment_parent" id="comment_parent" value="0">
</p>
<p style="display: none;">
<input type="hidden" id="akismet_comment_nonce" name="akismet_comment_nonce" value="123456789">
</p>
<input type="hidden" id="ak_js" name="ak_js" value="987654321">
</form>
</div>
</div>
`;
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('');
});
});

View File

@@ -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'),
});
}

View File

@@ -2,9 +2,12 @@
$form_settings = array(
class_form => 'comment-respond__form',
class_submit => 'comment-respond__submit',
comment_field => '<label class="comment-respond__label" for="comment">Comment</label><textarea class="comment-respond__input" id="comment" name="comment" cols="25" rows="8" aria-required="true"></textarea></p>',
comment_field => '<div class="comment-respond__item"><label class="comment-respond__label" for="comment">Comment</label><textarea class="comment-respond__input" id="comment" name="comment" cols="25" rows="8" aria-required="true"></textarea></div>',
logged_in_as => '<p class="comment-respond__text">' . sprintf( __( 'Logged in as <a class="link" href="%1$s">%2$s</a>. <a class="link" href="%3$s" title="Log out of this account">Log out?</a>' ), admin_url( 'profile.php' ), $user_identity, wp_logout_url( apply_filters( 'the_permalink', get_permalink( ) ) ) ) . '</p>',
title_reply_before => '<h3 id="reply-title" class="comment-respond__title">',
title_reply => '<span class="comment-respond__title-text">Reply</span>',
cancel_reply_before => '<span class="comment-respond__cancel">',
cancel_reply_after => '</span>',
comment_notes_before => '<p class="comment-respond__notes">' . __( 'Your email address will not be published.' ) . ( $req ? $required_text : '' ) . '</p>',
fields => array(
author => '<div class="comment-respond__item"><label class="comment-respond__label" for="author">' . __( 'Name', 'domainreference' ) . ( $req ? '<span class="comment-respond__required">*</span>' : '' ) . '</label>' . '<input class="comment-respond__input" id="author" name="author" type="text" value="' . esc_attr( $commenter['comment_author'] ) . '" size="30"' . $aria_req . ' /></div>',

View File

@@ -1,8 +1,10 @@
<?php
class Windycoys_Walker_Comment extends Walker_Comment {
function start_el(&$output, $comment, $depth = 0, $args = array(), $id = 0) {
function start_el(&$output, $comment, $depth = 0) {
$id = $comment->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 .= '</div></div>';
$output .= '<div class="comment__body">'.$content.'</div>';
$output .= '<div class="comment__foot">';
$output .= '<a class="comment__reply" href="'.get_comment_reply_link().'">';
$output .= '<a class="comment__reply" data-comment-author="'.$author.'" data-comment-id="'.$id.'" href="'.$reply_link.'">';
$output .= '<span class="comment__reply-icon">';
$output .= '<svg class="icon icon--extra-small"><use href="#icon-reply"></use></svg></span>';
$output .= '<span class="comment__reply-text">Reply</span></a>';
$output .= '<div class="comment__inline-reply"></div>';
$output .= '</div></div></li>';
}
}