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

Optional ScreenReaderText after the Collapsible title #464

Merged
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: 2 additions & 1 deletion app/SidebarCollapsibleWrapper.js
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@ export default function SidebarCollapsibleWrapper() {
<WidgetContainer>
<Collapsible
title="Insert some collapsible title here"
titleScreenReaderText="bad SEO score"
initialIsOpen={ true }
prefixIcon="circle"
prefixIconCollapsed="circle"
Expand All @@ -59,7 +60,7 @@ export default function SidebarCollapsibleWrapper() {
suffixIconCollapsed="question-circle"
suffixIconColor="purple"
>
<p>Maybe some help text here with a link <a target="_blank" href="https://yoast.com">Go to Yoast</a></p>
<p>Maybe some help text here with a link <a target="_blank" rel="noopener noreferrer" href="https://yoast.com">Go to Yoast</a></p>
</Collapsible>
</WidgetContainer>
</SortableContainer>
Expand Down
48 changes: 33 additions & 15 deletions composites/Plugin/Shared/components/Collapsible.js
Original file line number Diff line number Diff line change
@@ -1,9 +1,11 @@
import PropTypes from "prop-types";
import React from "react";
import styled from "styled-components";
import omit from "lodash/omit";

import colors from "../../../../style-guide/colors.json";
import { IconsButton } from "../../Shared/components/Button";
import ScreenReaderText from "../../../../a11y/ScreenReaderText";

const StyledContainer = styled.div`
background-color: ${ colors.$color_white };
Expand Down Expand Up @@ -72,7 +74,19 @@ const StyledHeading = wrapInHeading( StyledIconsButton, 2 );
/**
* Base collapsible panel. Optionally has a heading around the button.
*
* @param {object} props The properties for the component.
* @param {object} props The properties for the component.
* @param {children} props.children The content of the Collapsible.
* @param {IconsButton} props.Heading Heading button. May be wrapped or styled or both.
* @param {boolean} props.isOpen True displays the children. False means collapsed.
* @param {function} props.onToggle Function to handle the Heading click event.
* @param {string} props.prefixIcon Heading icon before the title.
* @param {string} props.prefixIconCollapsed Prefix icon when in collapsed state.
* @param {string} props.prefixIconColor CSS color of the prefix icon.
* @param {string} props.suffixIcon Heading icon after the title.
* @param {string} props.suffixIconColor CSS color of the suffix icon.
* @param {string} props.suffixIconCollapsed Suffix icon when in collapsed state.
* @param {string} props.title Title for in the Heading.
* @param {string} props.titleScreenReaderText Chance for an extra text to feed to a screenreader.
*
* @returns {ReactElement} A collapsible panel.
*/
Expand All @@ -88,6 +102,7 @@ export const CollapsibleStateless = ( props ) => {
suffixIconColor={ props.suffixIconColor }
>
<StyledTitle>{ props.title }</StyledTitle>
{ props.titleScreenReaderText ? <ScreenReaderText>{ props.titleScreenReaderText }</ScreenReaderText> : null }
</props.Heading>
{ props.isOpen && props.children }
</StyledContainer>
Expand All @@ -109,6 +124,7 @@ CollapsibleStateless.propTypes = {
suffixIconCollapsed: PropTypes.string,
suffixIconColor: PropTypes.string,
title: PropTypes.string,
titleScreenReaderText: PropTypes.string,
};

CollapsibleStateless.defaultProps = {
Expand All @@ -124,7 +140,17 @@ export class Collapsible extends React.Component {
/**
* The constructor.
*
* @param {object} props The properties for the component.
* @param {object} props The properties for the component.
* @param {number} props.headingLevel Heading level: 1 for h1, 2 for h2, etc.
* @param {boolean} props.initialIsOpen Determines if the initial isOpen state is open or closed.
* @param {string} props.prefixIcon Heading icon before the title.
* @param {string} props.prefixIconCollapsed Prefix icon when in collapsed state.
* @param {string} props.prefixIconColor CSS color of the prefix icon.
* @param {string} props.suffixIcon Heading icon after the title.
* @param {string} props.suffixIconColor CSS color of the suffix icon.
* @param {string} props.suffixIconCollapsed Suffix icon when in collapsed state.
* @param {string} props.title Title for in the Heading.
* @param {string} props.titleScreenReaderText Chance for an extra text to feed to a screenreader.
*
* @returns {ReactElement} Base collapsible panel.
*/
Expand Down Expand Up @@ -190,25 +216,16 @@ export class Collapsible extends React.Component {
*/
render() {
const { isOpen } = this.state;
const {
children,
prefixIcon, prefixIconCollapsed, prefixIconColor,
suffixIcon, suffixIconCollapsed, suffixIconColor,
title,
} = this.props;
const { children } = this.props;

const newProps = omit( this.props, [ "children" ] );

return (
<CollapsibleStateless
Heading={ this.Heading }
isOpen={ isOpen }
onToggle={ this.toggleCollapse }
prefixIcon={ prefixIcon }
prefixIconCollapsed={ prefixIconCollapsed }
prefixIconColor={ prefixIconColor }
suffixIcon={ suffixIcon }
suffixIconCollapsed={ suffixIconCollapsed }
suffixIconColor={ suffixIconColor }
title={ title }
{ ...newProps }
>
{ isOpen && <StyledContent>{ children }</StyledContent> }
</CollapsibleStateless>
Expand All @@ -230,6 +247,7 @@ Collapsible.propTypes = {
suffixIconCollapsed: PropTypes.string,
suffixIconColor: PropTypes.string,
title: PropTypes.string,
titleScreenReaderText: PropTypes.string,
};

Collapsible.defaultProps = {
Expand Down
19 changes: 19 additions & 0 deletions composites/Plugin/Shared/tests/CollapsibleTest.js
Original file line number Diff line number Diff line change
Expand Up @@ -97,4 +97,23 @@ describe( "CollapsibleStateless", () => {
onToggle();
expect( component.toJSON() ).toMatchSnapshot();
} );

it( "matches the snapshot with prefix seo icon and screen reader text", () => {
const component = renderer.create(
<CollapsibleStateless
title="Lorem ipsum dolor sit amet"
titleScreenReaderText="bad SEO score"
prefixIcon="circle"
prefixIconCollapsed="circle"
prefixIconColor="red"
isOpen={ true }
onToggle={ () => {} }
>
{ content }
</CollapsibleStateless>
);

let tree = component.toJSON();
expect( tree ).toMatchSnapshot();
} );
} );
187 changes: 187 additions & 0 deletions composites/Plugin/Shared/tests/__snapshots__/CollapsibleTest.js.snap
Original file line number Diff line number Diff line change
Expand Up @@ -1478,3 +1478,190 @@ exports[`CollapsibleStateless matches the snapshot when it is opened and closed
</p>
</div>
`;

exports[`CollapsibleStateless matches the snapshot with prefix seo icon and screen reader text 1`] = `
.c8 {
color: #555;
border-color: #ccc;
background: #f7f7f7;
box-shadow: 0 1px 0 rgba( 204,204,204,1 );
}

.c3 {
font-size: 0.8rem;
}

.c4:active {
box-shadow: inset 0 2px 5px -3px rgba( 0,0,0,0.5 );
}

.c5:hover {
color: #000;
}

.c6::-moz-focus-inner {
border-width: 0;
}

.c6:focus {
outline: none;
border-color: #0066cd;
box-shadow: 0 0 3px rgba( 8,74,103,0.8 );
}

.c7 {
display: -webkit-inline-box;
display: -webkit-inline-flex;
display: -ms-inline-flexbox;
display: inline-flex;
-webkit-align-items: center;
-webkit-box-align: center;
-ms-flex-align: center;
align-items: center;
-webkit-box-pack: center;
-webkit-justify-content: center;
-ms-flex-pack: center;
justify-content: center;
vertical-align: middle;
border-width: 1px;
border-style: solid;
margin: 0;
padding: 4px 10px;
border-radius: 3px;
cursor: pointer;
box-sizing: border-box;
font-size: inherit;
font-family: inherit;
font-weight: inherit;
text-align: left;
overflow: visible;
min-height: 32px;
}

.c7 svg {
-webkit-align-self: center;
-ms-flex-item-align: center;
align-self: center;
}

.c0 {
background-color: #fff;
}

.c2 {
width: 100%;
background-color: #fff;
padding: 15px;
-webkit-box-pack: start;
-webkit-justify-content: flex-start;
-ms-flex-pack: start;
justify-content: flex-start;
border-color: transparent;
border-radius: 0;
box-shadow: none;
color: #000;
}

.c2 svg:first-child {
margin-right: 8px;
}

.c2 svg:last-child {
margin-left: 8px;
}

.c10 {
white-space: nowrap;
text-overflow: ellipsis;
overflow-x: hidden;
-ms-flex-positive: 1;
-webkit-box-flex: 1;
-webkit-flex-grow: 1;
-ms-flex-positive: 1;
flex-grow: 1;
font-size: 1.03em;
font-weight: 600;
}

.c1 {
margin: 0;
font-weight: normal;
}

.c9 {
width: 16px;
height: 16px;
-webkit-flex: none;
-ms-flex: none;
flex: none;
}

@media all and ( -ms-high-contrast:none ),( -ms-high-contrast:active ) {
.c7::after {
display: inline-block;
content: "";
min-height: 22px;
}
}

<div
className="c0"
>
<h2
className="c1"
>
<button
aria-expanded={true}
className="c2 c3 Button-kDSBcD c4 Button-kDSBcD c5 Button-kDSBcD c6 Button-kDSBcD c7 c8"
onClick={[Function]}
type="button"
>
<svg
aria-hidden={true}
className="yoast-svg-icon yoast-svg-icon-circle c9"
fill="red"
focusable="false"
role="img"
size="16px"
viewBox="0 0 1792 1792"
xmlns="http://www.w3.org/2000/svg"
>
<path
d="M1664 896q0 209-103 385.5t-279.5 279.5-385.5 103-385.5-103-279.5-279.5-103-385.5 103-385.5 279.5-279.5 385.5-103 385.5 103 279.5 279.5 103 385.5z"
/>
</svg>
<span
className="c10"
>
Lorem ipsum dolor sit amet
</span>
<span
className="screen-reader-text"
style={
Object {
"clip": "rect(1px, 1px, 1px, 1px)",
"height": "1px",
"overflow": "hidden",
"position": "absolute",
"width": "1px",
}
}
>
bad SEO score
</span>
</button>
</h2>
<h4>
Vivamus rutrum velit ut nunc dignissim vulputate.
</h4>
<p>
In a purus quis leo dictum ultrices. Aenean commodo erat at pellentesque placerat.
</p>
<h4>
Ut id ex efficitur risus suscipit fermentum.
</h4>
<p>
Proin sed dolor neque. Vestibulum id leo ut ante luctus interdum sed ut sem.
</p>
</div>
`;