Update comment list positioning so that they slide in/out with the sidebar (#8345)

pull/10344/head
Karl Hobley 2023-04-18 00:58:14 +01:00 zatwierdzone przez GitHub
rodzic 167471b1f7
commit 7f297cd019
Nie znaleziono w bazie danych klucza dla tego podpisu
ID klucza GPG: 4AEE18F83AFDEB23
20 zmienionych plików z 165 dodań i 228 usunięć

Wyświetl plik

@ -41,6 +41,7 @@ Changelog
* Revise alignment and spacing of form fields and sections (Thibaud Colas)
* Update Wagtails type scale so StreamField block labels and field labels are the same size (Thibaud Colas)
* Allow customising the `search_fields` and search backend via SnippetViewSet (Sage Abdullah)
* Style comments as per page editor design, in side panel (Karl Hobley, Thibaud Colas)
* Fix: Ensure `label_format` on StructBlock gracefully handles missing variables (Aadi jindal)
* Fix: Adopt a no-JavaScript and more accessible solution for the 'Reset to default' switch to Gravatar when editing user profile (Loveth Omokaro)
* Fix: Ensure `Site.get_site_root_paths` works on cache backends that do not preserve Python objects (Jaap Roes)

Wyświetl plik

@ -53,7 +53,7 @@
}
&__close-button {
@apply w-text-primary w-absolute w-left-3 w-top-3 hover:w-text-primary-200 w-bg-white w-p-3 w-hidden w-transition;
@apply w-text-primary w-absolute w-left-3 w-top-3 hover:w-text-primary-200 w-bg-white w-p-3 w-transition;
.icon {
@apply w-w-4 w-h-4;
@ -93,10 +93,6 @@
@apply w-w-0 w-h-0 w-opacity-0 w-absolute w-pointer-events-none;
}
&--open .form-side__close-button {
@apply w-block;
}
&__panel {
@apply w-px-5 xl:w-px-10 w-py-4 w-w-full w-h-full w-overflow-y-auto w-scrollbar-thin;
}

Wyświetl plik

@ -411,7 +411,7 @@ export default class CommentComponent extends React.Component<CommentProps> {
{gettext('Are you sure?')}
<button
type="button"
className="comment__button"
className="comment__button button button-small"
onClick={onClickCancel}
>
{gettext('Cancel')}

Wyświetl plik

@ -1,43 +1,23 @@
.comment {
@include box;
width: calc(100vw - 40px);
max-width: calc(100vw - 19%);
width: 300px;
display: block;
transition: top 0.5s ease 0s, inset-inline-end 0.5s ease 0s,
height 0.5s ease 0s;
pointer-events: auto;
padding-bottom: 0;
inset-inline-end: -2000px;
background-color: theme('colors.white.DEFAULT');
@include media-breakpoint-up(sm) {
width: calc(100vw - 40px);
max-width: 400px;
inset-inline-start: initial;
}
@include media-breakpoint-up(md) {
max-width: 200px;
inset-inline-end: 0;
}
@include media-breakpoint-up(lg) {
max-width: 275px;
}
inset-inline-end: 0;
&--focused {
inset-inline-end: 35px;
@include media-breakpoint-up(md) {
inset-inline-end: 50px;
}
inset-inline-end: 30px;
}
&__text {
color: $color-box-text;
font-size: 13px;
line-height: 19px;
font-weight: 400;
font-size: 16px;
line-height: 150%;
margin-bottom: 0;
padding-top: 10px;
padding-bottom: 10px;
@ -53,11 +33,11 @@
&--mode-creating form {
border-top: 0;
margin-top: 10px;
margin-top: 12px;
}
&--mode-editing form {
margin-top: 10px;
margin-top: 12px;
}
&--mode-deleting &__text {
@ -76,13 +56,13 @@
&__actions,
&__reply-actions {
padding-bottom: 10px;
padding-bottom: 20px;
}
&__actions &__button,
&__reply-actions &__button {
margin-inline-end: 10px;
margin-top: 10px;
margin-top: 12px;
}
&__confirm-delete &__button {

Wyświetl plik

@ -16,8 +16,10 @@
); // Leave room for actions to the right and avatar to the left
margin: 0;
margin-inline-start: 45px;
font-size: 11px;
line-height: 15px;
font-weight: 600;
font-size: 14px;
line-height: 130%;
color: theme('colors.primary.DEFAULT');
}
&__date {

Wyświetl plik

@ -6,8 +6,9 @@
&__text {
color: $color-box-text;
font-size: 13px;
line-height: 19px;
font-weight: 400;
font-size: 16px;
line-height: 150%;
margin-bottom: 0;
padding-top: 10px;
padding-bottom: 10px;

Wyświetl plik

@ -1,16 +1,14 @@
@import '../../../scss/settings/variables';
$color-comment-separator: theme('colors.grey.100');
$color-comment-separator: theme('colors.grey.200');
$color-box-background: $color-white;
$color-box-border: $color-grey-3;
$color-box-border-focused: $color-grey-2;
$color-box-background-focused: theme('colors.grey.50');
$color-box-border: theme('colors.grey.150');
$color-box-border-focused: theme('colors.grey.200');
$color-box-text: $color-black;
$color-textarea-background: theme('colors.grey.50');
$color-textarea-border: $color-input-border;
$color-textarea-placeholder-text: $color-grey-2;
$box-border-radius: 5px;
$box-padding: 10px;
$box-padding: 20px;
@mixin focus-outline {
outline: $color-focus-outline solid 3px;
@ -18,28 +16,28 @@ $box-padding: 10px;
@mixin box {
background-color: $color-box-background;
border: 1px solid $color-box-border;
padding: $box-padding;
font-size: 11px;
border-radius: $box-border-radius;
color: $color-box-text;
border: 1px solid $color-box-border;
&--focused {
background-color: $color-box-background-focused;
border: 1px solid $color-box-border-focused;
box-shadow: 3px 2px 3px -1px theme('colors.black-10');
}
textarea {
font-family: $font-sans;
margin: 0;
padding: 10px;
padding: 12px;
width: 100%;
background-color: $color-textarea-background;
border: 1px solid $color-textarea-border;
background-color: $color-white;
border: 1px solid $color-input-border;
border-radius: 5px;
color: $color-box-text;
&::placeholder {
color: $color-textarea-placeholder-text;
color: theme('colors.grey.400');
opacity: 1;
}
}
@ -89,12 +87,10 @@ $box-padding: 10px;
border-radius: 3px;
color: $color-teal;
cursor: pointer;
font-family: inherit;
font-size: 12px;
font-weight: bold;
height: 25px;
padding-inline-start: 5px;
padding-inline-end: 5px;
font-weight: 700;
height: 30px;
padding-inline-start: 10px;
padding-inline-end: 10px;
&--primary {
color: $color-white;
@ -119,13 +115,10 @@ $box-padding: 10px;
}
.comments-list {
width: 400px;
position: absolute;
top: 30px;
inset-inline-end: 35px;
z-index: 95;
font-family: $font-sans;
pointer-events: none;
top: 20px;
inset-inline-end: 20px;
z-index: calc(theme('zIndex.header') + 5);
}
// stylelint-disable no-invalid-position-at-import-rule

Wyświetl plik

@ -1,4 +1,4 @@
import React from 'react';
import React, { useCallback } from 'react';
import ReactDOM from 'react-dom';
import { createStore } from 'redux';
@ -21,7 +21,6 @@ import {
selectComments,
selectCommentsForContentPathFactory,
selectCommentFactory,
selectEnabled,
selectFocused,
selectIsDirty,
selectCommentCount,
@ -70,17 +69,59 @@ const getAuthor = (
};
};
function renderCommentsUi(
store: Store,
layout: LayoutController,
comments: Comment[],
): React.ReactElement {
interface CommentListingProps {
store: Store;
layout: LayoutController;
comments: Comment[];
}
function CommentListing({
store,
layout,
comments,
}: CommentListingProps): React.ReactElement {
const state = store.getState();
const { commentsEnabled, user, currentTab } = state.settings;
const { user, currentTab } = state.settings;
const { focusedComment, forceFocus } = state.comments;
const commentsListRef = React.useRef<HTMLOListElement | null>(null);
// Update the position of the comments listing as the window scrolls to keep the comments in line with the content
const updateScroll = useCallback(
(e: Event) => {
if (!commentsListRef.current) {
return;
}
if (
e.type === 'scroll' &&
!document.querySelector('.form-side--comments')
) {
return;
}
const scrollContainer = document.querySelector('.content');
const top = scrollContainer?.getBoundingClientRect().top;
commentsListRef.current.style.top = `${top}px`;
},
[commentsListRef],
);
let commentsToRender = comments;
if (!commentsEnabled || !user) {
React.useEffect(() => {
const root = document.querySelector('#main');
const commentSidePanel = document.querySelector(
'[data-side-panel="comments"]',
);
root?.addEventListener('scroll', updateScroll);
commentSidePanel?.addEventListener('show', updateScroll);
return () => {
root?.removeEventListener('scroll', updateScroll);
commentSidePanel?.removeEventListener('show', updateScroll);
};
}, []);
if (!user) {
commentsToRender = [];
}
// Hide all resolved/deleted comments
@ -99,7 +140,11 @@ function renderCommentsUi(
isVisible={layout.getCommentVisible(currentTab, comment.localId)}
/>
));
return <ol className="comments-list">{commentsRendered}</ol>;
return (
<ol ref={commentsListRef} className="comments-list">
{commentsRendered}
</ol>
);
/* eslint-enable react/no-danger */
}
@ -115,13 +160,13 @@ export class CommentApp {
selectors = {
selectComments,
selectEnabled,
selectFocused,
selectIsDirty,
selectCommentCount,
};
actions = commentActionFunctions;
activationHandlers: (() => void)[] = [];
constructor() {
this.store = createStore(reducer, {
@ -189,12 +234,12 @@ export class CommentApp {
return commentId;
}
setVisible(visible: boolean) {
this.store.dispatch(
updateGlobalSettings({
commentsEnabled: visible,
}),
);
activate() {
this.activationHandlers.forEach((handler) => handler());
}
onActivate(handler: () => void) {
this.activationHandlers.push(handler);
}
invalidateContentPath(contentPath: string) {
@ -255,7 +300,11 @@ export class CommentApp {
}
ReactDOM.render(
renderCommentsUi(this.store, this.layout, commentList),
<CommentListing
store={this.store}
layout={this.layout}
comments={commentList}
/>,
element,
() => {
// Render again if layout has changed (eg, a comment was added, deleted or resized)
@ -263,7 +312,11 @@ export class CommentApp {
this.layout.refreshDesiredPositions(state.settings.currentTab);
if (this.layout.refreshLayout()) {
ReactDOM.render(
renderCommentsUi(this.store, this.layout, commentList),
<CommentListing
store={this.store}
layout={this.layout}
comments={commentList}
/>,
element,
);
}

Wyświetl plik

@ -27,8 +27,6 @@ export function selectCommentFactory(localId: number) {
});
}
export const selectEnabled = (state: State) => state.settings.commentsEnabled;
export const selectIsDirty = createSelector(
selectComments,
selectRemoteCommentCount,

Wyświetl plik

@ -5,7 +5,6 @@ import { update } from './utils';
export interface SettingsState {
user: Author | null;
commentsEnabled: boolean;
currentTab: string | null;
}
@ -14,7 +13,6 @@ export type SettingsStateUpdate = Partial<SettingsState>;
// Reducer with initial state
export const INITIAL_STATE: SettingsState = {
user: null,
commentsEnabled: false,
currentTab: null,
};

Wyświetl plik

@ -1,3 +1,11 @@
.Draftail-CommentControl {
display: none;
.tab-content--comments-enabled & {
display: block;
}
}
.Draftail-CommentControl .Draftail-ToolbarButton {
color: theme('colors.secondary.100');

Wyświetl plik

@ -5,7 +5,6 @@ import { createEditorStateFromRaw } from 'draftail';
import { DraftInlineStyleType, EditorState, SelectionState } from 'draft-js';
import { CommentApp } from '../../CommentApp/main';
import { updateGlobalSettings } from '../../CommentApp/actions/settings';
import { newComment } from '../../CommentApp/state/comments';
import { noop } from '../../../utils/noop';
@ -137,20 +136,12 @@ describe('CommentableEditor', () => {
commentApp = new CommentApp();
});
it('has control', () => {
commentApp.setVisible(true);
const editor = mount(getEditorComponent(commentApp));
const controls = editor.find('DraftailEditor').prop('controls');
expect(controls).toHaveLength(1);
expect(controls[0].inline).toBeTruthy();
editor.unmount();
});
it('has no control when comments disabled', () => {
commentApp.store.dispatch(updateGlobalSettings({ commentsEnabled: false }));
const editor = mount(getEditorComponent(commentApp));
const controls = editor.find('DraftailEditor').prop('controls');
expect(controls).toHaveLength(0);
editor.unmount();
});
it('can update comment positions', () => {
commentApp.store.dispatch(
commentApp.actions.addComment(

Wyświetl plik

@ -335,6 +335,9 @@ function getCommentControl(
</>
}
onClick={() => {
// Open the comments side panel
commentApp.activate();
onChange(
addNewComment(getEditorState(), fieldNode, commentApp, contentPath),
);
@ -467,7 +470,6 @@ function getCommentDecorator(commentApp: CommentApp) {
return null;
}
const enabled = useSelector(commentApp.selectors.selectEnabled);
const blockKey: BlockKey = children[0].props.block.getKey();
const start: number = children[0].props.start;
@ -490,10 +492,6 @@ function getCommentDecorator(commentApp: CommentApp) {
return undefined; // eslint demands an explicit return here
}, [commentId, annotationNode, blockKey]);
if (!enabled) {
return children;
}
const onClick = () => {
// Ensure the comment will appear alongside the current block
if (!commentId) {
@ -668,7 +666,6 @@ function CommentableEditor({
[commentApp],
);
const comments = useSelector(commentsSelector, shallowEqual);
const enabled = useSelector(commentApp.selectors.selectEnabled);
const focusedId = useSelector(commentApp.selectors.selectFocused);
const ids = useMemo(
@ -688,7 +685,6 @@ function CommentableEditor({
const previousFocused = usePrevious(focusedId);
const previousIds = usePrevious(ids);
const previousEnabled = usePrevious(enabled);
useEffect(() => {
// Only trigger a focus-related rerender if the current focused comment is inside the field, or the previous one was
const validFocusChange =
@ -700,7 +696,6 @@ function CommentableEditor({
if (
!validFocusChange &&
previousEnabled === enabled &&
(previousIds === ids ||
(previousIds.length === ids.length &&
previousIds.every((value, index) => value === ids[index])))
@ -731,7 +726,7 @@ function CommentableEditor({
),
);
setUniqueStyleId((id) => (id + 1) % 200);
}, [focusedId, enabled, inlineStyles, ids, editorState]);
}, [focusedId, inlineStyles, ids, editorState]);
useEffect(() => {
// if there are any comments without annotations, we need to add them to the EditorState
@ -806,9 +801,7 @@ function CommentableEditor({
setEditorState(newEditorState);
}}
editorState={editorState}
controls={
enabled ? controls.concat([{ inline: CommentControl }]) : controls
}
controls={controls.concat([{ inline: CommentControl }])}
inlineStyles={inlineStyles.concat(commentStyles)}
plugins={plugins.concat([
{
@ -840,7 +833,10 @@ function CommentableEditor({
handleArrowAtContentEnd(getEditorState(), setEditorState, 'RTL');
},
handleKeyCommand: (command: string, state: EditorState) => {
if (enabled && command === 'comment') {
if (command === 'comment') {
// Open the comments side panel
commentApp.activate();
const selection = state.getSelection();
const content = state.getCurrentContent();
if (selection.isCollapsed()) {
@ -869,9 +865,6 @@ function CommentableEditor({
return 'not-handled';
},
customStyleFn: (styleSet: DraftInlineStyle) => {
if (!enabled) {
return undefined;
}
// Use of casting in this function is due to issue #1563 in immutable-js, which causes operations like
// map and filter to lose type information on the results. It should be fixed in v4: when we upgrade,
// this casting should be removed

Wyświetl plik

@ -56,18 +56,15 @@ window.comments = (() => {
* @param {number} localId - the localId of the comment to subscribe to
*/
subscribeToUpdates(localId) {
const { selectFocused, selectEnabled } = commentApp.selectors;
const { selectFocused } = commentApp.selectors;
const selectComment = commentApp.utils.selectCommentFactory(localId);
const store = commentApp.store;
const initialState = store.getState();
let focused = selectFocused(initialState) === localId;
let shown = selectEnabled(initialState);
if (focused) {
this.onFocus();
}
if (shown) {
this.show();
}
this.show();
this.unsubscribe = store.subscribe(() => {
const state = store.getState();
const comment = selectComment(state);
@ -83,14 +80,6 @@ window.comments = (() => {
}
focused = nowFocused;
}
if (shown !== selectEnabled(state)) {
if (shown) {
this.hide();
} else {
this.show();
}
shown = selectEnabled(state);
}
});
this.setOnClickHandler(localId);
}
@ -125,6 +114,9 @@ window.comments = (() => {
setOnClickHandler(localId) {
this.node.addEventListener('click', () => {
// Open the comments side panel
commentApp.activate();
commentApp.store.dispatch(
commentApp.actions.setFocusedComment(localId, {
updatePinnedComment: true,
@ -163,7 +155,6 @@ window.comments = (() => {
throw new MissingElementError(annotationTemplateNode);
}
this.annotationTemplateNode = annotationTemplateNode;
this.updateVisibility(false);
}
register() {
@ -171,19 +162,14 @@ window.comments = (() => {
// The widget has no valid contentpath, skip subscriptions
return undefined;
}
const { selectEnabled } = commentApp.selectors;
const initialState = commentApp.store.getState();
let currentlyEnabled = selectEnabled(initialState);
const selectCommentsForContentPath =
commentApp.utils.selectCommentsForContentPathFactory(this.contentpath);
let currentComments = selectCommentsForContentPath(initialState);
this.updateVisibility(currentComments.length === 0 && currentlyEnabled);
const unsubscribeWidget = commentApp.store.subscribe(() => {
const state = commentApp.store.getState();
const newComments = selectCommentsForContentPath(state);
const newEnabled = selectEnabled(state);
const commentsChanged = currentComments !== newComments;
const enabledChanged = currentlyEnabled !== newEnabled;
if (commentsChanged) {
// Add annotations for any new comments
currentComments = newComments;
@ -195,14 +181,6 @@ window.comments = (() => {
annotation.subscribeToUpdates(comment.localId);
});
}
if (enabledChanged || commentsChanged) {
// If comments have been enabled or disabled, or the comments have changed
// check whether to show the widget (if comments are enabled and there are no existing comments)
currentlyEnabled = newEnabled;
this.updateVisibility(
currentComments.length === 0 && currentlyEnabled,
);
}
});
initialState.comments.comments.forEach((comment) => {
// Add annotations for any comments already in the store
@ -218,11 +196,14 @@ window.comments = (() => {
annotation.subscribeToUpdates(localId);
};
this.commentAdditionNode.addEventListener('click', () => {
// Open the comments side panel
commentApp.activate();
// Make the widget button clickable to add a comment
addComment();
});
this.fieldNode.addEventListener('keyup', (e) => {
if (currentlyEnabled && isCommentShortcut(e)) {
if (isCommentShortcut(e)) {
if (currentComments.length === 0) {
addComment();
} else {
@ -239,19 +220,6 @@ window.comments = (() => {
return unsubscribeWidget; // TODO: listen for widget deletion and use this
}
updateVisibility(newShown) {
if (newShown === this.shown) {
return;
}
this.shown = newShown;
if (!this.shown) {
this.commentAdditionNode.classList.add('u-hidden');
} else {
this.commentAdditionNode.classList.remove('u-hidden');
}
}
getAnnotationForComment() {
const annotationNode = this.annotationTemplateNode.cloneNode(true);
annotationNode.id = '';
@ -276,38 +244,13 @@ window.comments = (() => {
}
}
function onNextEnable(fn) {
// Run a function once, when comments are enabled
const { selectEnabled } = commentApp.selectors;
const getEnabled = () => selectEnabled(commentApp.store.getState());
let enabled = getEnabled();
if (enabled) {
// If we're starting off enabled, run the function immediately
fn();
return;
}
const unsubscribe = commentApp.store.subscribe(() => {
// Otherwise, subscribe to updates and run the function when comments change to enabled
const newEnabled = getEnabled();
if (newEnabled && !enabled) {
enabled = newEnabled;
unsubscribe();
fn();
}
});
}
function initAddCommentButton(buttonElement) {
const initWidget = () => {
const widget = new FieldLevelCommentWidget({
fieldNode: buttonElement.closest('[data-contentpath]'),
commentAdditionNode: buttonElement,
annotationTemplateNode: document.querySelector('#comment-icon'),
});
widget.register();
};
// Our template node may not exist yet - let's hold off until comments are loaded and enabled
onNextEnable(initWidget);
const widget = new FieldLevelCommentWidget({
fieldNode: buttonElement.closest('[data-contentpath]'),
commentAdditionNode: buttonElement,
annotationTemplateNode: document.querySelector('#comment-icon'),
});
widget.register();
}
function initCommentsInterface(formElement) {
@ -348,42 +291,21 @@ window.comments = (() => {
});
}
// Show/hide comments when the side panel is opened/closed
const commentsSidePanel = document.querySelector(
'[data-side-panel="comments"]',
);
// Show comments app
const commentNotifications = formElement.querySelector(
'[data-comment-notifications]',
);
commentNotifications.hidden = false;
const tabContentElement = formElement.querySelector('.tab-content');
tabContentElement.classList.add('tab-content--comments-enabled');
const updateCommentVisibility = (visible) => {
// Show/hide comments
commentApp.setVisible(visible);
// Add/Remove tab-nav--comments-enabled class. This changes the size of streamfields
if (visible) {
tabContentElement.classList.add('tab-content--comments-enabled');
if (commentNotifications) {
commentNotifications.hidden = false;
}
} else {
tabContentElement.classList.remove('tab-content--comments-enabled');
if (commentNotifications) {
commentNotifications.hidden = true;
}
}
};
if (commentsSidePanel) {
commentsSidePanel.addEventListener('show', () => {
updateCommentVisibility(true);
});
commentsSidePanel.addEventListener('hide', () => {
updateCommentVisibility(false);
});
}
// Open the comments panel whenever the comment app is activated by a user clicking on an "Add comment" widget on the form.
const commentSidePanel = document.querySelector(
'[data-side-panel="comments"]',
);
commentApp.onActivate(() => {
commentSidePanel.dispatchEvent(new Event('open'));
});
// Keep number of comments up to date with comment app
const commentToggle = document.querySelector(

Wyświetl plik

@ -43,14 +43,7 @@ export default function initSidePanel() {
if (panelName && !selectedPanel) return;
// Open / close side panel
// HACK: For now, the comments will show without the side-panel opening.
// They will later be updated so that they render inside the side panel.
// We couldn't implement this for Wagtail 3.0 as the existing field styling
// renders the "Add comment" button on the right hand side, and this gets
// covered up by the side panel.
if (panelName === '' || panelName === 'comments') {
if (panelName === '') {
sidePanelWrapper.classList.remove('form-side--open');
sidePanelWrapper.removeAttribute('aria-labelledby');
} else {
@ -129,6 +122,14 @@ export default function initSidePanel() {
}
};
// Open the side panel if the 'open' custom event is triggered on the side panel
// This is allows panels to be opened with JavaScript without hacking the toggle
document.querySelectorAll('[data-side-panel]').forEach((panel) => {
panel.addEventListener('open', () => {
setPanel(panel.dataset.sidePanel);
});
});
document.querySelectorAll('[data-side-panel-toggle]').forEach((toggle) => {
toggle.addEventListener('click', () => {
togglePanel(toggle.dataset.sidePanelToggle);

Wyświetl plik

@ -80,6 +80,7 @@ Those improvements were implemented by Albina Starykova as part of an [Outreachy
* Revise alignment and spacing of form fields and sections (Thibaud Colas)
* Update Wagtails type scale so StreamField block labels and field labels are the same size (Thibaud Colas)
* Allow customising the `search_fields` and search backend via SnippetViewSet (Sage Abdullah)
* Style comments as per page editor design, in side panel (Karl Hobley, Thibaud Colas)
### Bug fixes

Wyświetl plik

@ -5,7 +5,6 @@
{% block titletag %}{% blocktrans trimmed with page_type=content_type.model_class.get_verbose_name %}New {{ page_type }}{% endblocktrans %}{% endblock %}
{% block content %}
<div id="comments"></div>
<div class="w-sticky w-top-0 w-z-header">
{% include 'wagtailadmin/shared/headers/page_create_header.html' %}

Wyświetl plik

@ -6,7 +6,6 @@
{% block bodyclass %}{% if page.live %}page-is-live{% endif %} {% if page_locked %}content-locked{% endif %}{% endblock %}
{% block content %}
<div id="comments"></div>
{% page_permissions page as page_perms %}
<div class="w-sticky w-top-0 w-z-header">

Wyświetl plik

@ -73,7 +73,7 @@
{% endblock %}
{% if show_add_comment_button %}
<button class="w-field__comment-button w-field__comment-button--add u-hidden" type="button" data-component="add-comment-button" data-comment-add aria-label="{% trans 'Add comment' %}" {% if label_for %}aria-describedby="{{ label_id }}"{% endif %}>
<button class="w-field__comment-button w-field__comment-button--add" type="button" data-component="add-comment-button" data-comment-add aria-label="{% trans 'Add comment' %}" {% if label_for %}aria-describedby="{{ label_id }}"{% endif %}>
{% icon name="comment-add" %}
{% icon name="comment-add-reversed" %}
</button>

Wyświetl plik

@ -0,0 +1 @@
<div id="comments"></div>