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

10267 add cornerstone toggle to sidebar #668

Merged
merged 29 commits into from
Jul 30, 2018

Conversation

Dieterrr
Copy link
Contributor

@Dieterrr Dieterrr commented Jul 19, 2018

Summary

This PR can be summarized in the following changelog entry:

  • Adds a toggle to the Yoast Sidebar. The toggle is styled the same as the Gutenberg toggle.

Relevant technical choices:

  • As WP components is not (yet) available, it was not possible to import the toggle component from WP and we had to copy the code into our own file named GutenbergToggle.js. When the WP component becomes available we can remove this code and simply import the component.
  • We did not include the SCSS file from WP in our code, as it is already loaded in WordPress when Gutenberg is enabled. This means the button works in the plugin sidebar, but does not display correctly in the standalone app.
  • The toggle button currently "toggles", for testing purposes. In the future this functionality needs to use the yoast_wpseo_is_cornerstone value. For this we need the the finished container which houses the cornerstone toggle and is connected to the store.

Test instructions

This PR can be tested by following these steps:

  • Checkout out the branches 10267-add-cornerstone-toggle-to-sidebar in both components as well as Wordpress SEO and yarn link them (not needed when this is an RC).
  • Make sure define( 'YOAST_FEATURE_GUTENBERG_SIDEBAR', true ); is set in wp-config.php.
  • Edit a post or page, look at the Yoast sidebar (under the plug icon) and see the toggle :). The toggle should switch on/off when clicked and it's styling should be the same as the Gutenberg toggles found under the settings/gear icon.

Fixes #10267
Requires #10405

@@ -5,6 +5,7 @@ import styled from "styled-components";
// Internal dependencies.
import KeywordInput from "../composites/Plugin/Shared/components/KeywordInput";
import SynonymsSection from "../composites/Plugin/Synonyms/components/SynonymsSection";
import CornerstoneToggle from "../composites/Plugin/CornerstoneContent/CornerstoneToggle";
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this path should be "../composites/Plugin/CornerstoneContent/components/CornerstoneToggle"

margin-right: 10px;
flex-shrink: 0;
max-width: 75%;
}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

the indentation in all these lines is made of spaces. Should be tabs.

@@ -0,0 +1,81 @@
import React from "react";
import PropTypes from "prop-types";
import Styled from "styled-components";
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Styled should be lowercase


import GutenbergToggle from "../../Shared/components/GutenbergToggle";

const Cornerstone = Styled.div`
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Styled should be lowercase

key={this.props.key}
checked={this.state.cornerstoneToggleState}
onChange={this.handleChange}
id={this.props.toggleId}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

coding standards: all these lines (57 and 59-62) miss spaces between brackets.

The label should be always correctly associated with the input ID, more details in the CR comment.

* External dependencies
*/
import React from "react";
import classnames from "classnames";
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

has the introduction of the classnames package been discussed with @atimmer ?

className,
{ 'is-checked': checked },
);

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

so to my understanding, this gets properly styled only when in Gutenberg. When used outside Gutenberg, it will be completely unstyled (as in the standalone app example > "Keyword" tab). Is this what we want?

<svg className="components-form-toggle__on" width="2" height="6" aria-hidden="true" role="img" focusable="false" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 2 6"><path d="M0 0h2v6H0z" /></svg> : <svg className="components-form-toggle__off" width="6" height="6" aria-hidden="true" role="img" focusable="false" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 6 6"><path d="M3 1.5c.8 0 1.5.7 1.5 1.5S3.8 4.5 3 4.5 1.5 3.8 1.5 3 2.2 1.5 3 1.5M3 0C1.3 0 0 1.3 0 3s1.3 3 3 3 3-1.3 3-3-1.3-3-3-3z" /></svg>
}
</span>
);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please check indentation from line 24 to line 31

`;

class CornerstoneToggle extends React.Component {

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

eslint "warning Block must not be padded by blank lines", please remove this blank line

import classnames from "classnames";
import noop from "lodash/noop";

function GutenbergToggle( { className, checked, id, onChange = noop, ...props } ) {
Copy link
Contributor

@afercia afercia Jul 19, 2018

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

eslint error Missing JSDoc comment for this function
eslint error 'className' is missing in props validation
eslint error 'checked' is missing in props validation
eslint error 'id' is missing in props validation
eslint error 'onChange' is missing in props validation


function GutenbergToggle( { className, checked, id, onChange = noop, ...props } ) {
const wrapperClasses = classnames(
'components-form-toggle',
Copy link
Contributor

@afercia afercia Jul 19, 2018

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

eslint error Strings must use doublequote

const wrapperClasses = classnames(
'components-form-toggle',
className,
{ 'is-checked': checked },
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

eslint error Strings must use doublequote

/>
<span className="components-form-toggle__track"></span>
<span className="components-form-toggle__thumb"></span>
{ checked ?
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

eslint error '?' should be placed at the beginning of the line

<span className="components-form-toggle__track"></span>
<span className="components-form-toggle__thumb"></span>
{ checked ?
<svg className="components-form-toggle__on" width="2" height="6" aria-hidden="true" role="img" focusable="false" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 2 6"><path d="M0 0h2v6H0z" /></svg> : <svg className="components-form-toggle__off" width="6" height="6" aria-hidden="true" role="img" focusable="false" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 6 6"><path d="M3 1.5c.8 0 1.5.7 1.5 1.5S3.8 4.5 3 4.5 1.5 3.8 1.5 3 2.2 1.5 3 1.5M3 0C1.3 0 0 1.3 0 3s1.3 3 3 3 3-1.3 3-3-1.3-3-3-3z" /></svg>
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

eslint error Line 28 exceeds the maximum line length of 150

@afercia
Copy link
Contributor

afercia commented Jul 19, 2018

CR 🚧 please see comments

About associating the label: when clicking on the text the toggle should switch. Instead, nothing happens.

screen shot 2018-07-19 at 16 44 58

Since the label is not associated, the toggle (the underlying checkbox) is unlabelled and announced by screen readers just as "checkbox". In the rendered markup there's no "for" attribute:
<label>Cornerstone</label>

nor the checkbox has an ID. Looking at the props, there's a toggleId prop but it's not passed and in any case the label uses the key prop:
<label htmlFor={this.props.key}>Cornerstone</label>

while the checkbox uses the toggleId prop:
<GutenbergToggle ... id={this.props.toggleId}

Instead of passing this value as a prop, since the for/id attributes must always be set, I'd suggest to handle their values internally using lodash/uniqueId as we're already doing for other components.

<Cornerstone>
<label htmlFor={this.props.key}>Cornerstone</label>
<GutenbergToggle
key={this.props.key}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

not sure why there's the need of a key?

@afercia
Copy link
Contributor

afercia commented Jul 19, 2018

Note about accessibility;
on Gutenberg there's a long debate about these toggles, see WordPress/gutenberg#2146. The accessibility feedback asked for a way to have a textual indication of the toggle state. This is also what material.io recommends, when they state:

The option that the switch controls, as well as the state it’s in, should be made clear from the corresponding inline label.

We've suggested a couple alternative options:

1
indicating the state in the description below the label (this is currently used in Gutenberg for the Gallery crop setting):

screen shot 2018-04-12 at 18 52 02

screen shot 2018-04-12 at 18 53 56

2
using an "On/Off" test, as we're doing on MyYoast.

However, none of these 3 options is implemented in this component nor there's a way to change the inline label or use a description. I'd like to suggest this should be discussed, trying to find a good balance between accessibility and design. Of course, this can be addressed in a separate issue.
/Cc @hedgefield

@hedgefield
Copy link

@afercia For the Yoast sidebar, we intend to copy any components needed from their Gutenberg equivalents (if they exist). So the problem we face is really a problem we should fix in Gutenberg. I understand you'd rather not ship something not up to a11y standards, and neither do I, so let's build our version of this toggle to be compliant, and contribute that back as a PR at the same time. I'm a fan of the ON/OFF labels, they're simply the most efficient way of indicating state textually, I think we should try that first. Do you see any problems with this approach?

@afercia
Copy link
Contributor

afercia commented Jul 24, 2018

@hedgefield we already proposed the on/off text to the Gutenberg team, they don't like it. In the linked Gutenberg issues above we've also proposed different positions for the on/off (to the side or underneath the toggle) but still they don't like it.
Instead, the Gutenberg toggle already has it's needed to conditionally render the inline label or the help text below the label. Those can be used for the textual indication of the state, it's just a matter of implementation (and consensus).

@hedgefield
Copy link

@afercia That's too bad. Hmm. I'm not a fan of the help text, it adds a lot of bloat to the interface. But we can conditionally change the label text already? So let's do that. And then what do you think, use a variation on the original design and go for Mark/Unmark this as cornerstone content or This content has (not) been marked as cornerstone? (or something else)

}
}

CornerstoneToggle.propTypes = {
Copy link
Contributor

@maartenleenders maartenleenders Jul 26, 2018

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We usually set defaults for PropTypes that are not required.

Copy link
Contributor

@jcomack jcomack left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

CR done 🚧

Have some (minor) requests / questions.

`;

const ToggleVisualLabel = styled.span`
padding: 0px 0 0;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Seems odd to define 0px followed by two zeroes. I believe you can set all three to a simple 0 and be done with it.

Additionally, this means you're deliberately resetting all padding except for the one on the left-hand side. Not sure if this is intended or not.

margin: 0;
outline: 0;
&:focus > span {
box-shadow: inset 0 0 0 1px ${colors.$color_white}, 0 0 0 1px #5b9dd9, 0 0 2px 1px rgba(30, 140, 190, .8);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Unsure as to why the first color is variable, but the other two are not.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This applies also to "#a5d6a7" on line 8. When new colors are needed, please discuss with the design team: "our" colors should all be in the color palette (_colors.scss). Of course this makes sense when a new color is going to be used extensively. For colors that are used only once, we should wonder if they're really needed in the first place. Please consider this is essential to be able to keep our color palette under control in the long run. /Cc @hedgefield

In a way, #5b9dd9 and rgba(30, 140, 190, .8) are an exception, as they're the colors WordPress uses for the focus style. Normally, they're inherited on interactive elements. If we're going to use them extensively on non-natively interactive elements then we may want to consider to add them to the color palette. Worth noting Gutenberg uses slightly different colors 😬

}

/**
* Returns the current enablement state.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

enablement should be enabled

/**
* Returns the current enablement state.
*
* @returns {boolean} The current enablement state.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

See previous comment.

* Sets the state to the opposite of the current state.
*
* @param {object} evt React SyntheticEvent.
* @returns {void}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Missing whitespace above this line.

const ToggleBullet = styled.span`
background-color: ${ props => props.isEnabled ? colors.$color_green_medium_light : colors.$color_grey_medium_dark };
margin-left: ${ props => props.isEnabled ? "12px" : "-2px" };
box-shadow: 0 2px 2px 2px rgba(0, 0, 0, 0.1);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Same here: this is a hardcoded color. We should use a color from the palette (not necessarily a rgba color)

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hi Andrea, you are right. But we'll skip this for now to finish the component. We'll pick this up again during the polishing process.

<ToggleBullet isEnabled={this.isEnabled()} />
</ToggleBar>
<ToggleVisualLabel aria-hidden="true">
{ this.isEnabled() ? __( "On", "yoast-components" ) : __( "Off", "yoast-components" ) }
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

if this component is going to be reusable, we should be able to pass a different visual label text.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good point. We will make a issue next week to "improve" the toggle: refactor, change how we use colors and add props for the custom on/off label text.

* @param {object} evt React SyntheticEvent.
* @returns {void}
*/
setEnabled( evt ) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

please avoid abbreviations: event is fine

aria-checked={this.isEnabled()}
id={this.props.id}
>
<ToggleBullet isEnabled={this.isEnabled()} />
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

missing space within braces here and in the lines above

*/
setEnabled( evt ) {
// Makes the toggle actionable with the Space bar key.
if ( evt.type === "keydown" && evt.which !== 32 ) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think keycode is safer than which (which is non-standard, though in jQuery is a normalized property in JavaScript it is not)

return (
<Cornerstone>
<Toggle ariaLabel="Mark this post as cornerstone content" id="Cornerstone Toggle"
labelText={__( "Mark this as cornerstone content.", "yoast-components" )} />
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

missing spaces between brackets

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

On a side note, we shouldn't use mixed spaces and tabs for indentation

render() {
return (
<Cornerstone>
<Toggle ariaLabel="Mark this post as cornerstone content" id="Cornerstone Toggle"
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

the id should be lowercase and without spaces (it's used for HTML attributes). Also, it can't be hardcoded. IDs must be unique in a page, otherwise it's impossible to use more than one toggle.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

True, in the toggle it is a prop we can set, but in the cornerstone toggle we keep it hard coded for now because there is only one cornerstone toggle.

<ToggleDiv>
{ /* eslint-disable-next-line jsx-a11y/click-events-have-key-events, jsx-a11y/no-noninteractive-element-interactions */ }
<label
htmlFor={ this.props.id }
Copy link
Contributor

@boblinthorst boblinthorst Jul 30, 2018

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Not important enough for now, and also more a preference than an instruction.

but when props are used in multiple places, it's nice to extract props to local variables, this is done, for example, in SnippetEditorFields.js.

@boblinthorst
Copy link
Contributor

CR: no important comments. but merge conflicts. 😞

@boblinthorst boblinthorst merged commit 4d14498 into develop Jul 30, 2018
@boblinthorst boblinthorst deleted the 10267-add-cornerstone-toggle-to-sidebar branch July 30, 2018 14:50
@abotteram abotteram added this to the 4.9.0 milestone Jul 31, 2018
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

7 participants