Skip to content

Commit

Permalink
Merge pull request #21860 from Yoast/ux-feedback
Browse files Browse the repository at this point in the history
Dashboard: UX feedback
  • Loading branch information
thijsoo authored Nov 25, 2024
2 parents 390d1ee + 51428cf commit 5ed1a82
Show file tree
Hide file tree
Showing 4 changed files with 121 additions and 87 deletions.
61 changes: 36 additions & 25 deletions packages/js/src/dashboard/scores/components/score-chart.js
Original file line number Diff line number Diff line change
@@ -1,4 +1,6 @@
import { SkeletonLoader } from "@yoast/ui-library";
import { ArcElement, Chart, Tooltip } from "chart.js";
import classNames from "classnames";
import { Doughnut } from "react-chartjs-2";
import { SCORE_META } from "../score-meta";

Expand All @@ -12,29 +14,24 @@ Chart.register( ArcElement, Tooltip );
* @param {Score[]} scores The scores.
* @returns {Object} Parsed chart data.
*/
const transformScoresToGraphData = ( scores ) => {
const hexes = scores.map( ( { name } ) => SCORE_META[ name ].hex );

return {
labels: scores.map( ( { name } ) => SCORE_META[ name ].label ),
datasets: [
{
cutout: "82%",
data: scores.map( ( { amount } ) => amount ),
backgroundColor: hexes,
borderColor: hexes,
borderWidth: 1,
offset: 1,
hoverOffset: 5,
spacing: 1,
weight: 1,
animation: {
animateRotate: true,
},
const transformScoresToGraphData = ( scores ) => ( {
labels: scores.map( ( { name } ) => SCORE_META[ name ].label ),
datasets: [
{
cutout: "82%",
data: scores.map( ( { amount } ) => amount ),
backgroundColor: scores.map( ( { name } ) => SCORE_META[ name ].hex ),
borderWidth: 0,
offset: 0,
hoverOffset: 5,
spacing: 1,
weight: 1,
animation: {
animateRotate: true,
},
],
};
};
},
],
} );

const chartOptions = {
plugins: {
Expand All @@ -47,16 +44,30 @@ const chartOptions = {
},
},
},
layout: {
padding: 5,
},
};

/**
*
* @param {string} [className] The class name.
* @returns {JSX.Element} The element.
*/
export const ScoreChartSkeletonLoader = ( { className } ) => (
<div className={ classNames( className, "yst-relative" ) }>
<SkeletonLoader className="yst-w-full yst-aspect-square yst-rounded-full" />
<div className="yst-absolute yst-inset-5 yst-aspect-square yst-bg-white yst-rounded-full" />
</div>
);

/**
* @param {string} [className] The class name.
* @param {Score[]} scores The scores.
* @returns {JSX.Element} The element.
*/
export const ScoreChart = ( { scores } ) => {
export const ScoreChart = ( { className, scores } ) => {
return (
<div className="yst-col-span-3">
<div className={ className }>
<Doughnut
options={ chartOptions }
data={ transformScoresToGraphData( scores ) }
Expand Down
42 changes: 18 additions & 24 deletions packages/js/src/dashboard/scores/components/score-content.js
Original file line number Diff line number Diff line change
@@ -1,38 +1,32 @@
import { SkeletonLoader } from "@yoast/ui-library";
import { SCORE_META } from "../score-meta";
import { ContentStatusDescription } from "./content-status-description";
import { ScoreChart } from "./score-chart";
import { ScoreList } from "./score-list";
import { ScoreChart, ScoreChartSkeletonLoader } from "./score-chart";
import { ScoreList, ScoreListSkeletonLoader } from "./score-list";

/**
* @type {import("../index").Score} Score
* @type {import("../index").ScoreType} ScoreType
*/

/**
* @type {{container: string, list: string, chart: string}}
*/
const CLASSNAMES = {
container: "yst-flex yst-flex-col @md:yst-flex-row yst-gap-12 yst-mt-6",
list: "yst-grow",
// Calculation: (line-height 1rem + py 0.375rem * 2) * 4 + (spacing 0.75rem * 2 + border 1px ) * 3 = 11.5rem + 3px.
chart: "yst-w-[calc(11.5rem+3px)] yst-aspect-square",
};

/**
* @returns {JSX.Element} The element.
*/
const ScoreContentSkeletonLoader = () => (
<>
<SkeletonLoader className="yst-w-full">&nbsp;</SkeletonLoader>
<div className="yst-grid yst-grid-cols-1 @md:yst-grid-cols-7 yst-gap-6 yst-mt-6">
<ul className="yst-col-span-4">
{ Object.entries( SCORE_META ).map( ( [ name, { label } ] ) => (
<li
key={ `skeleton-loader--${ name }` }
className="yst-flex yst-items-center yst-min-h-[1rem] yst-py-3 yst-border-b last:yst-border-b-0"
>
<SkeletonLoader className="yst-w-3 yst-h-3 yst-rounded-full" />
<SkeletonLoader className="yst-ml-3 yst-mr-2">{ label }</SkeletonLoader>
<SkeletonLoader className="yst-w-7">1</SkeletonLoader>
<SkeletonLoader className="yst-ml-auto yst-button yst-button--small">View</SkeletonLoader>
</li>
) ) }
</ul>
<div className="yst-col-span-3 yst-relative">
<SkeletonLoader className="yst-w-full yst-aspect-square yst-rounded-full" />
<div className="yst-absolute yst-inset-5 yst-aspect-square yst-bg-white yst-rounded-full" />
</div>
<div className={ CLASSNAMES.container }>
<ScoreListSkeletonLoader className={ CLASSNAMES.list } />
<ScoreChartSkeletonLoader className={ CLASSNAMES.chart } />
</div>
</>
);
Expand All @@ -51,9 +45,9 @@ export const ScoreContent = ( { scores = [], isLoading, descriptions } ) => {
return (
<>
<ContentStatusDescription scores={ scores } descriptions={ descriptions } />
<div className="yst-grid yst-grid-cols-1 @md:yst-grid-cols-7 yst-gap-6 yst-mt-6">
{ scores && <ScoreList scores={ scores } /> }
{ scores && <ScoreChart scores={ scores } /> }
<div className={ CLASSNAMES.container }>
{ scores && <ScoreList className={ CLASSNAMES.list } scores={ scores } /> }
{ scores && <ScoreChart className={ CLASSNAMES.chart } scores={ scores } /> }
</div>
</>
);
Expand Down
45 changes: 38 additions & 7 deletions packages/js/src/dashboard/scores/components/score-list.js
Original file line number Diff line number Diff line change
@@ -1,24 +1,55 @@
import { Badge, Button } from "@yoast/ui-library";
import { Badge, Button, Label, SkeletonLoader } from "@yoast/ui-library";
import classNames from "classnames";
import { SCORE_META } from "../score-meta";

/**
* @type {import("../index").Score} Score
*/

/**
* @type {{listItem: string, score: string}}
*/
const CLASSNAMES = {
listItem: "yst-flex yst-items-center yst-py-3 first:yst-pt-0 last:yst-pb-0 yst-border-b last:yst-border-b-0",
score: "yst-shrink-0 yst-w-3 yst-aspect-square yst-rounded-full",
label: "yst-ml-3 yst-mr-2",
};

/**
* @param {string} [className] The class name for the UL.
* @returns {JSX.Element} The element.
*/
export const ScoreListSkeletonLoader = ( { className } ) => (
<ul className={ className }>
{ Object.entries( SCORE_META ).map( ( [ name, { label } ] ) => (
<li
key={ `skeleton-loader--${ name }` }
className={ CLASSNAMES.listItem }
>
<SkeletonLoader className={ CLASSNAMES.score } />
<SkeletonLoader className={ CLASSNAMES.label }>{ label }</SkeletonLoader>
<SkeletonLoader className="yst-w-7 yst-mr-3">1</SkeletonLoader>
<SkeletonLoader className="yst-ml-auto yst-button yst-button--small">View</SkeletonLoader>
</li>
) ) }
</ul>
);

/**
* @param {string} [className] The class name for the UL.
* @param {Score[]} scores The scores.
* @returns {JSX.Element} The element.
*/
export const ScoreList = ( { scores } ) => (
<ul className="yst-col-span-4">
export const ScoreList = ( { className, scores } ) => (
<ul className={ className }>
{ scores.map( ( score ) => (
<li
key={ score.name }
className="yst-flex yst-items-center yst-min-h-[1rem] yst-py-3 yst-border-b last:yst-border-b-0"
className={ CLASSNAMES.listItem }
>
<span className={ `yst-rounded-full yst-w-3 yst-h-3 ${ SCORE_META[ score.name ].color }` } />
<span className="yst-ml-3 yst-mr-2 yst-leading-4 yst-py-1.5">{ SCORE_META[ score.name ].label }</span>
<Badge variant="plain">{ score.amount }</Badge>
<span className={ classNames( CLASSNAMES.score, SCORE_META[ score.name ].color ) } />
<Label as="span" className={ classNames( CLASSNAMES.label, "yst-leading-4 yst-py-1.5" ) }>{ SCORE_META[ score.name ].label }</Label>
<Badge variant="plain" className={ classNames( score.links.view && "yst-mr-3" ) }>{ score.amount }</Badge>
{ score.links.view && (
<Button as="a" variant="secondary" size="small" href={ score.links.view } className="yst-ml-auto">View</Button>
) }
Expand Down
60 changes: 29 additions & 31 deletions packages/js/src/general/app.js
Original file line number Diff line number Diff line change
Expand Up @@ -37,37 +37,35 @@ const Menu = ( { idSuffix = "" } ) => {
<YoastLogo className="yst-w-40" { ...svgAriaProps } />
</Link>
</header>
<div className="yst-px-0.5 yst-space-y-6">
<ul className="yst-mt-1 yst-space-y-1">
<MenuItemLink
to={ ROUTES.dashboard }
label={ <>
<ChartPieIcon className="yst-sidebar-navigation__icon yst-w-6 yst-h-6" />
{ __( "Dashboard", "wordpress-seo" ) }
</> }
idSuffix={ idSuffix }
className="yst-gap-3"
/>
<MenuItemLink
to={ ROUTES.alertCenter }
label={ <>
<BellIcon className="yst-sidebar-navigation__icon yst-w-6 yst-h-6" />
{ __( "Alert center", "wordpress-seo" ) }
</> }
idSuffix={ idSuffix }
className="yst-gap-3"
/>
<MenuItemLink
to={ ROUTES.firstTimeConfiguration }
label={ <>
<AdjustmentsIcon className="yst-sidebar-navigation__icon yst-w-6 yst-h-6" />
{ __( "First-time configuration", "wordpress-seo" ) }
</> }
idSuffix={ idSuffix }
className="yst-gap-3"
/>
</ul>
</div>
<ul className="yst-mt-1 yst-px-0.5 yst-space-y-4">
<MenuItemLink
to={ ROUTES.dashboard }
label={ <>
<ChartPieIcon className="yst-sidebar-navigation__icon yst-w-6 yst-h-6" />
{ __( "Dashboard", "wordpress-seo" ) }
</> }
idSuffix={ idSuffix }
className="yst-gap-3"
/>
<MenuItemLink
to={ ROUTES.alertCenter }
label={ <>
<BellIcon className="yst-sidebar-navigation__icon yst-w-6 yst-h-6" />
{ __( "Alert center", "wordpress-seo" ) }
</> }
idSuffix={ idSuffix }
className="yst-gap-3"
/>
<MenuItemLink
to={ ROUTES.firstTimeConfiguration }
label={ <>
<AdjustmentsIcon className="yst-sidebar-navigation__icon yst-w-6 yst-h-6" />
{ __( "First-time configuration", "wordpress-seo" ) }
</> }
idSuffix={ idSuffix }
className="yst-gap-3"
/>
</ul>
</>;
};
Menu.propTypes = {
Expand Down

0 comments on commit 5ed1a82

Please sign in to comment.