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

Add page navigation for the new feedback dashboard #28826

Merged
merged 21 commits into from
Feb 27, 2023
Merged
Changes from 13 commits
Commits
Show all changes
21 commits
Select commit Hold shift + click to select a range
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
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
Significance: patch
Type: added

Added a page navigation component for the new feedback dashboard
2 changes: 1 addition & 1 deletion projects/packages/forms/package.json
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
{
"private": true,
"name": "@automattic/jetpack-forms",
"version": "0.5.1",
"version": "0.5.2-alpha",
"description": "Jetpack Forms",
"homepage": "https://github.com/Automattic/jetpack/tree/HEAD/projects/packages/forms/#readme",
"bugs": {
2 changes: 1 addition & 1 deletion projects/packages/forms/src/class-jetpack-forms.php
Original file line number Diff line number Diff line change
@@ -14,7 +14,7 @@
*/
class Jetpack_Forms {

const PACKAGE_VERSION = '0.5.1';
const PACKAGE_VERSION = '0.5.2-alpha';

/**
* Load the contact form module.
107 changes: 107 additions & 0 deletions projects/packages/forms/src/dashboard/components/gridicon/index.jsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,107 @@
/* !!!
This is a fork of the Jetpack Gridicon code:
https://github.com/Automattic/jetpack/blob/f8078c2cd12ac508334da2fb08e37a92cf283c14/_inc/client/components/gridicon/index.jsx
It has been modified to work with Preact, and only includes the icons that we need.
!!! */

import { Component } from '@wordpress/element';
import { __ } from '@wordpress/i18n';

import './style.scss';

class Gridicon extends Component {
static defaultProps = {
'aria-hidden': 'false',
focusable: 'true',
};

needsOffset( icon, size ) {
const iconNeedsOffset = [ 'gridicons-arrow-left', 'gridicons-arrow-right', 'gridicons-star' ];

if ( iconNeedsOffset.indexOf( icon ) >= 0 ) {
return size % 18 === 0;
}
return false;
}

getSVGTitle( icon ) {
// Enable overriding title with falsy/truthy values.
if ( 'title' in this.props ) {
return this.props.title ? <title>{ this.props.title }</title> : null;
}

switch ( icon ) {
default:
return null;
case 'gridicons-arrow-left':
return <title>{ __( 'Arrow left', 'jetpack-forms' ) }</title>;
case 'gridicons-arrow-right':
return <title>{ __( 'Arrow right', 'jetpack-forms' ) }</title>;
case 'gridicons-search':
return <title>{ __( 'Magnifying Glass', 'jetpack-forms' ) }</title>;
}
}

renderIcon( icon ) {
switch ( icon ) {
default:
return null;
case 'gridicons-arrow-left':
return (
<g>
<path d="M20 11H7.83l5.59-5.59L12 4l-8 8 8 8 1.41-1.41L7.83 13H20v-2z" />
</g>
);
case 'gridicons-arrow-right':
return (
<g>
<path d="M12 4l-1.41 1.41L16.17 11H4v2h12.17l-5.58 5.59L12 20l8-8-8-8z" />
</g>
);
case 'gridicons-search':
return (
<g>
<path d="M21 19l-5.154-5.154C16.574 12.742 17 11.42 17 10c0-3.866-3.134-7-7-7s-7 3.134-7 7 3.134 7 7 7c1.42 0 2.742-.426 3.846-1.154L19 21l2-2zM5 10c0-2.757 2.243-5 5-5s5 2.243 5 5-2.243 5-5 5-5-2.243-5-5z" />
</g>
);
}
}

render() {
const { size = 24, className = '' } = this.props;

const height = this.props.height || size;
const width = this.props.width || size;
const style = this.props.style || { height, width };

const icon = 'gridicons-' + this.props.icon,
needsOffset = this.needsOffset( icon, size );

let iconClass = [ 'gridicon', icon, className ];

if ( needsOffset ) {
iconClass.push( 'needs-offset' );
}
iconClass = iconClass.join( ' ' );

return (
<svg
aria-label={ this.props.description }
className={ iconClass }
focusable={ this.props.focusable }
height={ height }
onClick={ this.props.onClick }
style={ style }
viewBox="0 0 24 24"
width={ width }
xmlns="http://www.w3.org/2000/svg"
aria-hidden={ this.props[ 'aria-hidden' ] }
>
{ this.getSVGTitle( icon ) }
{ this.renderIcon( icon ) }
</svg>
);
}
}

export default Gridicon;
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
.gridicon {
fill: currentColor;
display: inline-block;

&.needs-offset g {
transform: translate( 1px, 1px ); /* translates to .5px because it's in a child element */
}

&.needs-offset-x g {
transform: translate( 1px, 0 ); /* only nudges horizontally */
}

&.needs-offset-y g {
transform: translate( 0, 1px ); /* only nudges vertically */
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,77 @@
import { useCallback } from '@wordpress/element';
import { __ } from '@wordpress/i18n';
import { filter, flatten, map, range } from 'lodash';
import Gridicon from '../Gridicon';
Copy link
Member

Choose a reason for hiding this comment

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

Can we pull from the Jetpack component (@automattic/jetpack-components) instead of adding this copy here?

class Gridicon extends Component< GridiconProps > {

Copy link
Member Author

Choose a reason for hiding this comment

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

I think we can, I'll make the change. Main reason I did it was I noticed a bunch of packages duplicating this component, which also makes me wonder if there's any edge cases related to that? Regardless of the use case here.

import PageNumber from './page-number';

import './style.scss';

const PageNavigation = ( { currentPage, expandedRange = 3, onSelectPage, pages } ) => {
const goToPrevious = useCallback( () => onSelectPage( currentPage - 1 ), [
currentPage,
onSelectPage,
] );
const goToNext = useCallback( () => onSelectPage( currentPage + 1 ), [
currentPage,
onSelectPage,
] );

const currentRange = range(
Math.max( 0, currentPage - expandedRange + 1 ),
Math.min( pages, currentPage + expandedRange - 1 ) + 1
);
const totalRange = filter(
flatten( [
expandedRange < currentPage && 1,
expandedRange + 1 < currentPage && ( currentPage === expandedRange + 2 ? 2 : Infinity ),
currentRange,
currentPage < pages - expandedRange &&
( currentPage === pages - expandedRange ? pages - 1 : Infinity ),
currentPage < pages - expandedRange + 1 && pages,
] )
);

return (
<div className="jp-forms__page-navigation">
<button
disabled={ currentPage === 1 }
className="jp-forms__page-navigation-button"
onClick={ goToPrevious }
>
<Gridicon icon="arrow-left" size={ 18 } />
{ __( 'Previous', 'jetpack-forms' ) }
</button>

{ map( totalRange, ( n, index ) =>
! isFinite( n ) ? (
<button
key={ `placeholder-${ index }` }
className="jp-forms__page-navigation-placeholder"
disabled
>
</button>
) : (
<PageNumber
key={ n }
className="jp-forms__page-navigation-button"
active={ n === currentPage }
page={ n }
onSelect={ onSelectPage }
/>
)
) }

<button
disabled={ currentPage === pages }
className="jp-forms__page-navigation-button"
onClick={ goToNext }
>
{ __( 'Next', 'jetpack-forms' ) }
<Gridicon icon="arrow-right" size={ 18 } />
</button>
</div>
);
};

export default PageNavigation;
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
import { useCallback } from '@wordpress/element';
import classNames from 'classnames';

const PageNumber = ( { active, className, page, onSelect } ) => {
const buttonClass = classNames( 'jp-forms__page-navigation-page-number', className, {
'is-active': active,
} );

const selectPage = useCallback( () => onSelect( page ), [ page, onSelect ] );

return (
<button className={ buttonClass } onClick={ selectPage }>
{ page }
</button>
);
};

export default PageNumber;
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
.jp-forms__page-navigation {
background-color: #fff;
border: 1px solid #dcdcde;
border-radius: 3px;
box-sizing: border-box;
display: flex;
flex-direction: row;
height: 36px;
justify-content: center;
padding: 0;
width: fit-content;
}

.jp-forms__page-navigation-button,
.jp-forms__page-navigation-button:hover,
.jp-forms__page-navigation-placeholder:hover,
.jp-forms__page-navigation-placeholder {
background: transparent;
border: 0;
box-sizing: border-box;
color: #101517;
cursor: pointer;
display: inline-flex;
font-weight: bold;
font-size: 13px;
height: 34px;
line-height: 18px;
text-align: center;
padding: 8px 12px;

&.is-active {
background-color: #101517;
color: #fff;
}

&:disabled {
cursor: default;

&:first-child, &:last-child {
color: #dcdcde;
}
}

.gridicon {
display: inline-flex;

&:first-child {
margin-right: 3px;
}

&:last-child {
margin-left: 3px;
}
}
}
11 changes: 11 additions & 0 deletions projects/packages/forms/src/dashboard/inbox/list.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
import { useState } from '@wordpress/element';
import { __ } from '@wordpress/i18n';
import { noop } from 'lodash';
import PageNavigation from '../components/page-navigation';
import Table from '../components/table';

const COLUMNS = [
@@ -51,6 +53,8 @@ const DATA = [
];

const InboxList = () => {
const [ currentPage, setCurrentPage ] = useState( 1 );

return (
<>
<Table
@@ -59,6 +63,13 @@ const InboxList = () => {
items={ DATA }
onSelectionChange={ noop }
/>

<PageNavigation
currentPage={ currentPage }
pages={ 10 }
onSelectPage={ setCurrentPage }
expandedRange={ 2 }
/>
</>
);
};
7 changes: 7 additions & 0 deletions projects/packages/forms/src/dashboard/inbox/style.scss
Original file line number Diff line number Diff line change
@@ -12,8 +12,11 @@
}

.jp-forms__inbox-content-column {
align-items: center;
box-sizing: border-box;
display: flex;
flex: 3;
flex-direction: column;

&:last-child {
display: none;
@@ -24,6 +27,10 @@
flex: 2;
}
}

.jp-forms__page-navigation {
margin: 16px 0;
}
}

.jp-forms__inbox-list,
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
Significance: patch
Type: other

This change doesn't affect the plugin.