@automattic/jetpack-components 0.41.1 → 0.42.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (107) hide show
  1. package/CHANGELOG.md +23 -0
  2. package/components/boost-score-graph/background.tsx +183 -0
  3. package/components/boost-score-graph/day-highlight-plugin.ts +82 -0
  4. package/components/boost-score-graph/get-date-format.ts +59 -0
  5. package/components/boost-score-graph/index.native.js +1 -0
  6. package/components/boost-score-graph/index.tsx +76 -0
  7. package/components/boost-score-graph/style-tooltip.scss +52 -0
  8. package/components/boost-score-graph/style-uplot.scss +5 -0
  9. package/components/boost-score-graph/style.scss +17 -0
  10. package/components/boost-score-graph/tooltip.tsx +87 -0
  11. package/components/boost-score-graph/tooltips-plugin.ts +98 -0
  12. package/components/boost-score-graph/types/declarations.d.ts +4 -0
  13. package/components/boost-score-graph/uplot-line-chart.tsx +193 -0
  14. package/components/boost-score-graph/use-boost-score-transform.ts +36 -0
  15. package/components/boost-score-graph/use-resize.ts +40 -0
  16. package/components/gridicon/index.tsx +13 -0
  17. package/components/{pricing-slider → number-slider}/index.tsx +12 -12
  18. package/components/{pricing-slider → number-slider}/style.scss +8 -8
  19. package/components/{pricing-slider → number-slider}/types.ts +2 -2
  20. package/components/popover/index.tsx +19 -0
  21. package/components/popover/style.scss +25 -0
  22. package/components/product-price/index.tsx +5 -5
  23. package/components/product-price/style.module.scss +8 -1
  24. package/index.ts +3 -1
  25. package/package.json +6 -3
  26. package/.gitattributes +0 -8
  27. package/components/action-button/stories/index.stories.jsx +0 -38
  28. package/components/action-button/test/component.jsx +0 -16
  29. package/components/action-popover/stories/index.stories.jsx +0 -115
  30. package/components/action-popover/stories/styles.module.scss +0 -7
  31. package/components/admin-page/stories/index.stories.tsx +0 -35
  32. package/components/admin-page/stories/style.module.scss +0 -13
  33. package/components/admin-section/stories/index.stories.tsx +0 -60
  34. package/components/alert/stories/index.stories.jsx +0 -24
  35. package/components/automattic-byline-logo/stories/index.stories.tsx +0 -20
  36. package/components/automattic-byline-logo/test/component.tsx +0 -28
  37. package/components/boost-score-bar/stories/index.stories.tsx +0 -39
  38. package/components/boost-score-bar/test/component.tsx +0 -102
  39. package/components/button/stories/index.mdx +0 -90
  40. package/components/button/stories/index.stories.tsx +0 -400
  41. package/components/button/stories/style.module.scss +0 -20
  42. package/components/contextual-upgrade-trigger/stories/index.stories.tsx +0 -25
  43. package/components/decorative-card/stories/index.stories.tsx +0 -18
  44. package/components/dialog/stories/boost.png +0 -0
  45. package/components/dialog/stories/declaration.d.ts +0 -1
  46. package/components/dialog/stories/index.stories.tsx +0 -118
  47. package/components/dialog/stories/js-components.components.dialog.mdx +0 -62
  48. package/components/dialog/stories/js-components.components.dialog.stories.js +0 -37
  49. package/components/dialog/stories/style.module.scss +0 -21
  50. package/components/donut-meter/stories/index.stories.tsx +0 -51
  51. package/components/donut-meter/test/component.tsx +0 -24
  52. package/components/gridicon/stories/index.stories.tsx +0 -18
  53. package/components/icon-tooltip/stories/index.stories.tsx +0 -108
  54. package/components/icon-tooltip/stories/style.scss +0 -36
  55. package/components/icon-tooltip/test/component.tsx +0 -15
  56. package/components/icons/stories/index.stories.tsx +0 -65
  57. package/components/icons/stories/style.module.scss +0 -33
  58. package/components/indeterminate-progress-bar/stories/index.stories.tsx +0 -14
  59. package/components/jetpack-footer/stories/index.stories.tsx +0 -39
  60. package/components/jetpack-footer/test/__snapshots__/component.tsx.snap +0 -186
  61. package/components/jetpack-footer/test/component.tsx +0 -159
  62. package/components/jetpack-logo/stories/index.stories.tsx +0 -20
  63. package/components/jetpack-logo/test/component.tsx +0 -16
  64. package/components/jetpack-search-logo/stories/index.stories.tsx +0 -21
  65. package/components/jetpack-search-logo/test/component.tsx +0 -16
  66. package/components/jetpack-vaultpress-backup-logo/stories/index.stories.tsx +0 -20
  67. package/components/jetpack-vaultpress-backup-logo/test/component.tsx +0 -18
  68. package/components/jetpack-videopress-logo/stories/index.stories.tsx +0 -20
  69. package/components/jetpack-videopress-logo/test/component.tsx +0 -16
  70. package/components/layout/stories/index.stories.tsx +0 -145
  71. package/components/layout/stories/styles.module.scss +0 -22
  72. package/components/loading-placeholder/stories/index.stories.tsx +0 -78
  73. package/components/notice/stories/index.stories.jsx +0 -90
  74. package/components/pricing-card/stories/index.stories.tsx +0 -55
  75. package/components/pricing-card/test/component.tsx +0 -89
  76. package/components/pricing-slider/stories/index.stories.tsx +0 -44
  77. package/components/pricing-slider/test/component.tsx +0 -14
  78. package/components/pricing-table/stories/index.stories.tsx +0 -74
  79. package/components/pricing-table/test/component.tsx +0 -41
  80. package/components/product-offer/stories/index.stories.tsx +0 -104
  81. package/components/product-price/stories/index.stories.tsx +0 -33
  82. package/components/product-price/test/component.tsx +0 -26
  83. package/components/qr-code/stories/index.stories.tsx +0 -32
  84. package/components/record-meter-bar/stories/index.stories.tsx +0 -39
  85. package/components/record-meter-bar/test/component.tsx +0 -52
  86. package/components/spinner/stories/index.stories.jsx +0 -20
  87. package/components/spinner/test/component.jsx +0 -17
  88. package/components/split-button/stories/index.stories.tsx +0 -35
  89. package/components/stat-card/stories/index.mdx +0 -35
  90. package/components/stat-card/stories/index.stories.tsx +0 -41
  91. package/components/stat-card/test/component.tsx +0 -24
  92. package/components/status/stories/index.stories.tsx +0 -44
  93. package/components/terms-of-service/test/index.test.jsx +0 -52
  94. package/components/testimonials/test/component.tsx +0 -103
  95. package/components/text/stories/headings.index.stories.tsx +0 -80
  96. package/components/text/stories/index.mdx +0 -195
  97. package/components/text/stories/index.stories.tsx +0 -69
  98. package/components/text/stories/style.module.scss +0 -64
  99. package/components/text/stories/title.index.stories.tsx +0 -34
  100. package/components/theme-provider/stories/index.stories.tsx +0 -164
  101. package/components/theme-provider/stories/style.module.scss +0 -79
  102. package/components/toggle-control/stories/index.stories.tsx +0 -83
  103. package/components/zendesk-chat/stories/index.stories.tsx +0 -15
  104. package/components/zendesk-chat/test/component.tsx +0 -16
  105. package/lib/locale/test/index.ts +0 -21
  106. package/tools/jp-redirect/test/index.tsx +0 -89
  107. package/tools/pricing-utils/test/index.ts +0 -39
@@ -0,0 +1,87 @@
1
+ import { getScoreLetter } from '@automattic/jetpack-boost-score-api';
2
+ import { dateI18n } from '@wordpress/date';
3
+ import { __, sprintf } from '@wordpress/i18n';
4
+ import { type FunctionComponent } from 'react';
5
+ import './style-tooltip.scss';
6
+ import { Period } from '.';
7
+
8
+ export const Tooltip: FunctionComponent = ( { period }: { period: Period } ) => {
9
+ if ( ! period || ! period.dimensions || ! period.timestamp ) {
10
+ return null;
11
+ }
12
+ const {
13
+ mobile_overall_score,
14
+ desktop_overall_score,
15
+ desktop_cls,
16
+ desktop_lcp,
17
+ desktop_tbt,
18
+ mobile_cls,
19
+ mobile_lcp,
20
+ mobile_tbt,
21
+ } = period.dimensions;
22
+ const scoreLetter = getScoreLetter( mobile_overall_score, desktop_overall_score );
23
+ const date = dateI18n( 'F j, Y', new Date( period.timestamp ), false );
24
+
25
+ return (
26
+ <div className="jb-score-tooltip">
27
+ <div className="jb-score-tooltip__date">{ date }</div>
28
+ <div className="jb-score-tooltip__row">
29
+ <div className="jb-score-tooltip__column">{ __( 'Overall score', 'jetpack' ) }</div>
30
+ <div className="jb-score-tooltip__column">{ scoreLetter }</div>
31
+ </div>
32
+ <hr />
33
+ <div className="jb-score-tooltip__row">
34
+ <div className="jb-score-tooltip__column">{ __( 'Desktop score', 'jetpack' ) }</div>
35
+ <div className="jb-score-tooltip__column">
36
+ {
37
+ /* translators: %d is the score */
38
+ sprintf( __( '%d / 100', 'jetpack' ), desktop_overall_score )
39
+ }
40
+ </div>
41
+ </div>
42
+ <div className="jb-score-tooltip__row jb-score-tooltip__row--secondary">
43
+ <div className="jb-score-tooltip__column">
44
+ { __( 'Largest Contentful Paint', 'jetpack' ) }
45
+ </div>
46
+ <div className="jb-score-tooltip__column">{ sprintf( '%0.2fs', desktop_lcp ) }</div>
47
+ </div>
48
+ <div className="jb-score-tooltip__row jb-score-tooltip__row--secondary">
49
+ <div className="jb-score-tooltip__column">{ __( 'Total Blocking Time', 'jetpack' ) }</div>
50
+ <div className="jb-score-tooltip__column">{ sprintf( '%0.2fs', desktop_tbt ) }</div>
51
+ </div>
52
+ <div className="jb-score-tooltip__row jb-score-tooltip__row--secondary">
53
+ <div className="jb-score-tooltip__column">
54
+ { __( 'Cumulative Layout Shift', 'jetpack' ) }
55
+ </div>
56
+ <div className="jb-score-tooltip__column">{ sprintf( '%0.2f', desktop_cls ) }</div>
57
+ </div>
58
+ <hr />
59
+ <div className="jb-score-tooltip__row">
60
+ <div className="jb-score-tooltip__column">{ __( 'Mobile score', 'jetpack' ) }</div>
61
+ <div className="jb-score-tooltip__column">
62
+ {
63
+ /* translators: %d is the score */
64
+ sprintf( __( '%d / 100', 'jetpack' ), mobile_overall_score )
65
+ }
66
+ </div>
67
+ </div>
68
+ <div className="jb-score-tooltip__row jb-score-tooltip__row--secondary">
69
+ <div className="jb-score-tooltip__column">
70
+ { __( 'Largest Contentful Paint', 'jetpack' ) }
71
+ </div>
72
+ <div className="jb-score-tooltip__column">{ sprintf( '%0.2fs', mobile_lcp ) }</div>
73
+ </div>
74
+ <div className="jb-score-tooltip__row jb-score-tooltip__row--secondary">
75
+ <div className="jb-score-tooltip__column">{ __( 'Total Blocking Time', 'jetpack' ) }</div>
76
+ <div className="jb-score-tooltip__column">{ sprintf( '%0.2fs', mobile_tbt ) }</div>
77
+ </div>
78
+ <div className="jb-score-tooltip__row jb-score-tooltip__row--secondary">
79
+ <div className="jb-score-tooltip__column">
80
+ { __( 'Cumulative Layout Shift', 'jetpack' ) }
81
+ </div>
82
+ <div className="jb-score-tooltip__column">{ sprintf( '%0.2f', mobile_cls ) }</div>
83
+ </div>
84
+ <div className="jb-score-tooltip__pointer"></div>
85
+ </div>
86
+ );
87
+ };
@@ -0,0 +1,98 @@
1
+ import ReactDOM from 'react-dom/client';
2
+ import uPlot from 'uplot';
3
+ import { Tooltip } from './tooltip';
4
+ import { Period } from '.';
5
+
6
+ /**
7
+ * Custom tooltips plugin for uPlot.
8
+ *
9
+ * @param {Period[]} periods - The periods to display in the tooltip.
10
+ * @returns {object} The uPlot plugin object with hooks.
11
+ */
12
+ export function tooltipsPlugin( periods ) {
13
+ const reactRoot = document.createElement( 'div' );
14
+ let reactDom;
15
+
16
+ /**
17
+ * Initializes the tooltips plugin.
18
+ *
19
+ * @param {uPlot} u - The uPlot instance.
20
+ * @param {object} _opts - Options for the uPlot instance.
21
+ */
22
+ function init( u, _opts ) {
23
+ const over = u.over;
24
+ reactDom = ReactDOM.createRoot( reactRoot );
25
+ reactRoot.style.position = 'absolute';
26
+ reactRoot.style.bottom = -20 + 'px';
27
+ reactRoot.style.translate = '-50% calc( 100% - 20px )';
28
+
29
+ over.appendChild( reactRoot );
30
+
31
+ /**
32
+ * Hides all tooltips.
33
+ */
34
+ function hideTips() {
35
+ reactRoot.style.display = 'none';
36
+ }
37
+
38
+ /**
39
+ * Shows all tooltips.
40
+ */
41
+ function showTips() {
42
+ reactRoot.style.display = null;
43
+ }
44
+
45
+ over.addEventListener( 'mouseleave', () => {
46
+ if ( ! u.cursor._lock ) {
47
+ hideTips();
48
+ }
49
+ } );
50
+
51
+ over.addEventListener( 'mouseenter', () => {
52
+ showTips();
53
+ } );
54
+
55
+ if ( u.cursor.left < 0 ) {
56
+ hideTips();
57
+ } else {
58
+ showTips();
59
+ }
60
+ }
61
+
62
+ /**
63
+ * Sets the cursor for tooltips.
64
+ *
65
+ * @param {uPlot} u - The uPlot instance.
66
+ */
67
+ function setCursor( u ) {
68
+ const { idx } = u.cursor;
69
+
70
+ const period = periods[ idx ];
71
+
72
+ // Timestamp of the cursor position
73
+ const timestamp = u.data[ 0 ][ idx ];
74
+
75
+ // Find start and end of day for the cursor position
76
+ const startOfDay = timestamp - ( timestamp % 86400 );
77
+ const endOfDay = startOfDay + 86400;
78
+
79
+ // Find the left position, and width of the box, bounded by the range of the graph
80
+ const boxLeft = u.valToPos( Math.max( startOfDay, u.scales.x.min ), 'x' );
81
+ const boxWidth = u.valToPos( Math.min( endOfDay, u.scales.x.max ), 'x' ) - boxLeft;
82
+ const boxCenter = boxLeft + boxWidth / 2;
83
+
84
+ reactRoot.style.left = boxCenter + 'px';
85
+ reactDom.render(
86
+ Tooltip( {
87
+ period,
88
+ } )
89
+ );
90
+ }
91
+
92
+ return {
93
+ hooks: {
94
+ init,
95
+ setCursor,
96
+ },
97
+ };
98
+ }
@@ -0,0 +1,4 @@
1
+ declare module '*.svg' {
2
+ const content: string;
3
+ export default content;
4
+ }
@@ -0,0 +1,193 @@
1
+ import { __ } from '@wordpress/i18n';
2
+ import React, { useMemo, useRef, useCallback } from 'react';
3
+ import uPlot from 'uplot';
4
+ import UplotReact from 'uplot-react';
5
+ import { getUserLocale } from '../../lib/locale';
6
+ import numberFormat from '../number-format';
7
+ import { dayHighlightPlugin } from './day-highlight-plugin';
8
+ import getDateFormat from './get-date-format';
9
+ import { tooltipsPlugin } from './tooltips-plugin';
10
+ import { useBoostScoreTransform } from './use-boost-score-transform';
11
+ import useResize from './use-resize';
12
+ import { Period } from '.';
13
+
14
+ import './style-uplot.scss';
15
+
16
+ const DEFAULT_DIMENSIONS = {
17
+ height: 300,
18
+ width: 600,
19
+ };
20
+
21
+ interface UplotChartProps {
22
+ periods: Period[];
23
+ options?: Partial< uPlot.Options >;
24
+ legendContainer?: React.RefObject< HTMLDivElement >;
25
+ solidFill?: boolean;
26
+ period?: string;
27
+ range?: { startDate: number; endDate: number };
28
+ }
29
+
30
+ /**
31
+ * Creates a series information object for uPlot based on the label and color.
32
+ *
33
+ * @param {string} label - The label for the series.
34
+ * @param {number} score - The last score for the series.
35
+ * @returns {object} The series information object.
36
+ */
37
+ function createSerieInfo( label: string, score ) {
38
+ const { spline } = uPlot.paths;
39
+ return {
40
+ label: label,
41
+ stroke: getColor( score ),
42
+ fill: u => {
43
+ const gradient = u.ctx.createLinearGradient( 0, 0, 0, DEFAULT_DIMENSIONS.height );
44
+ gradient.addColorStop( 0, getColor( score, '44' ) );
45
+ gradient.addColorStop( 1, getColor( score, '11' ) );
46
+
47
+ return gradient;
48
+ }, // use the gradient as fill for the series
49
+ width: 2,
50
+ paths: ( u, seriesIdx, idx0, idx1 ) => {
51
+ return spline?.()( u, seriesIdx, idx0, idx1 ) || null;
52
+ },
53
+ points: {
54
+ show: true,
55
+ },
56
+ value: ( self: uPlot, rawValue: number ) => {
57
+ if ( ! rawValue ) {
58
+ return '-';
59
+ }
60
+
61
+ return numberFormat( rawValue );
62
+ },
63
+ };
64
+ }
65
+
66
+ /**
67
+ * Get the color value based on the score.
68
+ *
69
+ * @param {number} score - The score to get the color for.
70
+ * @param {string} opacity - Whether to return a transparent color.
71
+ * @returns {string} The color value.
72
+ */
73
+ function getColor( score: number, opacity = 'FF' ) {
74
+ let color = '#D63638'; // bad
75
+
76
+ if ( score > 70 ) {
77
+ color = '#069e08'; // good
78
+ } else if ( score > 50 ) {
79
+ color = '#faa754'; //mediocre
80
+ }
81
+
82
+ return `${ color }${ opacity }`;
83
+ }
84
+
85
+ /**
86
+ * UplotLineChart component.
87
+ *
88
+ * @param {object} props - The props object for the UplotLineChart component.
89
+ * @param {{ startDate: number, endDate: number }} props.range - The date range of the chart.
90
+ * @param {Period[]} props.periods - The periods to display in the chart.
91
+ * @returns {React.Element} The JSX element representing the UplotLineChart component.
92
+ */
93
+ export default function UplotLineChart( { range, periods }: UplotChartProps ) {
94
+ const uplot = useRef< uPlot | null >( null );
95
+ const uplotContainer = useRef( null );
96
+
97
+ let lastDesktopScore = 0;
98
+ let lastMobileScore = 0;
99
+
100
+ if ( periods.length > 0 ) {
101
+ lastDesktopScore = periods[ periods.length - 1 ].dimensions.desktop_overall_score;
102
+ lastMobileScore = periods[ periods.length - 1 ].dimensions.mobile_overall_score;
103
+ }
104
+
105
+ const data = useBoostScoreTransform( periods );
106
+
107
+ const options: uPlot.Options = useMemo( () => {
108
+ const defaultOptions: uPlot.Options = {
109
+ class: 'boost-score-graph',
110
+ ...DEFAULT_DIMENSIONS,
111
+ tzDate: ts => uPlot.tzDate( new Date( ts * 1e3 ), 'Etc/UTC' ),
112
+ fmtDate: ( chartDateStringTemplate: string ) => {
113
+ return date => getDateFormat( chartDateStringTemplate, date, getUserLocale() );
114
+ },
115
+ padding: [ 17, 0, 17, 0 ],
116
+ axes: [
117
+ {
118
+ // x-axis
119
+ grid: {
120
+ show: false,
121
+ },
122
+ ticks: {
123
+ stroke: '#50575E',
124
+ width: 1,
125
+ size: 3,
126
+ },
127
+ },
128
+ {
129
+ // y-axis
130
+ side: 1,
131
+ gap: 8,
132
+ space: 100,
133
+ size: 30,
134
+ grid: {
135
+ stroke: 'rgba(220, 220, 222, 0.5)', // #DCDCDE with 0.5 opacity
136
+ width: 1,
137
+ },
138
+ ticks: {
139
+ show: false,
140
+ },
141
+ },
142
+ ],
143
+ cursor: {
144
+ x: false,
145
+ y: false,
146
+ },
147
+ series: [
148
+ {
149
+ label: __( 'Date', 'jetpack' ),
150
+ value: ( self: uPlot, rawValue: number ) => {
151
+ // outputs legend content - value available when mouse is hovering the chart
152
+ if ( ! rawValue ) {
153
+ return '-';
154
+ }
155
+ const date = new Date( rawValue );
156
+ return date.toLocaleDateString( getUserLocale() );
157
+ },
158
+ },
159
+ createSerieInfo( __( 'Desktop', 'jetpack' ), lastDesktopScore ),
160
+ createSerieInfo( __( 'Mobile', 'jetpack' ), lastMobileScore ),
161
+ ],
162
+ scales: {
163
+ x: {
164
+ time: true,
165
+ auto: false,
166
+ range: [ range.startDate / 1000, range.endDate / 1000 ],
167
+ },
168
+ y: {
169
+ range: [ 0, 100 ],
170
+ auto: false,
171
+ },
172
+ },
173
+ legend: {
174
+ show: false,
175
+ },
176
+ plugins: [ tooltipsPlugin( periods ), dayHighlightPlugin() ],
177
+ };
178
+ return {
179
+ ...defaultOptions,
180
+ };
181
+ }, [ lastDesktopScore, lastMobileScore, periods, range.endDate, range.startDate ] );
182
+
183
+ useResize( uplot, uplotContainer );
184
+ const onCreate = useCallback( chart => {
185
+ return ( uplot.current = chart );
186
+ }, [] );
187
+
188
+ return (
189
+ <div ref={ uplotContainer }>
190
+ <UplotReact data={ data } onCreate={ onCreate } options={ options } />
191
+ </div>
192
+ );
193
+ }
@@ -0,0 +1,36 @@
1
+ import { useMemo } from 'react';
2
+ import { Period } from './index';
3
+
4
+ type ScoreGraphAlignedData = [
5
+ number[], // timestamps
6
+ number[], // desktop_overall_score
7
+ number[] // mobile_overall_score
8
+ ];
9
+
10
+ // Extract the value of a dimension from an array of periods
11
+ const getPeriodDimension = function ( key: string, periods: Period[] ) {
12
+ return periods.map( ( { dimensions } ) => {
13
+ return dimensions[ key ];
14
+ } );
15
+ };
16
+
17
+ /**
18
+ * Transforms an array of periods into an array of arrays, where the first array is the timestamps, and the rest are the values for each key
19
+ *
20
+ * @param {Period[]} periods - Array of periods to transform
21
+ * @returns {ScoreGraphAlignedData | []} - Array of arrays, where the first array is the timestamps, and the rest are the values for each key
22
+ */
23
+ export function useBoostScoreTransform( periods: Period[] ): ScoreGraphAlignedData | [] {
24
+ return useMemo( () => {
25
+ if ( ! periods?.length || ! periods[ 0 ].dimensions ) {
26
+ return [];
27
+ }
28
+ const timestamps = periods.map( ( { timestamp } ) => timestamp / 1000 );
29
+
30
+ return [
31
+ timestamps,
32
+ getPeriodDimension( 'desktop_overall_score', periods ),
33
+ getPeriodDimension( 'mobile_overall_score', periods ),
34
+ ];
35
+ }, [ periods ] );
36
+ }
@@ -0,0 +1,40 @@
1
+ import { throttle } from '@wordpress/compose';
2
+ import React, { useEffect } from 'react';
3
+ import uPlot from 'uplot';
4
+
5
+ const THROTTLE_DURATION = 400; // in ms
6
+
7
+ /**
8
+ * Custom hook to handle resizing of uPlot charts.
9
+ *
10
+ * @param {React.RefObject<uPlot>} uplotRef - The ref object for the uPlot instance.
11
+ * @param {React.RefObject<HTMLDivElement>} containerRef - The ref object for the container div.
12
+ */
13
+ export default function useResize(
14
+ uplotRef: React.RefObject< uPlot >,
15
+ containerRef: React.RefObject< HTMLDivElement >
16
+ ) {
17
+ useEffect( () => {
18
+ if ( ! uplotRef.current || ! containerRef.current ) {
19
+ return;
20
+ }
21
+
22
+ const resizeChart = throttle( () => {
23
+ // Repeat the check since resize can happen much later than event registration.
24
+ if ( ! uplotRef.current || ! containerRef.current ) {
25
+ return;
26
+ }
27
+
28
+ // Only update width, not height.
29
+ uplotRef.current.setSize( {
30
+ height: uplotRef.current.height,
31
+ width: containerRef.current.clientWidth,
32
+ } );
33
+ }, THROTTLE_DURATION );
34
+ resizeChart();
35
+ window.addEventListener( 'resize', resizeChart );
36
+
37
+ // Cleanup on unmount.
38
+ return () => window.removeEventListener( 'resize', resizeChart );
39
+ }, [ uplotRef, containerRef ] );
40
+ }
@@ -259,6 +259,19 @@ class Gridicon extends Component< GridiconProps > {
259
259
  <path d="M20 4v2h-2V4H6v2H4V4c-1.105 0-2 .895-2 2v12c0 1.105.895 2 2 2v-2h2v2h12v-2h2v2c1.105 0 2-.895 2-2V6c0-1.105-.895-2-2-2zM6 16H4v-3h2v3zm0-5H4V8h2v3zm4 4V9l4.5 3-4.5 3zm10 1h-2v-3h2v3zm0-5h-2V8h2v3z" />
260
260
  </g>
261
261
  );
262
+ case 'gridicons-lock':
263
+ return (
264
+ <>
265
+ <g id="lock">
266
+ <path
267
+ d="M18,8h-1V7c0-2.757-2.243-5-5-5S7,4.243,7,7v1H6c-1.105,0-2,0.895-2,2v10c0,1.105,0.895,2,2,2h12c1.105,0,2-0.895,2-2V10
268
+ C20,8.895,19.105,8,18,8z M9,7c0-1.654,1.346-3,3-3s3,1.346,3,3v1H9V7z M13,15.723V18h-2v-2.277c-0.595-0.346-1-0.984-1-1.723
269
+ c0-1.105,0.895-2,2-2s2,0.895,2,2C14,14.738,13.595,15.376,13,15.723z"
270
+ />
271
+ </g>
272
+ <g id="Layer_1"></g>
273
+ </>
274
+ );
262
275
  }
263
276
  }
264
277
 
@@ -1,17 +1,17 @@
1
1
  import classNames from 'classnames';
2
2
  import React from 'react';
3
3
  import ReactSlider from 'react-slider';
4
- import { PricingSliderProps } from './types';
4
+ import { NumberSliderProps } from './types';
5
5
  import './style.scss';
6
6
 
7
7
  /**
8
- * Generate Pricing Slider
8
+ * Generate Number Slider
9
9
  * More support from the original ReactSlider component: https://zillow.github.io/react-slider/
10
10
  *
11
- * @param {PricingSliderProps} props - Props
11
+ * @param {NumberSliderProps} props - Props
12
12
  * @returns {React.ReactElement} - JSX element
13
13
  */
14
- const PricingSlider: React.FC< PricingSliderProps > = ( {
14
+ const NumberSlider: React.FC< NumberSliderProps > = ( {
15
15
  className,
16
16
  maxValue = 100,
17
17
  minValue = 0,
@@ -24,8 +24,8 @@ const PricingSlider: React.FC< PricingSliderProps > = ( {
24
24
  } ) => {
25
25
  const [ isThumbHolding, setIsThumbHolding ] = React.useState( false );
26
26
 
27
- const componentClassName = classNames( 'jp-components-pricing-slider', className, {
28
- 'jp-components-pricing-slider--is-holding': isThumbHolding,
27
+ const componentClassName = classNames( 'jp-components-number-slider', className, {
28
+ 'jp-components-number-slider--is-holding': isThumbHolding,
29
29
  } );
30
30
 
31
31
  const onBeforeChangeCallback = beforeValue => {
@@ -51,12 +51,12 @@ const PricingSlider: React.FC< PricingSliderProps > = ( {
51
51
  };
52
52
 
53
53
  return (
54
- <div className={ componentClassName } data-testid="pricing-slider">
54
+ <div className={ componentClassName } data-testid="number-slider">
55
55
  <ReactSlider
56
- className="jp-components-pricing-slider__control"
57
- thumbClassName="jp-components-pricing-slider__thumb"
58
- thumbActiveClassName="jp-components-pricing-slider__thumb--is-active"
59
- trackClassName="jp-components-pricing-slider__track"
56
+ className="jp-components-number-slider__control"
57
+ thumbClassName="jp-components-number-slider__thumb"
58
+ thumbActiveClassName="jp-components-number-slider__thumb--is-active"
59
+ trackClassName="jp-components-number-slider__track"
60
60
  value={ value }
61
61
  max={ maxValue }
62
62
  min={ minValue }
@@ -70,4 +70,4 @@ const PricingSlider: React.FC< PricingSliderProps > = ( {
70
70
  );
71
71
  };
72
72
 
73
- export default PricingSlider;
73
+ export default NumberSlider;
@@ -9,33 +9,33 @@ $track-height: 8px;
9
9
  }
10
10
 
11
11
  // On holding thumb styling
12
- .jp-components-pricing-slider--is-holding {
13
- .jp-components-pricing-slider__thumb {
12
+ .jp-components-number-slider--is-holding {
13
+ .jp-components-number-slider__thumb {
14
14
  box-shadow: 0 6px 8px rgba(0, 0, 0, 0.08), 0 1px 2px rgba(0, 0, 0, 0.04), 0 0 0 3px rgba(6, 158, 8, 0.25);
15
15
  }
16
16
  }
17
17
 
18
18
  // Base styles
19
- .jp-components-pricing-slider__control {
19
+ .jp-components-number-slider__control {
20
20
  width: 100%;
21
21
  height: $thumb-height;
22
22
  }
23
23
 
24
- .jp-components-pricing-slider__track {
24
+ .jp-components-number-slider__track {
25
25
  @include adjust-track-rail-styles;
26
26
  top: calc(($thumb-height / 2) - ($track-height / 2));
27
27
  background: var(--jp-gray);
28
28
 
29
- &.jp-components-pricing-slider__track-0 {
29
+ &.jp-components-number-slider__track-0 {
30
30
  background: var(--jp-green-40);
31
31
  }
32
32
 
33
- &.jp-components-pricing-slider__track-1 {
33
+ &.jp-components-number-slider__track-1 {
34
34
  background: var(--jp-gray);
35
35
  }
36
36
  }
37
37
 
38
- .jp-components-pricing-slider__thumb {
38
+ .jp-components-number-slider__thumb {
39
39
  display: flex;
40
40
  align-items: center;
41
41
  justify-content: center;
@@ -56,7 +56,7 @@ $track-height: 8px;
56
56
  cursor: pointer;
57
57
 
58
58
  // On focus thumb styling
59
- &.jp-components-pricing-slider__thumb--is-active {
59
+ &.jp-components-number-slider__thumb--is-active {
60
60
  outline: none;
61
61
  }
62
62
 
@@ -4,9 +4,9 @@ interface HTMLPropsWithRefCallback< T > extends HTMLProps< T > {
4
4
  ref: RefCallback< T >;
5
5
  }
6
6
 
7
- export type PricingSliderProps = {
7
+ export type NumberSliderProps = {
8
8
  /**
9
- * The wrapper class name of this PricingSlider component.
9
+ * The wrapper class name of this NumberSlider component.
10
10
  */
11
11
  className?: string;
12
12
 
@@ -0,0 +1,19 @@
1
+ import './style.scss';
2
+
3
+ type PopoverProps = {
4
+ action: React.ReactNode;
5
+ icon?: React.ReactNode;
6
+ children?: React.ReactNode;
7
+ };
8
+
9
+ const Popover: React.FC< PopoverProps > = ( { icon, children, action } ) => {
10
+ return (
11
+ <div className="jp-popover">
12
+ <div className="jp-popover__icon">{ icon }</div>
13
+ <div className="jp-popover__body">{ children }</div>
14
+ <div className="jp-popover__action">{ action }</div>
15
+ </div>
16
+ );
17
+ };
18
+
19
+ export default Popover;
@@ -0,0 +1,25 @@
1
+ .jp-popover {
2
+ width: 600px;
3
+ max-width: calc( 100% - 32px );
4
+ padding: 16px;
5
+ background-color: #fff;
6
+ box-shadow: 0px 1px 2px 0px rgba(0, 0, 0, 0.06), 0px 4px 8px 0px rgba(0, 0, 0, 0.03);
7
+ border: 1px solid rgba(220, 220, 222, 1);
8
+ border-radius: 4px;
9
+ text-align: center;
10
+ position: absolute;
11
+ top: 50%;
12
+ left: 50%;
13
+ transform: translate(-50%, -50%);
14
+
15
+ > * {
16
+ margin: 16px;
17
+ clear: both;
18
+ }
19
+
20
+ &__body p {
21
+ font-weight: 400;
22
+ line-height: 1.6em;
23
+ font-family: "SF Pro Text", -apple-system, BlinkMacSystemFont, "Segoe UI", "Roboto", "Oxygen-Sans", "Ubuntu", "Cantarell", "Helvetica Neue", sans-serif;
24
+ }
25
+ }
@@ -63,6 +63,11 @@ const ProductPrice: React.FC< ProductPriceProps > = ( {
63
63
  hidePriceFraction={ hidePriceFraction }
64
64
  />
65
65
  ) }
66
+ { discountElt && (
67
+ <Text className={ classnames( styles[ 'promo-label' ], 'product-price_promo_label' ) }>
68
+ { discountElt }
69
+ </Text>
70
+ ) }
66
71
  </div>
67
72
  </div>
68
73
  <div className={ styles.footer }>
@@ -76,11 +81,6 @@ const ProductPrice: React.FC< ProductPriceProps > = ( {
76
81
  { promoLabel }
77
82
  </Text>
78
83
  ) }
79
- { discountElt && (
80
- <Text className={ classnames( styles[ 'promo-label' ], 'product-price_promo_label' ) }>
81
- { discountElt }
82
- </Text>
83
- ) }
84
84
  </div>
85
85
  </>
86
86
  );