From 4106aac066ec811eb75b03543f8fbd510b879906 Mon Sep 17 00:00:00 2001 From: Vraja Das Date: Fri, 3 Jan 2025 09:44:21 +0200 Subject: [PATCH 01/27] WIP site kit tables --- .../js/src/dashboard/components/dashboard.js | 52 ++++++++++++++ .../scores/components/site-kit-table.js | 68 +++++++++++++++++++ 2 files changed, 120 insertions(+) create mode 100644 packages/js/src/dashboard/scores/components/site-kit-table.js diff --git a/packages/js/src/dashboard/components/dashboard.js b/packages/js/src/dashboard/components/dashboard.js index 62f58e02edc..bc9b4dd4f21 100644 --- a/packages/js/src/dashboard/components/dashboard.js +++ b/packages/js/src/dashboard/components/dashboard.js @@ -1,4 +1,6 @@ +import { __ } from "@wordpress/i18n"; import { Scores } from "../scores/components/scores"; +import { SiteKitTable } from "../scores/components/site-kit-table"; import { PageTitle } from "./page-title"; /** @@ -18,6 +20,53 @@ import { PageTitle } from "./page-title"; * @returns {JSX.Element} The element. */ export const Dashboard = ( { contentTypes, userName, features, endpoints, headers, links } ) => { + + const columnsNames = [ + __( "Landing page", "wordpress-seo" ), + __( "Clicks", "wordpress-seo" ), + __( "Impressions", "wordpress-seo" ), + __( "CTR", "wordpress-seo" ), + __( "Average position", "wordpress-seo" ), + __( "SEO score", "wordpress-seo" ), + ]; + + const data = [ + [ "https://example.com", + 100, + 1000, + 10, + 1, + 100, + ], + [ "https://example.com", + 100, + 1000, + 10, + 1, + 100, + ], + [ "https://example.com", + 100, + 1000, + 10, + 1, + 100, + ], + [ "https://example.com", + 100, + 1000, + 10, + 1, + 100, + ], + [ "https://example.com", + 100, + 1000, + 10, + 1, + 100, + ], + ]; return ( <> @@ -28,7 +77,10 @@ export const Dashboard = ( { contentTypes, userName, features, endpoints, header { features.indexables && features.readabilityAnalysis && ( ) } + + + ); }; diff --git a/packages/js/src/dashboard/scores/components/site-kit-table.js b/packages/js/src/dashboard/scores/components/site-kit-table.js new file mode 100644 index 00000000000..5adbde200ab --- /dev/null +++ b/packages/js/src/dashboard/scores/components/site-kit-table.js @@ -0,0 +1,68 @@ +import { ChevronDownIcon } from '@heroicons/react/solid'; +import { __ } from '@wordpress/i18n'; +import { Paper, Table, Title } from '@yoast/ui-library'; + + +const FirstCell = ( { value, index } ) => ( + + { index + 1 } + { value } + +); + +const LastCell = ( { score } ) => ( +
+ { score } +
+ +); + + +/** + * The Site Kit table component. + * + * @param {string} title The component props. + * @param {[string]} columnsNames The component props. + * @param {[object]} data The component props. + * + * @returns {JSX.Element} The element. + */ +export const SiteKitTable = ( { title, columnsNames, data } ) => { + return ( + + + { title } + + + + + + { columnsNames.map( ( columnName ) => + +
+ { columnName } + { columnName === __( "Clicks", "wordpress-seo") && } +
+
+ ) } +
+
+ + + { data.map( ( row, rowIndex ) => ( + + { row.map( ( cell, cellIndex ) => ( + + { cellIndex === 0 && } + + ))} + + + ) ) } + +
+
+ ); +}; From 9736b1b1d9a6ff5c02f8b6001906094e38b7618e Mon Sep 17 00:00:00 2001 From: Vraja Das Date: Mon, 6 Jan 2025 16:19:29 +0200 Subject: [PATCH 02/27] WIP site kit table component --- css/src/general-page.css | 20 +++++ .../js/src/dashboard/components/dashboard.js | 28 +++---- .../scores/components/site-kit-table.js | 78 +++++++++---------- 3 files changed, 72 insertions(+), 54 deletions(-) diff --git a/css/src/general-page.css b/css/src/general-page.css index d134b01a032..66162ad56cf 100644 --- a/css/src/general-page.css +++ b/css/src/general-page.css @@ -248,6 +248,26 @@ body.toplevel_page_wpseo_dashboard { } } + + .yst-site-kit-widget-table .yst-numbered-cell:first-child { + counter-increment: row-number; + @apply yst-relative yst-text-slate-900 yst-font-medium; + } + + .yst-site-kit-widget-table .yst-numbered-cell:first-child, .yst-site-kit-widget-table th:first-child{ + padding-left: 22px!important; + } + + .yst-numbered-cell:first-child::before { + content: counter(row-number) ". "; + @apply yst-absolute yst-left-0 yst-text-slate-500 yst-font-normal; + } + + /* Initialize the counter at the parent element level */ + .yst-parent-numbered-cell { + counter-reset: row-number; + } + /* RTL */ &.rtl { diff --git a/packages/js/src/dashboard/components/dashboard.js b/packages/js/src/dashboard/components/dashboard.js index bc9b4dd4f21..38fd979b363 100644 --- a/packages/js/src/dashboard/components/dashboard.js +++ b/packages/js/src/dashboard/components/dashboard.js @@ -20,47 +20,46 @@ import { PageTitle } from "./page-title"; * @returns {JSX.Element} The element. */ export const Dashboard = ( { contentTypes, userName, features, endpoints, headers, links } ) => { - - const columnsNames = [ - __( "Landing page", "wordpress-seo" ), - __( "Clicks", "wordpress-seo" ), - __( "Impressions", "wordpress-seo" ), - __( "CTR", "wordpress-seo" ), - __( "Average position", "wordpress-seo" ), - __( "SEO score", "wordpress-seo" ), + const columns = [ + { name: "landing-page", label: __( "Landing page", "wordpress-seo" ) }, + { name: "clicks", label: __( "Clicks", "wordpress-seo" ), sortable: true }, + { name: "impressions", label: __( "Impressions", "wordpress-seo" ) }, + { name: "ctr", label: __( "CTR", "wordpress-seo" ) }, + { name: "average-position", label: __( "Average position", "wordpress-seo" ) }, + { name: "seo-score", label: __( "SEO score", "wordpress-seo" ) }, ]; const data = [ [ "https://example.com", - 100, + 10, 1000, 10, 1, 100, ], [ "https://example.com", - 100, + 90, 1000, 10, 1, 100, ], [ "https://example.com", - 100, + 50, 1000, 10, 1, 100, ], [ "https://example.com", - 100, + 0, 1000, 10, 1, 100, ], [ "https://example.com", - 100, + 70, 1000, 10, 1, @@ -71,6 +70,7 @@ export const Dashboard = ( { contentTypes, userName, features, endpoints, header <>
+ { features.indexables && features.seoAnalysis && ( ) } @@ -78,7 +78,7 @@ export const Dashboard = ( { contentTypes, userName, features, endpoints, header ) } - +
diff --git a/packages/js/src/dashboard/scores/components/site-kit-table.js b/packages/js/src/dashboard/scores/components/site-kit-table.js index 5adbde200ab..ce2b9d295ad 100644 --- a/packages/js/src/dashboard/scores/components/site-kit-table.js +++ b/packages/js/src/dashboard/scores/components/site-kit-table.js @@ -1,64 +1,58 @@ -import { ChevronDownIcon } from '@heroicons/react/solid'; -import { __ } from '@wordpress/i18n'; -import { Paper, Table, Title } from '@yoast/ui-library'; +import { ChevronDownIcon } from "@heroicons/react/solid"; +import { Paper, Table, Title } from "@yoast/ui-library"; - -const FirstCell = ( { value, index } ) => ( - - { index + 1 } - { value } - -); - -const LastCell = ( { score } ) => ( -
- { score } +/** + * The sortable header component. + * @param {string} columnName The column name. + * @returns {JSX.Element} The element. + */ +const SortableHeader = ( { columnName } ) => ( +
+ { columnName } +
- + ); /** * The Site Kit table component. - * - * @param {string} title The component props. - * @param {[string]} columnsNames The component props. - * @param {[object]} data The component props. - * + * + * @param {string} title The table title. + * @param {[object]} columns The columns properties. + * @param {[object]} data The rows data. + * * @returns {JSX.Element} The element. */ -export const SiteKitTable = ( { title, columnsNames, data } ) => { +const SiteKitTable = ( { title, columns, data } ) => { return ( - - + <Paper className="yst-grow yst-max-w-screen-sm yst-p-8 yst-shadow-md"> + <Title as="h3" size="2"> { title } - - +
+ - { columnsNames.map( ( columnName ) => - -
- { columnName } - { columnName === __( "Clicks", "wordpress-seo") && } -
-
+ { columns.map( ( column ) => + + { column.sortable ? : column.label } + + ) }
- + { data.map( ( row, rowIndex ) => ( { row.map( ( cell, cellIndex ) => ( - - { cellIndex === 0 && } - - ))} - + + { cell } + ) ) } ) ) } @@ -66,3 +60,7 @@ export const SiteKitTable = ( { title, columnsNames, data } ) => { ); }; + +SiteKitTable.SortableHeader = SortableHeader; + +export { SiteKitTable }; From 6cf6219c5a15cbea0ebf011a383337b0847a60c9 Mon Sep 17 00:00:00 2001 From: Vraja Das Date: Tue, 7 Jan 2025 14:37:12 +0200 Subject: [PATCH 03/27] add sorting functionality and fix layout --- css/src/general-page.css | 11 ++- .../js/src/dashboard/components/dashboard.js | 68 ++------------ .../src/dashboard/scores/components/scores.js | 64 ++++++------- .../scores/components/site-kit-table.js | 91 +++++++++++++++---- 4 files changed, 125 insertions(+), 109 deletions(-) diff --git a/css/src/general-page.css b/css/src/general-page.css index 66162ad56cf..61e1e649e3d 100644 --- a/css/src/general-page.css +++ b/css/src/general-page.css @@ -251,14 +251,17 @@ body.toplevel_page_wpseo_dashboard { .yst-site-kit-widget-table .yst-numbered-cell:first-child { counter-increment: row-number; - @apply yst-relative yst-text-slate-900 yst-font-medium; + @apply yst-relative; } + .yst-site-kit-widget-table .yst-numbered-cell:first-child div { + @apply yst-text-slate-900 yst-font-medium; + } .yst-site-kit-widget-table .yst-numbered-cell:first-child, .yst-site-kit-widget-table th:first-child{ padding-left: 22px!important; } - .yst-numbered-cell:first-child::before { + .yst-site-kit-widget-table .yst-numbered-cell:first-child::before { content: counter(row-number) ". "; @apply yst-absolute yst-left-0 yst-text-slate-500 yst-font-normal; } @@ -268,6 +271,10 @@ body.toplevel_page_wpseo_dashboard { counter-reset: row-number; } + .yst-site-kit-widget-table th:last-child div { + @apply yst-text-end; + } + /* RTL */ &.rtl { diff --git a/packages/js/src/dashboard/components/dashboard.js b/packages/js/src/dashboard/components/dashboard.js index 38fd979b363..a62768a1833 100644 --- a/packages/js/src/dashboard/components/dashboard.js +++ b/packages/js/src/dashboard/components/dashboard.js @@ -1,6 +1,4 @@ -import { __ } from "@wordpress/i18n"; import { Scores } from "../scores/components/scores"; -import { SiteKitTable } from "../scores/components/site-kit-table"; import { PageTitle } from "./page-title"; /** @@ -20,67 +18,19 @@ import { PageTitle } from "./page-title"; * @returns {JSX.Element} The element. */ export const Dashboard = ( { contentTypes, userName, features, endpoints, headers, links } ) => { - const columns = [ - { name: "landing-page", label: __( "Landing page", "wordpress-seo" ) }, - { name: "clicks", label: __( "Clicks", "wordpress-seo" ), sortable: true }, - { name: "impressions", label: __( "Impressions", "wordpress-seo" ) }, - { name: "ctr", label: __( "CTR", "wordpress-seo" ) }, - { name: "average-position", label: __( "Average position", "wordpress-seo" ) }, - { name: "seo-score", label: __( "SEO score", "wordpress-seo" ) }, - ]; - - const data = [ - [ "https://example.com", - 10, - 1000, - 10, - 1, - 100, - ], - [ "https://example.com", - 90, - 1000, - 10, - 1, - 100, - ], - [ "https://example.com", - 50, - 1000, - 10, - 1, - 100, - ], - [ "https://example.com", - 0, - 1000, - 10, - 1, - 100, - ], - [ "https://example.com", - 70, - 1000, - 10, - 1, - 100, - ], - ]; return ( <> -
- - { features.indexables && features.seoAnalysis && ( - - ) } - { features.indexables && features.readabilityAnalysis && ( - - ) } - - +
+
+ { features.indexables && features.seoAnalysis && ( + + ) } + { features.indexables && features.readabilityAnalysis && ( + + ) } +
- ); }; diff --git a/packages/js/src/dashboard/scores/components/scores.js b/packages/js/src/dashboard/scores/components/scores.js index a2d0917a3ad..fc986e02d20 100644 --- a/packages/js/src/dashboard/scores/components/scores.js +++ b/packages/js/src/dashboard/scores/components/scores.js @@ -107,39 +107,41 @@ export const Scores = ( { analysisType, contentTypes, endpoint, headers } ) => { }, [ selectedContentType.name ] ); return ( - - - { analysisType === "readability" - ? __( "Readability scores", "wordpress-seo" ) - : __( "SEO scores", "wordpress-seo" ) - } - -
- - { selectedContentType.taxonomy && selectedContentType.taxonomy?.links?.search && - +
+ + { analysisType === "readability" + ? __( "Readability scores", "wordpress-seo" ) + : __( "SEO scores", "wordpress-seo" ) + } + +
+ - } -
-
- - { ! error && ( - - ) } + { selectedContentType.taxonomy && selectedContentType.taxonomy?.links?.search && + + } +
+
+ + { ! error && ( + + ) } +
); diff --git a/packages/js/src/dashboard/scores/components/site-kit-table.js b/packages/js/src/dashboard/scores/components/site-kit-table.js index ce2b9d295ad..c54834a8576 100644 --- a/packages/js/src/dashboard/scores/components/site-kit-table.js +++ b/packages/js/src/dashboard/scores/components/site-kit-table.js @@ -1,20 +1,45 @@ -import { ChevronDownIcon } from "@heroicons/react/solid"; +import { ChevronDownIcon, ChevronUpIcon } from "@heroicons/react/solid"; +import { useState, useCallback } from "@wordpress/element"; +import classNames from "classnames"; +import { SCORE_META } from "../score-meta"; import { Paper, Table, Title } from "@yoast/ui-library"; /** * The sortable header component. + * * @param {string} columnName The column name. + * @param {boolean} isAscending Whether the sorting is ascending. + * @param {Function} onClick The click handler. + * @param {number} index The index of the column. * @returns {JSX.Element} The element. */ -const SortableHeader = ( { columnName } ) => ( -
+const SortableHeader = ( { columnName, isAscending, onClick, index } ) => { + const handleSort = useCallback( () => { + onClick( index ); + }, [ onClick, index ] ); + + const ChevronIcon = isAscending ? ChevronDownIcon : ChevronUpIcon; + + return
+ +
; +}; +/** + * The score bullet component. + * + * @param {string} score The score. + * @returns {JSX.Element} The element. + */ +const ScoreBullet = ( { score } ) => ( +
+ + { SCORE_META[ score ].label } + +
); - /** * The Site Kit table component. * @@ -25,33 +50,65 @@ const SortableHeader = ( { columnName } ) => ( * @returns {JSX.Element} The element. */ const SiteKitTable = ( { title, columns, data } ) => { + const [ sortConfig, setSortConfig ] = useState( { key: null, direction: "ascending" } ); + + const sortedData = [ ...data ].sort( ( a, b ) => { + if ( sortConfig.key === null ) { + return 0; + } + const aValue = a[ sortConfig.key ]; + const bValue = b[ sortConfig.key ]; + if ( aValue < bValue ) { + return sortConfig.direction === "ascending" ? -1 : 1; + } + if ( aValue > bValue ) { + return sortConfig.direction === "ascending" ? 1 : -1; + } + return 0; + } ); + + const handleSort = useCallback( ( columnIndex ) => { + let direction = "ascending"; + if ( sortConfig.key === columnIndex && sortConfig.direction === "ascending" ) { + direction = "descending"; + } + setSortConfig( { key: columnIndex, direction } ); + }, [ sortConfig, setSortConfig ] ); + return ( - - + <Paper className="yst-grow yst-p-8 yst-shadow-md"> + <Title as="h3" size="2" className="yst-text-slate-900 yst-font-medium"> { title }
- - { columns.map( ( column ) => + { columns.map( ( column, index ) => - { column.sortable ? : column.label } - + { column.sortable ? ( + + ) :
{ column.label }
}
- ) } + ) }
- - { data.map( ( row, rowIndex ) => ( + { sortedData.map( ( row, rowIndex ) => ( { row.map( ( cell, cellIndex ) => ( - { cell } + { columns[ cellIndex ].name === "seo-score" ? :
{ cell }
}
) ) }
) ) } From e73b7d11436e2f527e6bcc4df1cd05b46b43d583 Mon Sep 17 00:00:00 2001 From: Vraja Das Date: Tue, 7 Jan 2025 14:49:26 +0200 Subject: [PATCH 04/27] simplify sort direction prop --- .../scores/components/site-kit-table.js | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/packages/js/src/dashboard/scores/components/site-kit-table.js b/packages/js/src/dashboard/scores/components/site-kit-table.js index c54834a8576..3d4fdfec88f 100644 --- a/packages/js/src/dashboard/scores/components/site-kit-table.js +++ b/packages/js/src/dashboard/scores/components/site-kit-table.js @@ -50,7 +50,7 @@ const ScoreBullet = ( { score } ) => ( * @returns {JSX.Element} The element. */ const SiteKitTable = ( { title, columns, data } ) => { - const [ sortConfig, setSortConfig ] = useState( { key: null, direction: "ascending" } ); + const [ sortConfig, setSortConfig ] = useState( { key: null, ascending: true } ); const sortedData = [ ...data ].sort( ( a, b ) => { if ( sortConfig.key === null ) { @@ -59,20 +59,20 @@ const SiteKitTable = ( { title, columns, data } ) => { const aValue = a[ sortConfig.key ]; const bValue = b[ sortConfig.key ]; if ( aValue < bValue ) { - return sortConfig.direction === "ascending" ? -1 : 1; + return sortConfig.ascending ? -1 : 1; } if ( aValue > bValue ) { - return sortConfig.direction === "ascending" ? 1 : -1; + return sortConfig.ascending ? 1 : -1; } return 0; } ); const handleSort = useCallback( ( columnIndex ) => { - let direction = "ascending"; - if ( sortConfig.key === columnIndex && sortConfig.direction === "ascending" ) { - direction = "descending"; + let ascending = true; + if ( sortConfig.key === columnIndex && sortConfig.ascending ) { + ascending = false; } - setSortConfig( { key: columnIndex, direction } ); + setSortConfig( { key: columnIndex, ascending } ); }, [ sortConfig, setSortConfig ] ); return ( @@ -91,7 +91,7 @@ const SiteKitTable = ( { title, columns, data } ) => { { column.sortable ? ( From 462b89a321e8363179dc861fa22252b7bf11c6d9 Mon Sep 17 00:00:00 2001 From: Vraja Das Date: Tue, 7 Jan 2025 15:15:15 +0200 Subject: [PATCH 05/27] add tests to SiteKitTable --- .../scores/components/site-kit-table.js | 6 +-- .../scores/components/site-kit-table.test.js | 53 +++++++++++++++++++ 2 files changed, 54 insertions(+), 5 deletions(-) create mode 100644 packages/js/tests/dashboard/scores/components/site-kit-table.test.js diff --git a/packages/js/src/dashboard/scores/components/site-kit-table.js b/packages/js/src/dashboard/scores/components/site-kit-table.js index 3d4fdfec88f..b0f62cbfd7b 100644 --- a/packages/js/src/dashboard/scores/components/site-kit-table.js +++ b/packages/js/src/dashboard/scores/components/site-kit-table.js @@ -49,7 +49,7 @@ const ScoreBullet = ( { score } ) => ( * * @returns {JSX.Element} The element. */ -const SiteKitTable = ( { title, columns, data } ) => { +export const SiteKitTable = ( { title, columns, data } ) => { const [ sortConfig, setSortConfig ] = useState( { key: null, ascending: true } ); const sortedData = [ ...data ].sort( ( a, b ) => { @@ -117,7 +117,3 @@ const SiteKitTable = ( { title, columns, data } ) => { ); }; - -SiteKitTable.SortableHeader = SortableHeader; - -export { SiteKitTable }; diff --git a/packages/js/tests/dashboard/scores/components/site-kit-table.test.js b/packages/js/tests/dashboard/scores/components/site-kit-table.test.js new file mode 100644 index 00000000000..d429223c8b1 --- /dev/null +++ b/packages/js/tests/dashboard/scores/components/site-kit-table.test.js @@ -0,0 +1,53 @@ +import { render, screen, fireEvent } from "@testing-library/react"; +import "@testing-library/jest-dom"; +import { SiteKitTable } from "../../../../src/dashboard/scores/components/site-kit-table.js"; +import { SCORE_META } from "../../../../src/dashboard/scores/score-meta.js"; + +describe( "SiteKitTable", () => { + const columns = [ + { name: "landing-page", label: "Landing page" }, + { name: "clicks", label: "Clicks", sortable: true }, + { name: "impressions", label: "Impressions" }, + { name: "ctr", label: "CTR" }, + { name: "average-position", label: "Average position" }, + { name: "seo-score", label: "SEO score" }, + ]; + + const data = [ + [ "https://example.com1", 10, 1001, 10, 1, "good" ], + [ "https://example.com2", 90, 1002, 9, 3, "bad" ], + [ "https://example.com3", 50, 1003, 8, 5, "good" ], + [ "https://example.com4", 0, 1004, 7, 2, "ok" ], + [ "https://example.com5", 70, 1005, 6, 4, "ok" ], + ]; + + it( "renders the table with correct title and columns", () => { + render( ); + expect( screen.getByText( "Test Table" ) ).toBeInTheDocument(); + columns.forEach( ( column ) => { + expect( screen.getByText( column.label ) ).toBeInTheDocument(); + } ); + } ); + + it( "renders the table rows correctly", () => { + render( ); + data.forEach( ( row ) => { + const rowElement = screen.getByText( row[ 0 ] ).closest( "tr" ); + expect( rowElement ).toHaveTextContent( row[ 0 ] ); + expect( rowElement ).toHaveTextContent( String( row[ 1 ] ) ); + expect( rowElement ).toHaveTextContent( String( row[ 2 ] ) ); + expect( rowElement ).toHaveTextContent( String( row[ 3 ] ) ); + expect( rowElement ).toHaveTextContent( String( row[ 4 ] ) ); + expect( rowElement ).toHaveTextContent( SCORE_META[ row[ 5 ] ].label ); + } ); + } ); + + it( "sorts the table correctly when a sortable column header is clicked", () => { + render( ); + const clicksHeader = screen.getByText( "Clicks" ); + fireEvent.click( clicksHeader ); + const sortedData = data.sort( ( a, b ) => a[ 1 ] - b[ 1 ] ); + const firstRow = screen.getAllByRole( "row" )[ 1 ]; + expect( firstRow ).toHaveTextContent( sortedData[ 0 ][ 0 ] ); + } ); +} ); From 848e423451db3bf29593fdbc83cae42688835c41 Mon Sep 17 00:00:00 2001 From: Vraja Das Date: Tue, 7 Jan 2025 16:04:45 +0200 Subject: [PATCH 06/27] remove hard coded padding --- css/src/general-page.css | 6 +++--- .../dashboard/scores/components/site-kit-table.js | 14 +++++++------- 2 files changed, 10 insertions(+), 10 deletions(-) diff --git a/css/src/general-page.css b/css/src/general-page.css index 61e1e649e3d..0534bf00ef3 100644 --- a/css/src/general-page.css +++ b/css/src/general-page.css @@ -254,11 +254,11 @@ body.toplevel_page_wpseo_dashboard { @apply yst-relative; } - .yst-site-kit-widget-table .yst-numbered-cell:first-child div { + .yst-site-kit-widget-table .yst-numbered-cell:first-child span { @apply yst-text-slate-900 yst-font-medium; } - .yst-site-kit-widget-table .yst-numbered-cell:first-child, .yst-site-kit-widget-table th:first-child{ - padding-left: 22px!important; + .yst-site-kit-widget-table .yst-numbered-cell:first-child span, .yst-site-kit-widget-table th:first-child span{ + @apply yst-ps-3; } .yst-site-kit-widget-table .yst-numbered-cell:first-child::before { diff --git a/packages/js/src/dashboard/scores/components/site-kit-table.js b/packages/js/src/dashboard/scores/components/site-kit-table.js index b0f62cbfd7b..284ece9d61f 100644 --- a/packages/js/src/dashboard/scores/components/site-kit-table.js +++ b/packages/js/src/dashboard/scores/components/site-kit-table.js @@ -20,10 +20,10 @@ const SortableHeader = ( { columnName, isAscending, onClick, index } ) => { const ChevronIcon = isAscending ? ChevronDownIcon : ChevronUpIcon; - return
; + ; }; /** @@ -33,11 +33,11 @@ const SortableHeader = ( { columnName, isAscending, onClick, index } ) => { * @returns {JSX.Element} The element. */ const ScoreBullet = ( { score } ) => ( -
+ { SCORE_META[ score ].label } -
+ ); /** @@ -95,7 +95,7 @@ export const SiteKitTable = ( { title, columns, data } ) => { index={ index } onClick={ handleSort } /> - ) :
{ column.label }
} + ) : { column.label } } ) } @@ -105,10 +105,10 @@ export const SiteKitTable = ( { title, columns, data } ) => { { row.map( ( cell, cellIndex ) => ( - { columns[ cellIndex ].name === "seo-score" ? :
: { cell }
} + >{ cell } }
) ) }
) ) } From d9c56400169c2b960646de50d3ad7a2e66173214 Mon Sep 17 00:00:00 2001 From: Vraja Das Date: Tue, 7 Jan 2025 16:16:44 +0200 Subject: [PATCH 07/27] center bullets --- css/src/general-page.css | 4 ---- packages/js/src/dashboard/scores/components/site-kit-table.js | 2 +- 2 files changed, 1 insertion(+), 5 deletions(-) diff --git a/css/src/general-page.css b/css/src/general-page.css index 0534bf00ef3..2319634e193 100644 --- a/css/src/general-page.css +++ b/css/src/general-page.css @@ -271,10 +271,6 @@ body.toplevel_page_wpseo_dashboard { counter-reset: row-number; } - .yst-site-kit-widget-table th:last-child div { - @apply yst-text-end; - } - /* RTL */ &.rtl { diff --git a/packages/js/src/dashboard/scores/components/site-kit-table.js b/packages/js/src/dashboard/scores/components/site-kit-table.js index 284ece9d61f..92b152c7c80 100644 --- a/packages/js/src/dashboard/scores/components/site-kit-table.js +++ b/packages/js/src/dashboard/scores/components/site-kit-table.js @@ -33,7 +33,7 @@ const SortableHeader = ( { columnName, isAscending, onClick, index } ) => { * @returns {JSX.Element} The element. */ const ScoreBullet = ( { score } ) => ( - + { SCORE_META[ score ].label } From d05d22cc69cd05ecc448154b5384ef4d5725b387 Mon Sep 17 00:00:00 2001 From: Vraja Das Date: Tue, 7 Jan 2025 16:42:42 +0200 Subject: [PATCH 08/27] remove classes and align bullet to the end center --- css/src/general-page.css | 14 ++++++++----- .../scores/components/site-kit-table.js | 20 ++++++++++--------- 2 files changed, 20 insertions(+), 14 deletions(-) diff --git a/css/src/general-page.css b/css/src/general-page.css index 2319634e193..21a04934884 100644 --- a/css/src/general-page.css +++ b/css/src/general-page.css @@ -249,28 +249,32 @@ body.toplevel_page_wpseo_dashboard { } - .yst-site-kit-widget-table .yst-numbered-cell:first-child { + .yst-site-kit-widget-table td:first-child { counter-increment: row-number; @apply yst-relative; } - .yst-site-kit-widget-table .yst-numbered-cell:first-child span { + .yst-site-kit-widget-table td:first-child div { @apply yst-text-slate-900 yst-font-medium; } - .yst-site-kit-widget-table .yst-numbered-cell:first-child span, .yst-site-kit-widget-table th:first-child span{ + .yst-site-kit-widget-table td:first-child div, .yst-site-kit-widget-table th:first-child div{ @apply yst-ps-3; } - .yst-site-kit-widget-table .yst-numbered-cell:first-child::before { + .yst-site-kit-widget-table td:first-child::before { content: counter(row-number) ". "; @apply yst-absolute yst-left-0 yst-text-slate-500 yst-font-normal; } /* Initialize the counter at the parent element level */ - .yst-parent-numbered-cell { + .yst-site-kit-widget-table tbody { counter-reset: row-number; } + .yst-site-kit-widget-table th:last-child div { + @apply yst-text-end; + } + /* RTL */ &.rtl { diff --git a/packages/js/src/dashboard/scores/components/site-kit-table.js b/packages/js/src/dashboard/scores/components/site-kit-table.js index 92b152c7c80..16de054edc9 100644 --- a/packages/js/src/dashboard/scores/components/site-kit-table.js +++ b/packages/js/src/dashboard/scores/components/site-kit-table.js @@ -33,11 +33,13 @@ const SortableHeader = ( { columnName, isAscending, onClick, index } ) => { * @returns {JSX.Element} The element. */ const ScoreBullet = ( { score } ) => ( - - - { SCORE_META[ score ].label } - - +
+
+ + { SCORE_META[ score ].label } + +
+
); /** @@ -95,20 +97,20 @@ export const SiteKitTable = ( { title, columns, data } ) => { index={ index } onClick={ handleSort } /> - ) : { column.label } } + ) :
{ column.label }
} ) } - + { sortedData.map( ( row, rowIndex ) => ( { row.map( ( cell, cellIndex ) => ( - { columns[ cellIndex ].name === "seo-score" ? : :
{ cell } } + >{ cell }
}
) ) }
) ) } From 70939feb2544ad25f90a110c2ce1a7b9c6e1f0ac Mon Sep 17 00:00:00 2001 From: Vraja Das Date: Fri, 10 Jan 2025 16:58:51 +0200 Subject: [PATCH 09/27] refactor dashboard table --- css/src/general-page.css | 27 -------- .../dashboard-table.js} | 9 ++- .../src/dashboard/scores/components/scores.js | 64 +++++++++---------- .../dashboard-table.test.js} | 14 ++-- 4 files changed, 44 insertions(+), 70 deletions(-) rename packages/js/src/dashboard/{scores/components/site-kit-table.js => components/dashboard-table.js} (91%) rename packages/js/tests/dashboard/{scores/components/site-kit-table.test.js => components/dashboard-table.test.js} (77%) diff --git a/css/src/general-page.css b/css/src/general-page.css index 21a04934884..d134b01a032 100644 --- a/css/src/general-page.css +++ b/css/src/general-page.css @@ -248,33 +248,6 @@ body.toplevel_page_wpseo_dashboard { } } - - .yst-site-kit-widget-table td:first-child { - counter-increment: row-number; - @apply yst-relative; - } - - .yst-site-kit-widget-table td:first-child div { - @apply yst-text-slate-900 yst-font-medium; - } - .yst-site-kit-widget-table td:first-child div, .yst-site-kit-widget-table th:first-child div{ - @apply yst-ps-3; - } - - .yst-site-kit-widget-table td:first-child::before { - content: counter(row-number) ". "; - @apply yst-absolute yst-left-0 yst-text-slate-500 yst-font-normal; - } - - /* Initialize the counter at the parent element level */ - .yst-site-kit-widget-table tbody { - counter-reset: row-number; - } - - .yst-site-kit-widget-table th:last-child div { - @apply yst-text-end; - } - /* RTL */ &.rtl { diff --git a/packages/js/src/dashboard/scores/components/site-kit-table.js b/packages/js/src/dashboard/components/dashboard-table.js similarity index 91% rename from packages/js/src/dashboard/scores/components/site-kit-table.js rename to packages/js/src/dashboard/components/dashboard-table.js index 16de054edc9..e1a37280fc3 100644 --- a/packages/js/src/dashboard/scores/components/site-kit-table.js +++ b/packages/js/src/dashboard/components/dashboard-table.js @@ -1,7 +1,7 @@ import { ChevronDownIcon, ChevronUpIcon } from "@heroicons/react/solid"; import { useState, useCallback } from "@wordpress/element"; import classNames from "classnames"; -import { SCORE_META } from "../score-meta"; +import { SCORE_META } from "../scores/score-meta"; import { Paper, Table, Title } from "@yoast/ui-library"; /** @@ -51,7 +51,7 @@ const ScoreBullet = ( { score } ) => ( * * @returns {JSX.Element} The element. */ -export const SiteKitTable = ( { title, columns, data } ) => { +export const DashboardTable = ( { title, columns, data } ) => { const [ sortConfig, setSortConfig ] = useState( { key: null, ascending: true } ); const sortedData = [ ...data ].sort( ( a, b ) => { @@ -78,13 +78,14 @@ export const SiteKitTable = ( { title, columns, data } ) => { }, [ sortConfig, setSortConfig ] ); return ( - + { title }
+ { "" } { columns.map( ( column, index ) => { { sortedData.map( ( row, rowIndex ) => ( + { rowIndex + 1 }. { row.map( ( cell, cellIndex ) => ( { columns[ cellIndex ].name === "seo-score" ? :
{ cell }
}
) ) } diff --git a/packages/js/src/dashboard/scores/components/scores.js b/packages/js/src/dashboard/scores/components/scores.js index fc986e02d20..a2d0917a3ad 100644 --- a/packages/js/src/dashboard/scores/components/scores.js +++ b/packages/js/src/dashboard/scores/components/scores.js @@ -107,41 +107,39 @@ export const Scores = ( { analysisType, contentTypes, endpoint, headers } ) => { }, [ selectedContentType.name ] ); return ( - -
- - { analysisType === "readability" - ? __( "Readability scores", "wordpress-seo" ) - : __( "SEO scores", "wordpress-seo" ) - } - -
- + + { analysisType === "readability" + ? __( "Readability scores", "wordpress-seo" ) + : __( "SEO scores", "wordpress-seo" ) + } + +
+ + { selectedContentType.taxonomy && selectedContentType.taxonomy?.links?.search && + - { selectedContentType.taxonomy && selectedContentType.taxonomy?.links?.search && - - } -
-
- - { ! error && ( - - ) } -
+ } +
+
+ + { ! error && ( + + ) }
); diff --git a/packages/js/tests/dashboard/scores/components/site-kit-table.test.js b/packages/js/tests/dashboard/components/dashboard-table.test.js similarity index 77% rename from packages/js/tests/dashboard/scores/components/site-kit-table.test.js rename to packages/js/tests/dashboard/components/dashboard-table.test.js index d429223c8b1..6cb144cd598 100644 --- a/packages/js/tests/dashboard/scores/components/site-kit-table.test.js +++ b/packages/js/tests/dashboard/components/dashboard-table.test.js @@ -1,9 +1,9 @@ -import { render, screen, fireEvent } from "@testing-library/react"; +import { render, screen, fireEvent } from "../../test-utils.js"; import "@testing-library/jest-dom"; -import { SiteKitTable } from "../../../../src/dashboard/scores/components/site-kit-table.js"; -import { SCORE_META } from "../../../../src/dashboard/scores/score-meta.js"; +import { DashboardTable } from "../../../src/dashboard/components/dashboard-table.js"; +import { SCORE_META } from "../../../src/dashboard/scores/score-meta.js"; -describe( "SiteKitTable", () => { +describe( "DashboardTable", () => { const columns = [ { name: "landing-page", label: "Landing page" }, { name: "clicks", label: "Clicks", sortable: true }, @@ -22,7 +22,7 @@ describe( "SiteKitTable", () => { ]; it( "renders the table with correct title and columns", () => { - render( ); + render( ); expect( screen.getByText( "Test Table" ) ).toBeInTheDocument(); columns.forEach( ( column ) => { expect( screen.getByText( column.label ) ).toBeInTheDocument(); @@ -30,7 +30,7 @@ describe( "SiteKitTable", () => { } ); it( "renders the table rows correctly", () => { - render( ); + render( ); data.forEach( ( row ) => { const rowElement = screen.getByText( row[ 0 ] ).closest( "tr" ); expect( rowElement ).toHaveTextContent( row[ 0 ] ); @@ -43,7 +43,7 @@ describe( "SiteKitTable", () => { } ); it( "sorts the table correctly when a sortable column header is clicked", () => { - render( ); + render( ); const clicksHeader = screen.getByText( "Clicks" ); fireEvent.click( clicksHeader ); const sortedData = data.sort( ( a, b ) => a[ 1 ] - b[ 1 ] ); From dc14803cd3ccecc5982bccff704b09f428fa0055 Mon Sep 17 00:00:00 2001 From: Vraja Das Date: Fri, 10 Jan 2025 17:04:41 +0200 Subject: [PATCH 10/27] revert dashboard changes revert dashboard changes --- .../js/src/dashboard/components/dashboard.js | 16 +++++++--------- 1 file changed, 7 insertions(+), 9 deletions(-) diff --git a/packages/js/src/dashboard/components/dashboard.js b/packages/js/src/dashboard/components/dashboard.js index a62768a1833..62f58e02edc 100644 --- a/packages/js/src/dashboard/components/dashboard.js +++ b/packages/js/src/dashboard/components/dashboard.js @@ -21,15 +21,13 @@ export const Dashboard = ( { contentTypes, userName, features, endpoints, header return ( <> -
-
- { features.indexables && features.seoAnalysis && ( - - ) } - { features.indexables && features.readabilityAnalysis && ( - - ) } -
+
+ { features.indexables && features.seoAnalysis && ( + + ) } + { features.indexables && features.readabilityAnalysis && ( + + ) }
); From 827f5b7a63261e9a7297c5c4527ede2d02fac9c8 Mon Sep 17 00:00:00 2001 From: Vraja Das Date: Mon, 13 Jan 2025 10:50:09 +0200 Subject: [PATCH 11/27] add type for columns and data, fix table overflow --- .../dashboard/components/dashboard-table.js | 85 ++++++++++--------- packages/js/src/dashboard/index.js | 14 +++ 2 files changed, 60 insertions(+), 39 deletions(-) diff --git a/packages/js/src/dashboard/components/dashboard-table.js b/packages/js/src/dashboard/components/dashboard-table.js index e1a37280fc3..8efc04825cd 100644 --- a/packages/js/src/dashboard/components/dashboard-table.js +++ b/packages/js/src/dashboard/components/dashboard-table.js @@ -4,6 +4,11 @@ import classNames from "classnames"; import { SCORE_META } from "../scores/score-meta"; import { Paper, Table, Title } from "@yoast/ui-library"; +/** + * @type {import("../index").Column} Column + * @type {import("../index").Data} Data + */ + /** * The sortable header component. * @@ -20,7 +25,7 @@ const SortableHeader = ( { columnName, isAscending, onClick, index } ) => { const ChevronIcon = isAscending ? ChevronDownIcon : ChevronUpIcon; - return ; @@ -46,8 +51,8 @@ const ScoreBullet = ( { score } ) => ( * The Site Kit table component. * * @param {string} title The table title. - * @param {[object]} columns The columns properties. - * @param {[object]} data The rows data. + * @param {Column[]} columns The columns properties. + * @param {Data} data The rows data. * * @returns {JSX.Element} The element. */ @@ -82,43 +87,45 @@ export const DashboardTable = ( { title, columns, data } ) => { { title } -
- - - { "" } - { columns.map( ( column, index ) => - - { column.sortable ? ( - - ) :
{ column.label }
} -
- ) } -
-
- - { sortedData.map( ( row, rowIndex ) => ( - - { rowIndex + 1 }. - { row.map( ( cell, cellIndex ) => ( - - { columns[ cellIndex ].name === "seo-score" ? :
{ cell }
} -
) ) } +
+
+ + + { "" } + { columns.map( ( column, index ) => + + { column.sortable ? ( + + ) :
{ column.label }
} +
+ ) }
- ) ) } - -
+ + + { sortedData.map( ( row, rowIndex ) => ( + + { rowIndex + 1 }. + { row.map( ( cell, cellIndex ) => ( + + { columns[ cellIndex ].name === "seo-score" ? :
{ cell }
} +
) ) } +
+ ) ) } +
+ +
); }; diff --git a/packages/js/src/dashboard/index.js b/packages/js/src/dashboard/index.js index 2c9b4ad479c..675c5bf5a83 100644 --- a/packages/js/src/dashboard/index.js +++ b/packages/js/src/dashboard/index.js @@ -54,3 +54,17 @@ export { Dashboard } from "./components/dashboard"; * @typedef {Object} Links The links. * @property {string} dashboardLearnMore The dashboard information link. */ + +/** + * @typedef {Object} Column A column in the table. + * @property {string} name The unique identifier. + * @property {string} label The user-facing label. + * @property {boolean} sortable Whether the column is sortable. + * @property {string} [className] The CSS class name for the column. + */ + +/** + * @typedef {Array>} Data The data for the table rows. + */ + + From 8bfa47c191f2ec431766b59a6daa5fc603d01b98 Mon Sep 17 00:00:00 2001 From: Vraja Das Date: Wed, 15 Jan 2025 12:28:22 +0200 Subject: [PATCH 12/27] remove sortable column --- .../dashboard/components/dashboard-table.js | 67 ++----------------- 1 file changed, 5 insertions(+), 62 deletions(-) diff --git a/packages/js/src/dashboard/components/dashboard-table.js b/packages/js/src/dashboard/components/dashboard-table.js index 8efc04825cd..81a1020f40e 100644 --- a/packages/js/src/dashboard/components/dashboard-table.js +++ b/packages/js/src/dashboard/components/dashboard-table.js @@ -1,36 +1,12 @@ -import { ChevronDownIcon, ChevronUpIcon } from "@heroicons/react/solid"; -import { useState, useCallback } from "@wordpress/element"; import classNames from "classnames"; -import { SCORE_META } from "../scores/score-meta"; import { Paper, Table, Title } from "@yoast/ui-library"; +import { SCORE_META } from "../scores/score-meta"; /** * @type {import("../index").Column} Column * @type {import("../index").Data} Data */ -/** - * The sortable header component. - * - * @param {string} columnName The column name. - * @param {boolean} isAscending Whether the sorting is ascending. - * @param {Function} onClick The click handler. - * @param {number} index The index of the column. - * @returns {JSX.Element} The element. - */ -const SortableHeader = ( { columnName, isAscending, onClick, index } ) => { - const handleSort = useCallback( () => { - onClick( index ); - }, [ onClick, index ] ); - - const ChevronIcon = isAscending ? ChevronDownIcon : ChevronUpIcon; - - return ; -}; - /** * The score bullet component. * @@ -57,31 +33,6 @@ const ScoreBullet = ( { score } ) => ( * @returns {JSX.Element} The element. */ export const DashboardTable = ( { title, columns, data } ) => { - const [ sortConfig, setSortConfig ] = useState( { key: null, ascending: true } ); - - const sortedData = [ ...data ].sort( ( a, b ) => { - if ( sortConfig.key === null ) { - return 0; - } - const aValue = a[ sortConfig.key ]; - const bValue = b[ sortConfig.key ]; - if ( aValue < bValue ) { - return sortConfig.ascending ? -1 : 1; - } - if ( aValue > bValue ) { - return sortConfig.ascending ? 1 : -1; - } - return 0; - } ); - - const handleSort = useCallback( ( columnIndex ) => { - let ascending = true; - if ( sortConfig.key === columnIndex && sortConfig.ascending ) { - ascending = false; - } - setSortConfig( { key: columnIndex, ascending } ); - }, [ sortConfig, setSortConfig ] ); - return ( @@ -92,32 +43,24 @@ export const DashboardTable = ( { title, columns, data } ) => { <Table.Head className="yst-bg-white yst-mt-2 yst-border-b-slate-300 yst-border-b yst-border-t-0"> <Table.Row> <Table.Header>{ "" }</Table.Header> - { columns.map( ( column, index ) => + { columns.map( ( column ) => <Table.Header key={ `${column.name}-${title}` } className="yst-align-bottom yst-pb-3 yst-text-slate-900 yst-font-medium" > - { column.sortable ? ( - <SortableHeader - columnName={ column.label } - isAscending={ sortConfig.ascending } - index={ index } - onClick={ handleSort } - /> - ) : <div className={ column.className }>{ column.label }</div> } + <div className={ column.className }>{ column.label }</div> </Table.Header> ) } </Table.Row> </Table.Head> <Table.Body> - { sortedData.map( ( row, rowIndex ) => ( + { data.map( ( row, rowIndex ) => ( <Table.Row key={ `row-${title}-${rowIndex}` }> <Table.Cell>{ rowIndex + 1 }. </Table.Cell> { row.map( ( cell, cellIndex ) => ( <Table.Cell key={ `cell-${title}-${cellIndex}` } className="yst-numbered-cell"> { columns[ cellIndex ].name === "seo-score" ? <ScoreBullet score={ cell } /> : <div - className={ classNames( columns[ cellIndex ].sortable ? "yst-text-end yst-pe-5" : "", - cellIndex === 0 ? "yst-text-slate-900 yst-font-medium" : "", + className={ classNames( cellIndex === 0 ? "yst-text-slate-900 yst-font-medium" : "", columns[ cellIndex ].className ) } >{ cell }</div> } </Table.Cell> ) ) } From 9169efb2fadff8480b01b4689c09295b61e94033 Mon Sep 17 00:00:00 2001 From: Vraja Das <vraja.pro@gmail.com> Date: Wed, 15 Jan 2025 16:54:57 +0200 Subject: [PATCH 13/27] add minimal variant to ui library and update storybook --- .../src/elements/table/docs/index.js | 1 + .../src/elements/table/docs/table-row.md | 2 +- .../table/docs/table-variant-minimal.md | 1 + .../ui-library/src/elements/table/index.js | 22 ++++++--- .../ui-library/src/elements/table/stories.js | 38 +++++++++++++++- .../ui-library/src/elements/table/style.css | 45 ++++++++++++++++--- 6 files changed, 94 insertions(+), 15 deletions(-) create mode 100644 packages/ui-library/src/elements/table/docs/table-variant-minimal.md diff --git a/packages/ui-library/src/elements/table/docs/index.js b/packages/ui-library/src/elements/table/docs/index.js index b930f54b03d..9c56c7deef8 100644 --- a/packages/ui-library/src/elements/table/docs/index.js +++ b/packages/ui-library/src/elements/table/docs/index.js @@ -4,3 +4,4 @@ export { default as tableCell } from "./table-cell.md"; export { default as tableHead } from "./table-head.md"; export { default as tableHeader } from "./table-header.md"; export { default as tableRow } from "./table-row.md"; +export { default as minimal } from "./table-variant-minimal.md"; diff --git a/packages/ui-library/src/elements/table/docs/table-row.md b/packages/ui-library/src/elements/table/docs/table-row.md index 4f489ab8eb7..eb90b5eb9cc 100644 --- a/packages/ui-library/src/elements/table/docs/table-row.md +++ b/packages/ui-library/src/elements/table/docs/table-row.md @@ -1 +1 @@ -The sub component `Table.Row`. +The sub component `Table.Row`. The row has a variant props which can be set to `striped`. diff --git a/packages/ui-library/src/elements/table/docs/table-variant-minimal.md b/packages/ui-library/src/elements/table/docs/table-variant-minimal.md new file mode 100644 index 00000000000..b9244731d56 --- /dev/null +++ b/packages/ui-library/src/elements/table/docs/table-variant-minimal.md @@ -0,0 +1 @@ +The minial variant is the table without the table border. \ No newline at end of file diff --git a/packages/ui-library/src/elements/table/index.js b/packages/ui-library/src/elements/table/index.js index bb13ef9ebc1..d22c317f45b 100644 --- a/packages/ui-library/src/elements/table/index.js +++ b/packages/ui-library/src/elements/table/index.js @@ -16,7 +16,7 @@ const rowClassNameMap = { * @returns {JSX.Element} The element. */ const Cell = ( { children, className = "", ...props } ) => ( - <td className={ classNames( "yst-table-cell yst-px-3 yst-py-4 yst-text-sm yst-text-slate-600", className ) } { ...props }> + <td className={ classNames( "yst-table-cell", className ) } { ...props }> { children } </td> ); @@ -54,7 +54,7 @@ Row.propTypes = { */ const Header = ( { children, className = "", ...props } ) => ( <th - className={ classNames( "yst-table-header yst-px-3 yst-py-4 yst-text-start yst-text-sm yst-font-semibold yst-text-slate-900", className ) } + className={ classNames( "yst-table-header", className ) } { ...props } > { children } @@ -73,7 +73,7 @@ Header.propTypes = { * @returns {JSX.Element} The element. */ const Head = ( { children, className = "", ...props } ) => ( - <thead className={ classNames( "yst-bg-slate-50", className ) } { ...props }>{ children }</thead> + <thead className={ className } { ...props }>{ children }</thead> ); Head.propTypes = { @@ -88,7 +88,7 @@ Head.propTypes = { * @returns {JSX.Element} The element. */ const Body = ( { children, className = "", ...props } ) => ( - <tbody className={ classNames( "yst-divide-y yst-divide-gray-200 yst-bg-white", className ) } { ...props }>{ children }</tbody> + <tbody className={ className } { ...props }>{ children }</tbody> ); Body.propTypes = { @@ -96,15 +96,21 @@ Body.propTypes = { className: PropTypes.string, }; +const tableVariants = { + "default": "yst-table--default", + minimal: "yst-table--minimal", +}; + /** * @param {JSX.node} children The content. * @param {string} [className] Optional class name. + * @param {string} [variant] The variant of the table. * @param {Object} [props] Optional table props. * @returns {JSX.Element} The element. */ -const Table = forwardRef( ( { children, className = "", ...props }, ref ) => ( - <div className="yst-table-wrapper yst-shadow yst-ring-1 yst-ring-black yst-ring-opacity-5"> - <table className={ classNames( "yst-min-w-full yst-divide-y yst-divide-slate-300", className ) } { ...props } ref={ ref }> +const Table = forwardRef( ( { children, className = "", variant = "default", ...props }, ref ) => ( + <div className={ classNames( "yst-table-wrapper", tableVariants[ variant ] ) }> + <table className={ className } { ...props } ref={ ref }> { children } </table> </div> @@ -114,9 +120,11 @@ Table.displayName = "Table"; Table.propTypes = { children: PropTypes.node.isRequired, className: PropTypes.string, + variant: PropTypes.string, }; Table.defaultProps = { className: "", + variant: "default", }; Table.Head = Head; diff --git a/packages/ui-library/src/elements/table/stories.js b/packages/ui-library/src/elements/table/stories.js index 021167d1e76..bbc6e679338 100644 --- a/packages/ui-library/src/elements/table/stories.js +++ b/packages/ui-library/src/elements/table/stories.js @@ -1,7 +1,7 @@ import React from "react"; import Table from "."; import { InteractiveDocsPage } from "../../../.storybook/interactive-docs-page"; -import { component, tableBody, tableCell, tableHead, tableHeader, tableRow } from "./docs"; +import { component, tableBody, tableCell, tableHead, tableHeader, tableRow, minimal } from "./docs"; export const Factory = { parameters: { @@ -199,6 +199,40 @@ export const TableCell = { }, }; +export const MinimalVariant = { + name: "Table variant minimal", + parameters: { + controls: { disable: false }, + docs: { description: { story: minimal } }, + }, + args: { + variant: "minimal", + children: ( + <> + <Table.Head> + <Table.Row> + <Table.Header>Header 1</Table.Header> + <Table.Header>Header 2</Table.Header> + <Table.Header>Header 3</Table.Header> + </Table.Row> + </Table.Head> + <Table.Body> + <Table.Row> + <Table.Cell>Cell 1</Table.Cell> + <Table.Cell>Cell 2</Table.Cell> + <Table.Cell>Cell 3</Table.Cell> + </Table.Row> + <Table.Row> + <Table.Cell>Cell 1</Table.Cell> + <Table.Cell>Cell 2</Table.Cell> + <Table.Cell>Cell 3</Table.Cell> + </Table.Row> + </Table.Body> + </> + ), + }, +}; + export default { title: "1) Elements/Table", component: Table, @@ -208,7 +242,7 @@ export default { parameters: { docs: { description: { component }, - page: () => <InteractiveDocsPage stories={ [ TableHead, TableRow, TableHeader, TableBody, TableCell ] } />, + page: () => <InteractiveDocsPage stories={ [ TableHead, TableRow, TableHeader, TableBody, TableCell, MinimalVariant ] } />, }, }, }; diff --git a/packages/ui-library/src/elements/table/style.css b/packages/ui-library/src/elements/table/style.css index 1885ffbaf45..9a6a86b0a5b 100644 --- a/packages/ui-library/src/elements/table/style.css +++ b/packages/ui-library/src/elements/table/style.css @@ -1,15 +1,50 @@ @layer components { .yst-root { .yst-table-wrapper { - @apply yst-rounded-lg; + table { + @apply yst-min-w-full; + + .yst-table-header { + @apply yst-px-3 yst-py-4 yst-text-start yst-text-sm yst-font-semibold yst-text-slate-900; + } + + .yst-table-cell { + @apply yst-px-3 yst-py-4 yst-text-sm yst-text-slate-600; + } + } } + + .yst-table--default { + @apply yst-rounded-lg yst-shadow yst-ring-1 yst-ring-black yst-ring-opacity-5; + + table { + @apply yst-divide-y yst-divide-slate-300; + + thead{ + @apply yst-bg-slate-50; + + .yst-table-header { + @apply first:yst-rounded-ss-lg last:yst-rounded-se-lg; + } + } + + tbody { + @apply yst-divide-y yst-divide-gray-200 yst-bg-white; + } - .yst-table-header { - @apply first:yst-rounded-ss-lg last:yst-rounded-se-lg; + .yst-table-row:last-of-type .yst-table-cell { + @apply first:yst-rounded-es-lg last:yst-rounded-ee-lg; + } + } } - .yst-table-row:last-of-type .yst-table-cell { - @apply first:yst-rounded-es-lg last:yst-rounded-ee-lg; + .yst-table--minimal { + table { + @apply yst-divide-y yst-divide-slate-300; + tbody { + @apply yst-divide-y yst-divide-gray-200 yst-bg-white; + } + } } } } From 87a9e20d8242f4dfb6f4c7332571ffd730c11232 Mon Sep 17 00:00:00 2001 From: Vraja Das <vraja.pro@gmail.com> Date: Wed, 15 Jan 2025 17:14:37 +0200 Subject: [PATCH 14/27] drop sorting drop sort drop sorting from tests --- .../js/src/dashboard/components/dashboard-table.js | 4 ++-- .../dashboard/components/dashboard-table.test.js | 13 ++----------- 2 files changed, 4 insertions(+), 13 deletions(-) diff --git a/packages/js/src/dashboard/components/dashboard-table.js b/packages/js/src/dashboard/components/dashboard-table.js index 81a1020f40e..2e609dba78c 100644 --- a/packages/js/src/dashboard/components/dashboard-table.js +++ b/packages/js/src/dashboard/components/dashboard-table.js @@ -39,7 +39,7 @@ export const DashboardTable = ( { title, columns, data } ) => { { title }
- +
{ "" } @@ -67,7 +67,7 @@ export const DashboardTable = ( { title, columns, data } ) => { ) ) } -
+
); diff --git a/packages/js/tests/dashboard/components/dashboard-table.test.js b/packages/js/tests/dashboard/components/dashboard-table.test.js index 6cb144cd598..a39abad6d2e 100644 --- a/packages/js/tests/dashboard/components/dashboard-table.test.js +++ b/packages/js/tests/dashboard/components/dashboard-table.test.js @@ -1,4 +1,4 @@ -import { render, screen, fireEvent } from "../../test-utils.js"; +import { render, screen } from "../../test-utils.js"; import "@testing-library/jest-dom"; import { DashboardTable } from "../../../src/dashboard/components/dashboard-table.js"; import { SCORE_META } from "../../../src/dashboard/scores/score-meta.js"; @@ -6,7 +6,7 @@ import { SCORE_META } from "../../../src/dashboard/scores/score-meta.js"; describe( "DashboardTable", () => { const columns = [ { name: "landing-page", label: "Landing page" }, - { name: "clicks", label: "Clicks", sortable: true }, + { name: "clicks", label: "Clicks" }, { name: "impressions", label: "Impressions" }, { name: "ctr", label: "CTR" }, { name: "average-position", label: "Average position" }, @@ -41,13 +41,4 @@ describe( "DashboardTable", () => { expect( rowElement ).toHaveTextContent( SCORE_META[ row[ 5 ] ].label ); } ); } ); - - it( "sorts the table correctly when a sortable column header is clicked", () => { - render( ); - const clicksHeader = screen.getByText( "Clicks" ); - fireEvent.click( clicksHeader ); - const sortedData = data.sort( ( a, b ) => a[ 1 ] - b[ 1 ] ); - const firstRow = screen.getAllByRole( "row" )[ 1 ]; - expect( firstRow ).toHaveTextContent( sortedData[ 0 ][ 0 ] ); - } ); } ); From ef5a2ed80a44347e705e191ee3a65ab75ad83527 Mon Sep 17 00:00:00 2001 From: Vraja Das Date: Tue, 21 Jan 2025 18:10:16 +0200 Subject: [PATCH 15/27] add reusable widget table --- .../dashboard/components/dashboard-table.js | 74 ----------------- .../components/most-popular-table.js | 42 ++++++++++ .../src/dashboard/components/table-widget.js | 81 +++++++++++++++++++ packages/js/src/dashboard/index.js | 18 ++--- .../components/dashboard-table.test.js | 44 ---------- .../ui-library/src/elements/table/style.css | 4 + 6 files changed, 134 insertions(+), 129 deletions(-) delete mode 100644 packages/js/src/dashboard/components/dashboard-table.js create mode 100644 packages/js/src/dashboard/components/most-popular-table.js create mode 100644 packages/js/src/dashboard/components/table-widget.js delete mode 100644 packages/js/tests/dashboard/components/dashboard-table.test.js diff --git a/packages/js/src/dashboard/components/dashboard-table.js b/packages/js/src/dashboard/components/dashboard-table.js deleted file mode 100644 index 2e609dba78c..00000000000 --- a/packages/js/src/dashboard/components/dashboard-table.js +++ /dev/null @@ -1,74 +0,0 @@ -import classNames from "classnames"; -import { Paper, Table, Title } from "@yoast/ui-library"; -import { SCORE_META } from "../scores/score-meta"; - -/** - * @type {import("../index").Column} Column - * @type {import("../index").Data} Data - */ - -/** - * The score bullet component. - * - * @param {string} score The score. - * @returns {JSX.Element} The element. - */ -const ScoreBullet = ( { score } ) => ( -
-
- - { SCORE_META[ score ].label } - -
-
-); - -/** - * The Site Kit table component. - * - * @param {string} title The table title. - * @param {Column[]} columns The columns properties. - * @param {Data} data The rows data. - * - * @returns {JSX.Element} The element. - */ -export const DashboardTable = ( { title, columns, data } ) => { - return ( - - - { title } - -
- - - - { "" } - { columns.map( ( column ) => - -
{ column.label }
-
- ) } -
-
- - { data.map( ( row, rowIndex ) => ( - - { rowIndex + 1 }. - { row.map( ( cell, cellIndex ) => ( - - { columns[ cellIndex ].name === "seo-score" ? :
{ cell }
} -
) ) } -
- ) ) } -
-
-
-
- ); -}; diff --git a/packages/js/src/dashboard/components/most-popular-table.js b/packages/js/src/dashboard/components/most-popular-table.js new file mode 100644 index 00000000000..2222009e391 --- /dev/null +++ b/packages/js/src/dashboard/components/most-popular-table.js @@ -0,0 +1,42 @@ +import { __ } from "@wordpress/i18n"; +import { TableWidget } from "./table-widget"; + +/** + * @type {import("../index").MostPopularContent} Most popular content + */ + +/** + * The top 5 most popular content table component. + * + * @param {[MostPopularContent]} Data The component props. + * + * @returns {JSX.Element} The element. + */ +export const MostPopularTable = ( { data } ) => { + return + + { __( "Landing page", "wordpress-seo" ) } + { __( "Clicks", "wordpress-seo" ) } + { __( "Impressions", "wordpress-seo" ) } + { __( "CTR", "wordpress-seo" ) } + { __( "Average position", "wordpress-seo" ) } + +
+ { __( "SEO score", "wordpress-seo" ) } +
+
+
+ + { data.map( ( { subject, clicks, impressions, ctr, averagePosition, seoScore }, index ) => ( + + { subject } + { clicks } + { impressions } + { ctr } + { averagePosition } + + + ) ) } + +
; +}; diff --git a/packages/js/src/dashboard/components/table-widget.js b/packages/js/src/dashboard/components/table-widget.js new file mode 100644 index 00000000000..ea11ac99653 --- /dev/null +++ b/packages/js/src/dashboard/components/table-widget.js @@ -0,0 +1,81 @@ +import classNames from "classnames"; +import { Paper, Table, Title } from "@yoast/ui-library"; +import { SCORE_META } from "../scores/score-meta"; + +/** + * The score bullet component. + * + * @param {string} score The score. + * @returns {JSX.Element} The element. + */ +const ScoreBullet = ( { score } ) => ( +
+
+ + { SCORE_META[ score ].label } + +
+
+); + +/** + * The table head component. + * + * @param {JSX.Element} children The table headers. + * @returns {JSX.Element} The element. + */ +const TableHead = ( { children } ) => { + return + + { "" } + { children } + + ; +}; + +/** + * The table row component for the table widget includes numbering of first cell. + * + * @param {children} children The row cells. + * @param {number} index The row index. + * @param {string} key The row key. + * + * @returns {JSX.Element} The row element. + */ +const TableRow = ( { children, index, key } ) => { + return + { index + 1 }. + { children } + ; +}; + +/** + * The Site Kit table component. + * + * @param {string} title The table title. + * @param {JSX.Element} children The table rows. + * + * @returns {JSX.Element} The element. + */ +export const TableWidget = ( { title, children } ) => { + return ( + + + { title } + +
+ + { children } +
+
+
+ ); +}; + +TableWidget.Head = TableHead; +TableWidget.Row = TableRow; +TableWidget.ScoreBullet = ScoreBullet; +TableWidget.Cell = Table.Cell; +TableWidget.Header = Table.Header; +TableWidget.Body = Table.Body; + diff --git a/packages/js/src/dashboard/index.js b/packages/js/src/dashboard/index.js index 675c5bf5a83..3a6aca76c67 100644 --- a/packages/js/src/dashboard/index.js +++ b/packages/js/src/dashboard/index.js @@ -56,15 +56,11 @@ export { Dashboard } from "./components/dashboard"; */ /** - * @typedef {Object} Column A column in the table. - * @property {string} name The unique identifier. - * @property {string} label The user-facing label. - * @property {boolean} sortable Whether the column is sortable. - * @property {string} [className] The CSS class name for the column. + * @typedef {Object} MostPopularContent The most popular content data. + * @property {string} subject The landing page. + * @property {number} clicks The number of clicks. + * @property {number} impressions The number of impressions. + * @property {number} ctr The click-through rate. + * @property {number} position The average position. + * @property {number} seoScore The seo score. */ - -/** - * @typedef {Array>} Data The data for the table rows. - */ - - diff --git a/packages/js/tests/dashboard/components/dashboard-table.test.js b/packages/js/tests/dashboard/components/dashboard-table.test.js deleted file mode 100644 index a39abad6d2e..00000000000 --- a/packages/js/tests/dashboard/components/dashboard-table.test.js +++ /dev/null @@ -1,44 +0,0 @@ -import { render, screen } from "../../test-utils.js"; -import "@testing-library/jest-dom"; -import { DashboardTable } from "../../../src/dashboard/components/dashboard-table.js"; -import { SCORE_META } from "../../../src/dashboard/scores/score-meta.js"; - -describe( "DashboardTable", () => { - const columns = [ - { name: "landing-page", label: "Landing page" }, - { name: "clicks", label: "Clicks" }, - { name: "impressions", label: "Impressions" }, - { name: "ctr", label: "CTR" }, - { name: "average-position", label: "Average position" }, - { name: "seo-score", label: "SEO score" }, - ]; - - const data = [ - [ "https://example.com1", 10, 1001, 10, 1, "good" ], - [ "https://example.com2", 90, 1002, 9, 3, "bad" ], - [ "https://example.com3", 50, 1003, 8, 5, "good" ], - [ "https://example.com4", 0, 1004, 7, 2, "ok" ], - [ "https://example.com5", 70, 1005, 6, 4, "ok" ], - ]; - - it( "renders the table with correct title and columns", () => { - render( ); - expect( screen.getByText( "Test Table" ) ).toBeInTheDocument(); - columns.forEach( ( column ) => { - expect( screen.getByText( column.label ) ).toBeInTheDocument(); - } ); - } ); - - it( "renders the table rows correctly", () => { - render( ); - data.forEach( ( row ) => { - const rowElement = screen.getByText( row[ 0 ] ).closest( "tr" ); - expect( rowElement ).toHaveTextContent( row[ 0 ] ); - expect( rowElement ).toHaveTextContent( String( row[ 1 ] ) ); - expect( rowElement ).toHaveTextContent( String( row[ 2 ] ) ); - expect( rowElement ).toHaveTextContent( String( row[ 3 ] ) ); - expect( rowElement ).toHaveTextContent( String( row[ 4 ] ) ); - expect( rowElement ).toHaveTextContent( SCORE_META[ row[ 5 ] ].label ); - } ); - } ); -} ); diff --git a/packages/ui-library/src/elements/table/style.css b/packages/ui-library/src/elements/table/style.css index 9a6a86b0a5b..7c43cb7fb78 100644 --- a/packages/ui-library/src/elements/table/style.css +++ b/packages/ui-library/src/elements/table/style.css @@ -41,6 +41,10 @@ .yst-table--minimal { table { @apply yst-divide-y yst-divide-slate-300; + + th { + @apply yst-align-bottom; + } tbody { @apply yst-divide-y yst-divide-gray-200 yst-bg-white; } From febd022b43cf4217a35be4c52f73c019d4e00315 Mon Sep 17 00:00:00 2001 From: Vraja Das Date: Wed, 22 Jan 2025 13:35:34 +0200 Subject: [PATCH 16/27] align seo score header --- packages/js/src/dashboard/components/most-popular-table.js | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/packages/js/src/dashboard/components/most-popular-table.js b/packages/js/src/dashboard/components/most-popular-table.js index 2222009e391..704679d38a1 100644 --- a/packages/js/src/dashboard/components/most-popular-table.js +++ b/packages/js/src/dashboard/components/most-popular-table.js @@ -20,10 +20,8 @@ export const MostPopularTable = ( { data } ) => { { __( "Impressions", "wordpress-seo" ) } { __( "CTR", "wordpress-seo" ) } { __( "Average position", "wordpress-seo" ) } - -
- { __( "SEO score", "wordpress-seo" ) } -
+ + { __( "SEO score", "wordpress-seo" ) } From c49cf49adfdcc47c39201d416e210d8476ae7f44 Mon Sep 17 00:00:00 2001 From: Vraja Das Date: Thu, 23 Jan 2025 12:39:02 +0200 Subject: [PATCH 17/27] add formatters --- packages/js/package.json | 2 +- .../js/src/dashboard/components/dashboard.js | 44 +++++++++++++++++++ .../components/most-popular-table.js | 32 +++++++++----- .../src/dashboard/components/table-widget.js | 30 +++---------- .../dashboard/formatters/SeoScoreFormatter.js | 18 ++++++++ .../src/dashboard/formatters/ctrFormatter.js | 10 +++++ packages/js/src/dashboard/formatters/index.js | 3 ++ .../dashboard/formatters/positionFormatter.js | 10 +++++ .../general-page-integration.php | 1 + 9 files changed, 113 insertions(+), 37 deletions(-) create mode 100644 packages/js/src/dashboard/formatters/SeoScoreFormatter.js create mode 100644 packages/js/src/dashboard/formatters/ctrFormatter.js create mode 100644 packages/js/src/dashboard/formatters/index.js create mode 100644 packages/js/src/dashboard/formatters/positionFormatter.js diff --git a/packages/js/package.json b/packages/js/package.json index 5f6d121ce15..8a3e08f4b7b 100644 --- a/packages/js/package.json +++ b/packages/js/package.json @@ -6,7 +6,7 @@ "scripts": { "build": "cd ../.. && wp-scripts build --config config/webpack/webpack.config.js", "test": "jest", - "lint": "eslint . --max-warnings=64" + "lint": "eslint . --max-warnings=63" }, "dependencies": { "@draft-js-plugins/mention": "^5.0.0", diff --git a/packages/js/src/dashboard/components/dashboard.js b/packages/js/src/dashboard/components/dashboard.js index 62f58e02edc..47d486a7488 100644 --- a/packages/js/src/dashboard/components/dashboard.js +++ b/packages/js/src/dashboard/components/dashboard.js @@ -1,5 +1,6 @@ import { Scores } from "../scores/components/scores"; import { PageTitle } from "./page-title"; +import { MostPopularTable } from "./most-popular-table"; /** * @type {import("../index").ContentType} ContentType @@ -18,9 +19,52 @@ import { PageTitle } from "./page-title"; * @returns {JSX.Element} The element. */ export const Dashboard = ( { contentTypes, userName, features, endpoints, headers, links } ) => { + const data = [ + { + subject: "https://example.com/page1", + clicks: 100, + impressions: 1000, + ctr: 0.020383459755, + averagePosition: 5.568768, + seoScore: "good", + }, + { + subject: "https://example.com/page2", + clicks: 200, + impressions: 2000, + ctr: 0.62448974546, + averagePosition: 10.5479879879, + seoScore: "ok", + }, + { + subject: "https://example.com/page3", + clicks: 300, + impressions: 3000, + ctr: 0.05897354354, + averagePosition: 15.3216544, + seoScore: "bad", + }, + { + subject: "https://example.com/page4", + clicks: 400, + impressions: 4000, + ctr: 0.98456465, + averagePosition: 20.6756456, + seoScore: "good", + }, + { + subject: "https://example.com/page5", + clicks: 500, + impressions: 5000, + ctr: 0.1256465465, + averagePosition: 25.467686, + seoScore: "ok", + }, + ]; return ( <> +
{ features.indexables && features.seoAnalysis && ( diff --git a/packages/js/src/dashboard/components/most-popular-table.js b/packages/js/src/dashboard/components/most-popular-table.js index 704679d38a1..0cd19036532 100644 --- a/packages/js/src/dashboard/components/most-popular-table.js +++ b/packages/js/src/dashboard/components/most-popular-table.js @@ -1,5 +1,8 @@ import { __ } from "@wordpress/i18n"; +import { select } from "@wordpress/data"; +import { STORE_NAME } from "../../general/constants"; import { TableWidget } from "./table-widget"; +import { ctrFormatter, positionFormatter, SeoScoreFormatter } from "../formatters"; /** * @type {import("../index").MostPopularContent} Most popular content @@ -13,26 +16,33 @@ import { TableWidget } from "./table-widget"; * @returns {JSX.Element} The element. */ export const MostPopularTable = ( { data } ) => { + const languageCode = select( STORE_NAME ).selectPreference( "languageCode" ); return { __( "Landing page", "wordpress-seo" ) } - { __( "Clicks", "wordpress-seo" ) } - { __( "Impressions", "wordpress-seo" ) } - { __( "CTR", "wordpress-seo" ) } - { __( "Average position", "wordpress-seo" ) } + { __( "Clicks", "wordpress-seo" ) } + { __( "Impressions", "wordpress-seo" ) } + { __( "CTR", "wordpress-seo" ) } + + { __( "Average position", "wordpress-seo" ) } + - { __( "SEO score", "wordpress-seo" ) } +
+
+ { __( "SEO score", "wordpress-seo" ) } +
+
{ data.map( ( { subject, clicks, impressions, ctr, averagePosition, seoScore }, index ) => ( - { subject } - { clicks } - { impressions } - { ctr } - { averagePosition } - + { subject } + { clicks } + { impressions } + { ctrFormatter( ctr, languageCode ) } + { positionFormatter( averagePosition, languageCode ) } + ) ) } diff --git a/packages/js/src/dashboard/components/table-widget.js b/packages/js/src/dashboard/components/table-widget.js index ea11ac99653..2bfe217353d 100644 --- a/packages/js/src/dashboard/components/table-widget.js +++ b/packages/js/src/dashboard/components/table-widget.js @@ -1,22 +1,4 @@ -import classNames from "classnames"; import { Paper, Table, Title } from "@yoast/ui-library"; -import { SCORE_META } from "../scores/score-meta"; - -/** - * The score bullet component. - * - * @param {string} score The score. - * @returns {JSX.Element} The element. - */ -const ScoreBullet = ( { score } ) => ( -
-
- - { SCORE_META[ score ].label } - -
-
-); /** * The table head component. @@ -27,7 +9,7 @@ const ScoreBullet = ( { score } ) => ( const TableHead = ( { children } ) => { return - { "" } + { "" } { children } ; @@ -38,13 +20,12 @@ const TableHead = ( { children } ) => { * * @param {children} children The row cells. * @param {number} index The row index. - * @param {string} key The row key. * * @returns {JSX.Element} The row element. */ -const TableRow = ( { children, index, key } ) => { - return - { index + 1 }. +const TableRow = ( { children, index } ) => { + return + { index + 1 }. { children } ; }; @@ -59,7 +40,7 @@ const TableRow = ( { children, index, key } ) => { */ export const TableWidget = ( { title, children } ) => { return ( - + { title } @@ -74,7 +55,6 @@ export const TableWidget = ( { title, children } ) => { TableWidget.Head = TableHead; TableWidget.Row = TableRow; -TableWidget.ScoreBullet = ScoreBullet; TableWidget.Cell = Table.Cell; TableWidget.Header = Table.Header; TableWidget.Body = Table.Body; diff --git a/packages/js/src/dashboard/formatters/SeoScoreFormatter.js b/packages/js/src/dashboard/formatters/SeoScoreFormatter.js new file mode 100644 index 00000000000..bd59dddbeb4 --- /dev/null +++ b/packages/js/src/dashboard/formatters/SeoScoreFormatter.js @@ -0,0 +1,18 @@ +import classNames from "classnames"; +import { SCORE_META } from "../scores/score-meta"; + +/** + * The score bullet component. + * + * @param {string} score The score. + * @returns {JSX.Element} The element. + */ +export const SeoScoreFormatter = ( { score } ) => ( +
+
+ + { SCORE_META[ score ].label } + +
+
+); diff --git a/packages/js/src/dashboard/formatters/ctrFormatter.js b/packages/js/src/dashboard/formatters/ctrFormatter.js new file mode 100644 index 00000000000..a7e6a02cb32 --- /dev/null +++ b/packages/js/src/dashboard/formatters/ctrFormatter.js @@ -0,0 +1,10 @@ +/** + * Convert decimal position number. + * + * @param {number} ctr The number of ctr. + * + * @returns {string} The formatted ctr. + */ +export const ctrFormatter = ( ctr, languageCode ) => { + return new Intl.NumberFormat( languageCode, { style: "percent", minimumFractionDigits: 2, maximumFractionDigits: 2 } ).format( ctr ); +}; diff --git a/packages/js/src/dashboard/formatters/index.js b/packages/js/src/dashboard/formatters/index.js new file mode 100644 index 00000000000..aaf1fcd3721 --- /dev/null +++ b/packages/js/src/dashboard/formatters/index.js @@ -0,0 +1,3 @@ +export { ctrFormatter } from "./ctrFormatter"; +export { positionFormatter } from "./positionFormatter"; +export { SeoScoreFormatter } from "./SeoScoreFormatter"; diff --git a/packages/js/src/dashboard/formatters/positionFormatter.js b/packages/js/src/dashboard/formatters/positionFormatter.js new file mode 100644 index 00000000000..558752e5acd --- /dev/null +++ b/packages/js/src/dashboard/formatters/positionFormatter.js @@ -0,0 +1,10 @@ +/** + * Convert position number. + * + * @param {number} position The number of position. + * + * @returns {string} The formatted position. + */ +export const positionFormatter = ( position, languageCode ) => { + return new Intl.NumberFormat( languageCode, { minimumFractionDigits: 2, maximumFractionDigits: 2 } ).format( position ); +}; diff --git a/src/general/user-interface/general-page-integration.php b/src/general/user-interface/general-page-integration.php index 0a119835477..9c0b6b91cf9 100644 --- a/src/general/user-interface/general-page-integration.php +++ b/src/general/user-interface/general-page-integration.php @@ -207,6 +207,7 @@ private function get_script_data() { 'actionId' => 'load-nfd-ctb', 'premiumCtbId' => 'f6a84663-465f-4cb5-8ba5-f7a6d72224b2', ], + 'languageCode' => explode('_', \get_locale())[0], ], 'linkParams' => $this->shortlink_helper->get_query_params(), 'userEditUrl' => \add_query_arg( 'user_id', '{user_id}', \admin_url( 'user-edit.php' ) ), From 4cddd2793712a297cea414a51a9cb8f68c5aa3fa Mon Sep 17 00:00:00 2001 From: Vraja Das Date: Thu, 23 Jan 2025 15:40:31 +0200 Subject: [PATCH 18/27] Revert "add formatters" This reverts commit c49cf49adfdcc47c39201d416e210d8476ae7f44. --- packages/js/package.json | 2 +- .../js/src/dashboard/components/dashboard.js | 44 ------------------- .../components/most-popular-table.js | 32 +++++--------- .../src/dashboard/components/table-widget.js | 30 ++++++++++--- .../dashboard/formatters/SeoScoreFormatter.js | 18 -------- .../src/dashboard/formatters/ctrFormatter.js | 10 ----- packages/js/src/dashboard/formatters/index.js | 3 -- .../dashboard/formatters/positionFormatter.js | 10 ----- .../general-page-integration.php | 1 - 9 files changed, 37 insertions(+), 113 deletions(-) delete mode 100644 packages/js/src/dashboard/formatters/SeoScoreFormatter.js delete mode 100644 packages/js/src/dashboard/formatters/ctrFormatter.js delete mode 100644 packages/js/src/dashboard/formatters/index.js delete mode 100644 packages/js/src/dashboard/formatters/positionFormatter.js diff --git a/packages/js/package.json b/packages/js/package.json index 8a3e08f4b7b..5f6d121ce15 100644 --- a/packages/js/package.json +++ b/packages/js/package.json @@ -6,7 +6,7 @@ "scripts": { "build": "cd ../.. && wp-scripts build --config config/webpack/webpack.config.js", "test": "jest", - "lint": "eslint . --max-warnings=63" + "lint": "eslint . --max-warnings=64" }, "dependencies": { "@draft-js-plugins/mention": "^5.0.0", diff --git a/packages/js/src/dashboard/components/dashboard.js b/packages/js/src/dashboard/components/dashboard.js index 47d486a7488..62f58e02edc 100644 --- a/packages/js/src/dashboard/components/dashboard.js +++ b/packages/js/src/dashboard/components/dashboard.js @@ -1,6 +1,5 @@ import { Scores } from "../scores/components/scores"; import { PageTitle } from "./page-title"; -import { MostPopularTable } from "./most-popular-table"; /** * @type {import("../index").ContentType} ContentType @@ -19,52 +18,9 @@ import { MostPopularTable } from "./most-popular-table"; * @returns {JSX.Element} The element. */ export const Dashboard = ( { contentTypes, userName, features, endpoints, headers, links } ) => { - const data = [ - { - subject: "https://example.com/page1", - clicks: 100, - impressions: 1000, - ctr: 0.020383459755, - averagePosition: 5.568768, - seoScore: "good", - }, - { - subject: "https://example.com/page2", - clicks: 200, - impressions: 2000, - ctr: 0.62448974546, - averagePosition: 10.5479879879, - seoScore: "ok", - }, - { - subject: "https://example.com/page3", - clicks: 300, - impressions: 3000, - ctr: 0.05897354354, - averagePosition: 15.3216544, - seoScore: "bad", - }, - { - subject: "https://example.com/page4", - clicks: 400, - impressions: 4000, - ctr: 0.98456465, - averagePosition: 20.6756456, - seoScore: "good", - }, - { - subject: "https://example.com/page5", - clicks: 500, - impressions: 5000, - ctr: 0.1256465465, - averagePosition: 25.467686, - seoScore: "ok", - }, - ]; return ( <> -
{ features.indexables && features.seoAnalysis && ( diff --git a/packages/js/src/dashboard/components/most-popular-table.js b/packages/js/src/dashboard/components/most-popular-table.js index 0cd19036532..704679d38a1 100644 --- a/packages/js/src/dashboard/components/most-popular-table.js +++ b/packages/js/src/dashboard/components/most-popular-table.js @@ -1,8 +1,5 @@ import { __ } from "@wordpress/i18n"; -import { select } from "@wordpress/data"; -import { STORE_NAME } from "../../general/constants"; import { TableWidget } from "./table-widget"; -import { ctrFormatter, positionFormatter, SeoScoreFormatter } from "../formatters"; /** * @type {import("../index").MostPopularContent} Most popular content @@ -16,33 +13,26 @@ import { ctrFormatter, positionFormatter, SeoScoreFormatter } from "../formatter * @returns {JSX.Element} The element. */ export const MostPopularTable = ( { data } ) => { - const languageCode = select( STORE_NAME ).selectPreference( "languageCode" ); return { __( "Landing page", "wordpress-seo" ) } - { __( "Clicks", "wordpress-seo" ) } - { __( "Impressions", "wordpress-seo" ) } - { __( "CTR", "wordpress-seo" ) } - - { __( "Average position", "wordpress-seo" ) } - + { __( "Clicks", "wordpress-seo" ) } + { __( "Impressions", "wordpress-seo" ) } + { __( "CTR", "wordpress-seo" ) } + { __( "Average position", "wordpress-seo" ) } -
-
- { __( "SEO score", "wordpress-seo" ) } -
-
+ { __( "SEO score", "wordpress-seo" ) }
{ data.map( ( { subject, clicks, impressions, ctr, averagePosition, seoScore }, index ) => ( - { subject } - { clicks } - { impressions } - { ctrFormatter( ctr, languageCode ) } - { positionFormatter( averagePosition, languageCode ) } - + { subject } + { clicks } + { impressions } + { ctr } + { averagePosition } + ) ) } diff --git a/packages/js/src/dashboard/components/table-widget.js b/packages/js/src/dashboard/components/table-widget.js index 2bfe217353d..ea11ac99653 100644 --- a/packages/js/src/dashboard/components/table-widget.js +++ b/packages/js/src/dashboard/components/table-widget.js @@ -1,4 +1,22 @@ +import classNames from "classnames"; import { Paper, Table, Title } from "@yoast/ui-library"; +import { SCORE_META } from "../scores/score-meta"; + +/** + * The score bullet component. + * + * @param {string} score The score. + * @returns {JSX.Element} The element. + */ +const ScoreBullet = ( { score } ) => ( +
+
+ + { SCORE_META[ score ].label } + +
+
+); /** * The table head component. @@ -9,7 +27,7 @@ import { Paper, Table, Title } from "@yoast/ui-library"; const TableHead = ( { children } ) => { return - { "" } + { "" } { children } ; @@ -20,12 +38,13 @@ const TableHead = ( { children } ) => { * * @param {children} children The row cells. * @param {number} index The row index. + * @param {string} key The row key. * * @returns {JSX.Element} The row element. */ -const TableRow = ( { children, index } ) => { - return - { index + 1 }. +const TableRow = ( { children, index, key } ) => { + return + { index + 1 }. { children } ; }; @@ -40,7 +59,7 @@ const TableRow = ( { children, index } ) => { */ export const TableWidget = ( { title, children } ) => { return ( - + { title } @@ -55,6 +74,7 @@ export const TableWidget = ( { title, children } ) => { TableWidget.Head = TableHead; TableWidget.Row = TableRow; +TableWidget.ScoreBullet = ScoreBullet; TableWidget.Cell = Table.Cell; TableWidget.Header = Table.Header; TableWidget.Body = Table.Body; diff --git a/packages/js/src/dashboard/formatters/SeoScoreFormatter.js b/packages/js/src/dashboard/formatters/SeoScoreFormatter.js deleted file mode 100644 index bd59dddbeb4..00000000000 --- a/packages/js/src/dashboard/formatters/SeoScoreFormatter.js +++ /dev/null @@ -1,18 +0,0 @@ -import classNames from "classnames"; -import { SCORE_META } from "../scores/score-meta"; - -/** - * The score bullet component. - * - * @param {string} score The score. - * @returns {JSX.Element} The element. - */ -export const SeoScoreFormatter = ( { score } ) => ( -
-
- - { SCORE_META[ score ].label } - -
-
-); diff --git a/packages/js/src/dashboard/formatters/ctrFormatter.js b/packages/js/src/dashboard/formatters/ctrFormatter.js deleted file mode 100644 index a7e6a02cb32..00000000000 --- a/packages/js/src/dashboard/formatters/ctrFormatter.js +++ /dev/null @@ -1,10 +0,0 @@ -/** - * Convert decimal position number. - * - * @param {number} ctr The number of ctr. - * - * @returns {string} The formatted ctr. - */ -export const ctrFormatter = ( ctr, languageCode ) => { - return new Intl.NumberFormat( languageCode, { style: "percent", minimumFractionDigits: 2, maximumFractionDigits: 2 } ).format( ctr ); -}; diff --git a/packages/js/src/dashboard/formatters/index.js b/packages/js/src/dashboard/formatters/index.js deleted file mode 100644 index aaf1fcd3721..00000000000 --- a/packages/js/src/dashboard/formatters/index.js +++ /dev/null @@ -1,3 +0,0 @@ -export { ctrFormatter } from "./ctrFormatter"; -export { positionFormatter } from "./positionFormatter"; -export { SeoScoreFormatter } from "./SeoScoreFormatter"; diff --git a/packages/js/src/dashboard/formatters/positionFormatter.js b/packages/js/src/dashboard/formatters/positionFormatter.js deleted file mode 100644 index 558752e5acd..00000000000 --- a/packages/js/src/dashboard/formatters/positionFormatter.js +++ /dev/null @@ -1,10 +0,0 @@ -/** - * Convert position number. - * - * @param {number} position The number of position. - * - * @returns {string} The formatted position. - */ -export const positionFormatter = ( position, languageCode ) => { - return new Intl.NumberFormat( languageCode, { minimumFractionDigits: 2, maximumFractionDigits: 2 } ).format( position ); -}; diff --git a/src/general/user-interface/general-page-integration.php b/src/general/user-interface/general-page-integration.php index 9c0b6b91cf9..0a119835477 100644 --- a/src/general/user-interface/general-page-integration.php +++ b/src/general/user-interface/general-page-integration.php @@ -207,7 +207,6 @@ private function get_script_data() { 'actionId' => 'load-nfd-ctb', 'premiumCtbId' => 'f6a84663-465f-4cb5-8ba5-f7a6d72224b2', ], - 'languageCode' => explode('_', \get_locale())[0], ], 'linkParams' => $this->shortlink_helper->get_query_params(), 'userEditUrl' => \add_query_arg( 'user_id', '{user_id}', \admin_url( 'user-edit.php' ) ), From 8156e79d279e04e4519bca2e72e050b17c2e7f2c Mon Sep 17 00:00:00 2001 From: Vraja Das Date: Thu, 23 Jan 2025 16:36:49 +0200 Subject: [PATCH 19/27] add classes to align data --- .../components/most-popular-table.js | 26 ++++++++++++------- 1 file changed, 16 insertions(+), 10 deletions(-) diff --git a/packages/js/src/dashboard/components/most-popular-table.js b/packages/js/src/dashboard/components/most-popular-table.js index 704679d38a1..07a99120386 100644 --- a/packages/js/src/dashboard/components/most-popular-table.js +++ b/packages/js/src/dashboard/components/most-popular-table.js @@ -16,22 +16,28 @@ export const MostPopularTable = ( { data } ) => { return { __( "Landing page", "wordpress-seo" ) } - { __( "Clicks", "wordpress-seo" ) } - { __( "Impressions", "wordpress-seo" ) } - { __( "CTR", "wordpress-seo" ) } - { __( "Average position", "wordpress-seo" ) } + { __( "Clicks", "wordpress-seo" ) } + { __( "Impressions", "wordpress-seo" ) } + { __( "CTR", "wordpress-seo" ) } + + { __( "Average position", "wordpress-seo" ) } + - { __( "SEO score", "wordpress-seo" ) } +
+
+ { __( "SEO score", "wordpress-seo" ) } +
+
{ data.map( ( { subject, clicks, impressions, ctr, averagePosition, seoScore }, index ) => ( - { subject } - { clicks } - { impressions } - { ctr } - { averagePosition } + { subject } + { clicks } + { impressions } + { ctr } + { averagePosition } ) ) } From 370622f476b4d860b6ee7d7343e41965cf69e7b1 Mon Sep 17 00:00:00 2001 From: Vraja Das Date: Thu, 23 Jan 2025 16:37:17 +0200 Subject: [PATCH 20/27] update warnings threshold --- packages/js/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/js/package.json b/packages/js/package.json index 5f6d121ce15..8a3e08f4b7b 100644 --- a/packages/js/package.json +++ b/packages/js/package.json @@ -6,7 +6,7 @@ "scripts": { "build": "cd ../.. && wp-scripts build --config config/webpack/webpack.config.js", "test": "jest", - "lint": "eslint . --max-warnings=64" + "lint": "eslint . --max-warnings=63" }, "dependencies": { "@draft-js-plugins/mention": "^5.0.0", From 2c0be6a30cb59950a1b2e9fe87d05f8a3e979858 Mon Sep 17 00:00:00 2001 From: Vraja Das Date: Thu, 23 Jan 2025 16:47:07 +0200 Subject: [PATCH 21/27] change the table width --- packages/js/src/dashboard/components/table-widget.js | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/packages/js/src/dashboard/components/table-widget.js b/packages/js/src/dashboard/components/table-widget.js index ea11ac99653..16591523369 100644 --- a/packages/js/src/dashboard/components/table-widget.js +++ b/packages/js/src/dashboard/components/table-widget.js @@ -27,7 +27,7 @@ const ScoreBullet = ( { score } ) => ( const TableHead = ( { children } ) => { return - { "" } + { "" } { children } ; @@ -44,7 +44,7 @@ const TableHead = ( { children } ) => { */ const TableRow = ( { children, index, key } ) => { return - { index + 1 }. + { index + 1 }. { children } ; }; @@ -59,7 +59,7 @@ const TableRow = ( { children, index, key } ) => { */ export const TableWidget = ( { title, children } ) => { return ( - + { title } From 075b19ed2a4f73934bc703c66144888e404c21b1 Mon Sep 17 00:00:00 2001 From: Vraja Das Date: Thu, 23 Jan 2025 16:58:02 +0200 Subject: [PATCH 22/27] fix styling with classes and fix space --- packages/ui-library/src/elements/table/index.js | 2 +- packages/ui-library/src/elements/table/style.css | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/ui-library/src/elements/table/index.js b/packages/ui-library/src/elements/table/index.js index d22c317f45b..a097dcc4954 100644 --- a/packages/ui-library/src/elements/table/index.js +++ b/packages/ui-library/src/elements/table/index.js @@ -88,7 +88,7 @@ Head.propTypes = { * @returns {JSX.Element} The element. */ const Body = ( { children, className = "", ...props } ) => ( - { children } + { children } ); Body.propTypes = { diff --git a/packages/ui-library/src/elements/table/style.css b/packages/ui-library/src/elements/table/style.css index 7c43cb7fb78..7a62d1b1424 100644 --- a/packages/ui-library/src/elements/table/style.css +++ b/packages/ui-library/src/elements/table/style.css @@ -42,7 +42,7 @@ table { @apply yst-divide-y yst-divide-slate-300; - th { + .yst-table-header { @apply yst-align-bottom; } tbody { From 2b1ffa2b5dc4a3b09282ca74dee7fdeb9bfe45a1 Mon Sep 17 00:00:00 2001 From: Vraja Das Date: Thu, 23 Jan 2025 17:02:03 +0200 Subject: [PATCH 23/27] fix spacing --- packages/ui-library/src/elements/table/style.css | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/packages/ui-library/src/elements/table/style.css b/packages/ui-library/src/elements/table/style.css index 7a62d1b1424..ee5bef1f7c1 100644 --- a/packages/ui-library/src/elements/table/style.css +++ b/packages/ui-library/src/elements/table/style.css @@ -10,17 +10,17 @@ .yst-table-cell { @apply yst-px-3 yst-py-4 yst-text-sm yst-text-slate-600; - } + } } } - + .yst-table--default { @apply yst-rounded-lg yst-shadow yst-ring-1 yst-ring-black yst-ring-opacity-5; table { @apply yst-divide-y yst-divide-slate-300; - thead{ + thead { @apply yst-bg-slate-50; .yst-table-header { @@ -45,6 +45,7 @@ .yst-table-header { @apply yst-align-bottom; } + tbody { @apply yst-divide-y yst-divide-gray-200 yst-bg-white; } From fae50a9b1daee7ba4f08ae8a3845f5fd44a0a29e Mon Sep 17 00:00:00 2001 From: Vraja Das Date: Thu, 23 Jan 2025 17:03:55 +0200 Subject: [PATCH 24/27] fix spacing --- packages/ui-library/src/elements/table/style.css | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/ui-library/src/elements/table/style.css b/packages/ui-library/src/elements/table/style.css index ee5bef1f7c1..8205fbc4e2f 100644 --- a/packages/ui-library/src/elements/table/style.css +++ b/packages/ui-library/src/elements/table/style.css @@ -7,7 +7,7 @@ .yst-table-header { @apply yst-px-3 yst-py-4 yst-text-start yst-text-sm yst-font-semibold yst-text-slate-900; } - + .yst-table-cell { @apply yst-px-3 yst-py-4 yst-text-sm yst-text-slate-600; } @@ -16,7 +16,7 @@ .yst-table--default { @apply yst-rounded-lg yst-shadow yst-ring-1 yst-ring-black yst-ring-opacity-5; - + table { @apply yst-divide-y yst-divide-slate-300; From 7fd66b3c37db667791362de27b314429006ef8ec Mon Sep 17 00:00:00 2001 From: Vraja Das Date: Thu, 23 Jan 2025 17:05:49 +0200 Subject: [PATCH 25/27] remove key prop --- packages/js/src/dashboard/components/table-widget.js | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/packages/js/src/dashboard/components/table-widget.js b/packages/js/src/dashboard/components/table-widget.js index 16591523369..f13d79d6f5a 100644 --- a/packages/js/src/dashboard/components/table-widget.js +++ b/packages/js/src/dashboard/components/table-widget.js @@ -38,12 +38,11 @@ const TableHead = ( { children } ) => { * * @param {children} children The row cells. * @param {number} index The row index. - * @param {string} key The row key. * * @returns {JSX.Element} The row element. */ -const TableRow = ( { children, index, key } ) => { - return +const TableRow = ( { children, index } ) => { + return { index + 1 }. { children } ; From 9e997822106be0e89ae1a83eb40b1c065b5141ac Mon Sep 17 00:00:00 2001 From: Vraja Das Date: Thu, 23 Jan 2025 17:08:08 +0200 Subject: [PATCH 26/27] remove container class --- packages/js/src/dashboard/components/table-widget.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/js/src/dashboard/components/table-widget.js b/packages/js/src/dashboard/components/table-widget.js index f13d79d6f5a..fa258824b57 100644 --- a/packages/js/src/dashboard/components/table-widget.js +++ b/packages/js/src/dashboard/components/table-widget.js @@ -58,7 +58,7 @@ const TableRow = ( { children, index } ) => { */ export const TableWidget = ( { title, children } ) => { return ( - + { title } From 5ce72c4b8ac6063229561d8f08e1d4bf391cd383 Mon Sep 17 00:00:00 2001 From: Vraja Das Date: Fri, 24 Jan 2025 09:43:43 +0200 Subject: [PATCH 27/27] fix paddings and colors --- packages/js/src/dashboard/components/table-widget.js | 2 +- packages/ui-library/src/elements/table/style.css | 6 +++++- 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/packages/js/src/dashboard/components/table-widget.js b/packages/js/src/dashboard/components/table-widget.js index fa258824b57..30893a90a4b 100644 --- a/packages/js/src/dashboard/components/table-widget.js +++ b/packages/js/src/dashboard/components/table-widget.js @@ -43,7 +43,7 @@ const TableHead = ( { children } ) => { */ const TableRow = ( { children, index } ) => { return - { index + 1 }. + { index + 1 }. { children } ; }; diff --git a/packages/ui-library/src/elements/table/style.css b/packages/ui-library/src/elements/table/style.css index 8205fbc4e2f..ea8851937c1 100644 --- a/packages/ui-library/src/elements/table/style.css +++ b/packages/ui-library/src/elements/table/style.css @@ -43,7 +43,11 @@ @apply yst-divide-y yst-divide-slate-300; .yst-table-header { - @apply yst-align-bottom; + @apply yst-align-bottom yst-py-2; + } + + .yst-table-cell { + @apply yst-py-2; } tbody {