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

11802 convert faq block titles to headings #12023

Closed
wants to merge 4 commits into from
Closed
Show file tree
Hide file tree
Changes from all 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
3 changes: 3 additions & 0 deletions js/src/structured-data-blocks/faq/block.js
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,9 @@ export default () => {
additionalListCssClasses: {
type: "string",
},
header: {
type: "string",
},
},

/**
Expand Down
89 changes: 80 additions & 9 deletions js/src/structured-data-blocks/faq/components/FAQ.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,16 +2,19 @@
import React from "react";
import PropTypes from "prop-types";
import isUndefined from "lodash/isUndefined";
import get from "lodash/get";
import clamp from "lodash/clamp";
import { __ } from "@wordpress/i18n";
import { subscribe, select } from "@wordpress/data";
import { IconButton } from "@wordpress/components";
import { Component, renderToString } from "@wordpress/element";


/* Internal dependencies */
import Question from "./Question";
import { stripHTML } from "../../../helpers/stringHelpers";
import appendSpace from "../../../components/higherorder/appendSpace";

const { IconButton } = window.wp.components;
const { Component, renderToString } = window.wp.element;

const QuestionContentWithAppendedSpace = appendSpace( Question.Content );

/**
Expand All @@ -28,7 +31,7 @@ export default class FAQ extends Component {
constructor( props ) {
super( props );

this.state = { focus: "" };
this.state = { focus: "", headingBlockClientId: "" };

this.changeQuestion = this.changeQuestion.bind( this );
this.insertQuestion = this.insertQuestion.bind( this );
Expand All @@ -41,6 +44,8 @@ export default class FAQ extends Component {
this.onAddQuestionButtonClick = this.onAddQuestionButtonClick.bind( this );

this.editorRefs = {};

this.subscribeToBlocks();
}

/**
Expand Down Expand Up @@ -92,7 +97,6 @@ export default class FAQ extends Component {
*/
changeQuestion( newQuestion, newAnswer, previousQuestion, previousAnswer, index ) {
const questions = this.props.attributes.questions ? this.props.attributes.questions.slice() : [];

if ( index >= questions.length ) {
return;
}
Expand Down Expand Up @@ -123,7 +127,7 @@ export default class FAQ extends Component {
* @param {number} [index] The index of the Question after which a new Question should be added.
* @param {array|string} [question] The question of the new Question.
* @param {array|string} [answer] The answer of the new Question.
* @param {bool} [focus=true] Whether or not to focus the new Question.
* @param {boolean} [focus=true] Whether or not to focus the new Question.
*
* @returns {void}
*/
Expand All @@ -137,7 +141,7 @@ export default class FAQ extends Component {
let lastIndex = questions.length - 1;
while ( lastIndex > index ) {
this.editorRefs[ `${ lastIndex + 1 }:question` ] = this.editorRefs[ `${ lastIndex }:question` ];
this.editorRefs[ `${ lastIndex + 1}:answer` ] = this.editorRefs[ `${ lastIndex }:answer` ];
this.editorRefs[ `${ lastIndex + 1 }:answer` ] = this.editorRefs[ `${ lastIndex }:answer` ];
lastIndex--;
}

Expand Down Expand Up @@ -318,6 +322,7 @@ export default class FAQ extends Component {
onMoveDown={ this.moveQuestionDown }
isFirst={ index === 0 }
isLast={ index === attributes.questions.length - 1 }
questionHeader={ attributes.header }
/>
);
}
Expand All @@ -334,10 +339,10 @@ export default class FAQ extends Component {
* @returns {Component} The component representing a FAQ block.
*/
static Content( attributes ) {
const { questions, className } = attributes;
const { questions, className, header } = attributes;

const questionList = questions ? questions.map( ( question, index ) =>
<QuestionContentWithAppendedSpace key={ index } { ...question } />
<QuestionContentWithAppendedSpace key={ index } header={ header } { ...question } />
) : null;

const classNames = [ "schema-faq", className ].filter( ( i ) => i ).join( " " );
Expand All @@ -349,6 +354,72 @@ export default class FAQ extends Component {
);
}

/**
* Gets the correct header level for the questions in the FAQ block.
*
* @returns {string} The header tag.
*/
getHeader() {
const blocks = select( "core/editor" ).getBlocks();
let currentblock = {};
let header = "h2";

// Use the order data as this is updated live in the store, the order in blocks is only updated on refresh.
const order = select( "core/editor" ).getBlockOrder();
for ( let i = 0; i < order.length; i++ ) {
currentblock = blocks.find( block => block.clientId === order[ i ] );

// Update header when preceding header is found, but limit it to a value between 2 and 6.
if ( currentblock.attributes.level ) {
header = "h" + ( clamp( currentblock.attributes.level + 1, 2, 6 ) );
this.setState( { headingBlockClientId: currentblock.clientId } );
}

// Exit when faq-block is found.
if ( currentblock.name === "yoast/faq-block" ) {
break;
}
}
return header;
}

/**
* Subscribes to the store, listens for changes relevant for header changes.
*
* @returns {void}
*/
subscribeToBlocks() {
subscribe( () => {
// Check if the order of blocks has changed.
const blockOrder = select( "core/editor" ).getBlockOrder();
if ( this._previousBlockOrder !== blockOrder ) {
this._previousBlockOrder = blockOrder;
this.setHeader();
}

// Check if the the current last header block has changed.
const lastHeadingBlock = select( "core/editor" ).getBlock( this.state.headingBlockClientId );
const previousHeadingBlockLevel = get( this._previousLastHeadingBlock, "attributes.level" );

if ( lastHeadingBlock && previousHeadingBlockLevel !== lastHeadingBlock.attributes.level ) {
this._previousLastHeadingBlock = lastHeadingBlock;
this.setHeader();
}
} );
}

/**
* Sets the header level for the questions in the FAQ block.
*
* @returns {void}
*/
setHeader() {
const newHeader = this.getHeader();
if ( this.props.attributes.header !== newHeader ) {
this.props.setAttributes( { header: newHeader } );
}
}

/**
* Renders this component.
*
Expand Down
9 changes: 7 additions & 2 deletions js/src/structured-data-blocks/faq/components/Question.js
Original file line number Diff line number Diff line change
Expand Up @@ -310,7 +310,7 @@ export default class Question extends Component {
return (
<div className={ "schema-faq-section" } key={ question.id }>
<RichTextWithAppendedSpace
tagName="strong"
tagName={ question.header }
className="schema-faq-question"
key={ question.id + "-question" }
value={ question.question }
Expand Down Expand Up @@ -361,7 +361,7 @@ export default class Question extends Component {
<div className="schema-faq-section" key={ id }>
<RichText
className="schema-faq-question"
tagName="p"
tagName={ this.props.questionHeader }
unstableOnSetup={ this.setQuestionRef }
key={ id + "-question" }
value={ question }
Expand Down Expand Up @@ -409,4 +409,9 @@ Question.propTypes = {
isSelected: PropTypes.bool.isRequired,
isFirst: PropTypes.bool.isRequired,
isLast: PropTypes.bool.isRequired,
questionHeader: PropTypes.string,
};

Question.defaultProps = {
questionHeader: "h2",
};