@wordpress/block-library 9.31.1-next.f56bd8138.0 → 9.32.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.
- package/CHANGELOG.md +2 -0
- package/build/accordion/edit.js +62 -12
- package/build/accordion/edit.js.map +1 -1
- package/build/accordion/index.js +15 -8
- package/build/accordion/index.js.map +1 -1
- package/build/accordion/view.js +15 -15
- package/build/accordion/view.js.map +1 -1
- package/build/accordion-heading/edit.js +68 -0
- package/build/accordion-heading/edit.js.map +1 -0
- package/build/accordion-heading/icon.js.map +1 -0
- package/build/{accordion-header → accordion-heading}/index.js +9 -17
- package/build/accordion-heading/index.js.map +1 -0
- package/build/{accordion-content → accordion-heading}/init.js.map +1 -1
- package/build/{accordion-header → accordion-heading}/save.js +7 -20
- package/build/accordion-heading/save.js.map +1 -0
- package/build/{accordion-content → accordion-item}/edit.js +8 -2
- package/build/accordion-item/edit.js.map +1 -0
- package/build/accordion-item/icon.js.map +1 -0
- package/build/{accordion-content → accordion-item}/index.js +6 -5
- package/build/accordion-item/index.js.map +1 -0
- package/build/accordion-item/init.js.map +1 -0
- package/build/{accordion-content → accordion-item}/save.js +5 -7
- package/build/accordion-item/save.js.map +1 -0
- package/build/accordion-panel/edit.js +2 -1
- package/build/accordion-panel/edit.js.map +1 -1
- package/build/accordion-panel/index.js +5 -3
- package/build/accordion-panel/index.js.map +1 -1
- package/build/accordion-panel/save.js +3 -1
- package/build/accordion-panel/save.js.map +1 -1
- package/build/block/index.js +1 -0
- package/build/block/index.js.map +1 -1
- package/build/group/variations.js +3 -4
- package/build/group/variations.js.map +1 -1
- package/build/index.js +24 -4
- package/build/index.js.map +1 -1
- package/build/navigation/edit/leaf-more-menu.js +6 -1
- package/build/navigation/edit/leaf-more-menu.js.map +1 -1
- package/build/navigation-link/edit.js +3 -140
- package/build/navigation-link/edit.js.map +1 -1
- package/build/navigation-link/shared/controls.js +171 -0
- package/build/navigation-link/shared/controls.js.map +1 -0
- package/build/navigation-link/shared/index.js +13 -0
- package/build/navigation-link/shared/index.js.map +1 -0
- package/build/navigation-submenu/edit.js +7 -114
- package/build/navigation-submenu/edit.js.map +1 -1
- package/build/pattern/index.js +1 -0
- package/build/pattern/index.js.map +1 -1
- package/build/post-date/edit.js +16 -3
- package/build/post-date/edit.js.map +1 -1
- package/build/post-time-to-read/edit.js +27 -15
- package/build/post-time-to-read/edit.js.map +1 -1
- package/build/post-time-to-read/index.js +7 -1
- package/build/post-time-to-read/index.js.map +1 -1
- package/build/post-time-to-read/variations.js +41 -0
- package/build/post-time-to-read/variations.js.map +1 -0
- package/build/query-title/edit.js +1 -1
- package/build/query-title/edit.js.map +1 -1
- package/build/table-of-contents/index.js +1 -0
- package/build/table-of-contents/index.js.map +1 -1
- package/build/template-part/index.js +1 -0
- package/build/template-part/index.js.map +1 -1
- package/build/utils/get-transformed-metadata.js +7 -0
- package/build/utils/get-transformed-metadata.js.map +1 -1
- package/build-module/accordion/edit.js +66 -16
- package/build-module/accordion/edit.js.map +1 -1
- package/build-module/accordion/index.js +15 -8
- package/build-module/accordion/index.js.map +1 -1
- package/build-module/accordion/view.js +15 -15
- package/build-module/accordion/view.js.map +1 -1
- package/build-module/accordion-heading/edit.js +61 -0
- package/build-module/accordion-heading/edit.js.map +1 -0
- package/build-module/accordion-heading/icon.js.map +1 -0
- package/build-module/{accordion-header → accordion-heading}/index.js +9 -17
- package/build-module/accordion-heading/index.js.map +1 -0
- package/build-module/{accordion-content → accordion-heading}/init.js.map +1 -1
- package/build-module/{accordion-header → accordion-heading}/save.js +7 -18
- package/build-module/accordion-heading/save.js.map +1 -0
- package/build-module/{accordion-content → accordion-item}/edit.js +8 -2
- package/build-module/accordion-item/edit.js.map +1 -0
- package/build-module/accordion-item/icon.js.map +1 -0
- package/build-module/{accordion-content → accordion-item}/index.js +6 -5
- package/build-module/accordion-item/index.js.map +1 -0
- package/build-module/accordion-item/init.js.map +1 -0
- package/build-module/{accordion-content → accordion-item}/save.js +5 -7
- package/build-module/accordion-item/save.js.map +1 -0
- package/build-module/accordion-panel/edit.js +2 -1
- package/build-module/accordion-panel/edit.js.map +1 -1
- package/build-module/accordion-panel/index.js +5 -3
- package/build-module/accordion-panel/index.js.map +1 -1
- package/build-module/accordion-panel/save.js +3 -1
- package/build-module/accordion-panel/save.js.map +1 -1
- package/build-module/block/index.js +1 -0
- package/build-module/block/index.js.map +1 -1
- package/build-module/group/variations.js +3 -4
- package/build-module/group/variations.js.map +1 -1
- package/build-module/index.js +25 -5
- package/build-module/index.js.map +1 -1
- package/build-module/navigation/edit/leaf-more-menu.js +6 -1
- package/build-module/navigation/edit/leaf-more-menu.js.map +1 -1
- package/build-module/navigation-link/edit.js +4 -141
- package/build-module/navigation-link/edit.js.map +1 -1
- package/build-module/navigation-link/shared/controls.js +165 -0
- package/build-module/navigation-link/shared/controls.js.map +1 -0
- package/build-module/navigation-link/shared/index.js +9 -0
- package/build-module/navigation-link/shared/index.js.map +1 -0
- package/build-module/navigation-submenu/edit.js +7 -114
- package/build-module/navigation-submenu/edit.js.map +1 -1
- package/build-module/pattern/index.js +1 -0
- package/build-module/pattern/index.js.map +1 -1
- package/build-module/post-date/edit.js +17 -4
- package/build-module/post-date/edit.js.map +1 -1
- package/build-module/post-time-to-read/edit.js +27 -15
- package/build-module/post-time-to-read/edit.js.map +1 -1
- package/build-module/post-time-to-read/index.js +7 -1
- package/build-module/post-time-to-read/index.js.map +1 -1
- package/build-module/post-time-to-read/variations.js +33 -0
- package/build-module/post-time-to-read/variations.js.map +1 -0
- package/build-module/query-title/edit.js +1 -1
- package/build-module/query-title/edit.js.map +1 -1
- package/build-module/table-of-contents/index.js +1 -0
- package/build-module/table-of-contents/index.js.map +1 -1
- package/build-module/template-part/index.js +1 -0
- package/build-module/template-part/index.js.map +1 -1
- package/build-module/utils/get-transformed-metadata.js +7 -0
- package/build-module/utils/get-transformed-metadata.js.map +1 -1
- package/build-style/{accordion → accordion-heading}/style-rtl.css +9 -54
- package/build-style/{accordion → accordion-heading}/style.css +9 -54
- package/build-style/accordion-item/style-rtl.css +155 -0
- package/build-style/accordion-item/style.css +155 -0
- package/build-style/accordion-panel/style-rtl.css +140 -0
- package/build-style/accordion-panel/style.css +140 -0
- package/build-style/style-rtl.css +23 -42
- package/build-style/style.css +23 -42
- package/package.json +35 -35
- package/src/accordion/block.json +11 -4
- package/src/accordion/edit.js +70 -13
- package/src/accordion/index.js +4 -4
- package/src/accordion/index.php +1 -1
- package/src/accordion/view.js +15 -15
- package/src/{accordion-header → accordion-heading}/block.json +10 -17
- package/src/accordion-heading/edit.js +70 -0
- package/src/{accordion-header → accordion-heading}/save.js +8 -18
- package/src/accordion-heading/style.scss +43 -0
- package/src/{accordion-content → accordion-item}/block.json +6 -5
- package/src/{accordion-content → accordion-item}/edit.js +12 -2
- package/src/{accordion-content → accordion-item}/index.php +11 -11
- package/src/{accordion-content → accordion-item}/save.js +4 -10
- package/src/accordion-item/style.scss +21 -0
- package/src/accordion-panel/block.json +5 -3
- package/src/accordion-panel/edit.js +1 -0
- package/src/accordion-panel/save.js +3 -1
- package/src/accordion-panel/style.scss +8 -0
- package/src/block/block.json +1 -0
- package/src/comments/index.php +2 -2
- package/src/group/variations.js +3 -14
- package/src/index.js +23 -4
- package/src/navigation/edit/leaf-more-menu.js +9 -1
- package/src/navigation/index.php +2 -2
- package/src/navigation-link/edit.js +3 -142
- package/src/navigation-link/shared/README.md +47 -0
- package/src/navigation-link/shared/controls.js +167 -0
- package/src/navigation-link/shared/index.js +8 -0
- package/src/navigation-link/shared/test/controls.js +210 -0
- package/src/navigation-submenu/edit.js +8 -129
- package/src/pattern/block.json +1 -0
- package/src/post-date/edit.js +16 -16
- package/src/post-time-to-read/block.json +5 -1
- package/src/post-time-to-read/edit.js +87 -59
- package/src/post-time-to-read/index.js +2 -0
- package/src/post-time-to-read/index.php +48 -23
- package/src/post-time-to-read/variations.js +39 -0
- package/src/query-title/edit.js +2 -1
- package/src/query-title/index.php +3 -1
- package/src/social-link/index.php +2 -2
- package/src/style.scss +3 -1
- package/src/table-of-contents/block.json +1 -0
- package/src/table-of-contents/index.php +44 -0
- package/src/template-part/block.json +1 -0
- package/src/utils/get-transformed-metadata.js +8 -0
- package/build/accordion-content/edit.js.map +0 -1
- package/build/accordion-content/icon.js.map +0 -1
- package/build/accordion-content/index.js.map +0 -1
- package/build/accordion-content/save.js.map +0 -1
- package/build/accordion-header/edit.js +0 -94
- package/build/accordion-header/edit.js.map +0 -1
- package/build/accordion-header/icon.js.map +0 -1
- package/build/accordion-header/index.js.map +0 -1
- package/build/accordion-header/init.js.map +0 -1
- package/build/accordion-header/save.js.map +0 -1
- package/build-module/accordion-content/edit.js.map +0 -1
- package/build-module/accordion-content/icon.js.map +0 -1
- package/build-module/accordion-content/index.js.map +0 -1
- package/build-module/accordion-content/save.js.map +0 -1
- package/build-module/accordion-header/edit.js +0 -85
- package/build-module/accordion-header/edit.js.map +0 -1
- package/build-module/accordion-header/icon.js.map +0 -1
- package/build-module/accordion-header/index.js.map +0 -1
- package/build-module/accordion-header/init.js.map +0 -1
- package/build-module/accordion-header/save.js.map +0 -1
- package/src/accordion/style.scss +0 -90
- package/src/accordion-header/edit.js +0 -94
- /package/build/{accordion-header → accordion-heading}/icon.js +0 -0
- /package/build/{accordion-content → accordion-heading}/init.js +0 -0
- /package/build/{accordion-content → accordion-item}/icon.js +0 -0
- /package/build/{accordion-header → accordion-item}/init.js +0 -0
- /package/build-module/{accordion-header → accordion-heading}/icon.js +0 -0
- /package/build-module/{accordion-content → accordion-heading}/init.js +0 -0
- /package/build-module/{accordion-content → accordion-item}/icon.js +0 -0
- /package/build-module/{accordion-header → accordion-item}/init.js +0 -0
- /package/src/{accordion-header → accordion-heading}/icon.js +0 -0
- /package/src/{accordion-content → accordion-heading}/index.js +0 -0
- /package/src/{accordion-content → accordion-heading}/init.js +0 -0
- /package/src/{accordion-content → accordion-item}/icon.js +0 -0
- /package/src/{accordion-header → accordion-item}/index.js +0 -0
- /package/src/{accordion-header → accordion-item}/init.js +0 -0
|
@@ -0,0 +1,210 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @jest-environment jsdom
|
|
3
|
+
*/
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* External dependencies
|
|
7
|
+
*/
|
|
8
|
+
import { render, screen, fireEvent } from '@testing-library/react';
|
|
9
|
+
|
|
10
|
+
/**
|
|
11
|
+
* Internal dependencies
|
|
12
|
+
*/
|
|
13
|
+
import { Controls } from '../controls';
|
|
14
|
+
|
|
15
|
+
// Mock the updateAttributes function
|
|
16
|
+
let mockUpdateAttributes;
|
|
17
|
+
jest.mock( '../../update-attributes', () => ( {
|
|
18
|
+
updateAttributes: ( ...args ) => mockUpdateAttributes( ...args ),
|
|
19
|
+
} ) );
|
|
20
|
+
|
|
21
|
+
// Mock the useToolsPanelDropdownMenuProps hook
|
|
22
|
+
jest.mock( '../../../utils/hooks', () => ( {
|
|
23
|
+
useToolsPanelDropdownMenuProps: () => ( {} ),
|
|
24
|
+
} ) );
|
|
25
|
+
|
|
26
|
+
describe( 'Controls', () => {
|
|
27
|
+
// Initialize the mock function
|
|
28
|
+
beforeAll( () => {
|
|
29
|
+
mockUpdateAttributes = jest.fn();
|
|
30
|
+
} );
|
|
31
|
+
|
|
32
|
+
const defaultProps = {
|
|
33
|
+
attributes: {
|
|
34
|
+
label: 'Test Link',
|
|
35
|
+
url: 'https://example.com',
|
|
36
|
+
description: 'Test description',
|
|
37
|
+
rel: 'nofollow',
|
|
38
|
+
opensInNewTab: false,
|
|
39
|
+
},
|
|
40
|
+
setAttributes: jest.fn(),
|
|
41
|
+
setIsEditingControl: jest.fn(),
|
|
42
|
+
};
|
|
43
|
+
|
|
44
|
+
beforeEach( () => {
|
|
45
|
+
jest.clearAllMocks();
|
|
46
|
+
mockUpdateAttributes.mockClear();
|
|
47
|
+
} );
|
|
48
|
+
|
|
49
|
+
it( 'renders all form controls', () => {
|
|
50
|
+
render( <Controls { ...defaultProps } /> );
|
|
51
|
+
|
|
52
|
+
expect( screen.getByLabelText( 'Text' ) ).toBeInTheDocument();
|
|
53
|
+
expect( screen.getByLabelText( 'Link' ) ).toBeInTheDocument();
|
|
54
|
+
expect(
|
|
55
|
+
screen.getByLabelText( 'Open in new tab' )
|
|
56
|
+
).toBeInTheDocument();
|
|
57
|
+
expect( screen.getByLabelText( 'Description' ) ).toBeInTheDocument();
|
|
58
|
+
expect( screen.getByLabelText( 'Rel attribute' ) ).toBeInTheDocument();
|
|
59
|
+
} );
|
|
60
|
+
|
|
61
|
+
it( 'strips HTML from label values', () => {
|
|
62
|
+
const propsWithHtml = {
|
|
63
|
+
...defaultProps,
|
|
64
|
+
attributes: {
|
|
65
|
+
...defaultProps.attributes,
|
|
66
|
+
label: '<strong>Bold Text</strong>',
|
|
67
|
+
},
|
|
68
|
+
};
|
|
69
|
+
render( <Controls { ...propsWithHtml } /> );
|
|
70
|
+
|
|
71
|
+
const textInput = screen.getByLabelText( 'Text' );
|
|
72
|
+
expect( textInput.value ).toBe( 'Bold Text' );
|
|
73
|
+
} );
|
|
74
|
+
|
|
75
|
+
it( 'decodes URL values for display', () => {
|
|
76
|
+
const propsWithEncodedUrl = {
|
|
77
|
+
...defaultProps,
|
|
78
|
+
attributes: {
|
|
79
|
+
...defaultProps.attributes,
|
|
80
|
+
url: 'https://example.com/test%20page',
|
|
81
|
+
},
|
|
82
|
+
};
|
|
83
|
+
render( <Controls { ...propsWithEncodedUrl } /> );
|
|
84
|
+
|
|
85
|
+
const urlInput = screen.getByLabelText( 'Link' );
|
|
86
|
+
expect( urlInput.value ).toBe( 'https://example.com/test page' );
|
|
87
|
+
} );
|
|
88
|
+
|
|
89
|
+
it( 'encodes URL values when changed', () => {
|
|
90
|
+
render( <Controls { ...defaultProps } /> );
|
|
91
|
+
|
|
92
|
+
const urlInput = screen.getByLabelText( 'Link' );
|
|
93
|
+
|
|
94
|
+
fireEvent.change( urlInput, {
|
|
95
|
+
target: { value: 'https://example.com/test page' },
|
|
96
|
+
} );
|
|
97
|
+
|
|
98
|
+
expect( defaultProps.setAttributes ).toHaveBeenCalledWith( {
|
|
99
|
+
url: 'https://example.com/test%20page',
|
|
100
|
+
} );
|
|
101
|
+
} );
|
|
102
|
+
|
|
103
|
+
it( 'calls updateAttributes on URL blur', () => {
|
|
104
|
+
render( <Controls { ...defaultProps } /> );
|
|
105
|
+
|
|
106
|
+
const urlInput = screen.getByLabelText( 'Link' );
|
|
107
|
+
|
|
108
|
+
fireEvent.focus( urlInput );
|
|
109
|
+
fireEvent.blur( urlInput );
|
|
110
|
+
|
|
111
|
+
expect( mockUpdateAttributes ).toHaveBeenCalledWith(
|
|
112
|
+
{ url: 'https://example.com' },
|
|
113
|
+
defaultProps.setAttributes,
|
|
114
|
+
{ ...defaultProps.attributes, url: 'https://example.com' }
|
|
115
|
+
);
|
|
116
|
+
} );
|
|
117
|
+
|
|
118
|
+
it( 'stores last URL value on focus and uses it in updateAttributes', () => {
|
|
119
|
+
const propsWithDifferentUrl = {
|
|
120
|
+
...defaultProps,
|
|
121
|
+
attributes: {
|
|
122
|
+
...defaultProps.attributes,
|
|
123
|
+
url: 'https://different.com',
|
|
124
|
+
},
|
|
125
|
+
};
|
|
126
|
+
render( <Controls { ...propsWithDifferentUrl } /> );
|
|
127
|
+
|
|
128
|
+
const urlInput = screen.getByLabelText( 'Link' );
|
|
129
|
+
|
|
130
|
+
fireEvent.focus( urlInput );
|
|
131
|
+
|
|
132
|
+
// Change the URL
|
|
133
|
+
fireEvent.change( urlInput, {
|
|
134
|
+
target: { value: 'https://new.com' },
|
|
135
|
+
} );
|
|
136
|
+
|
|
137
|
+
// Blur should call updateAttributes with the current URL (since url exists)
|
|
138
|
+
fireEvent.blur( urlInput );
|
|
139
|
+
|
|
140
|
+
expect( mockUpdateAttributes ).toHaveBeenCalledWith(
|
|
141
|
+
{ url: 'https://different.com' }, // Current URL from attributes (not input value)
|
|
142
|
+
defaultProps.setAttributes,
|
|
143
|
+
{
|
|
144
|
+
...propsWithDifferentUrl.attributes,
|
|
145
|
+
url: 'https://different.com',
|
|
146
|
+
} // lastURLRef.current
|
|
147
|
+
);
|
|
148
|
+
} );
|
|
149
|
+
|
|
150
|
+
it( 'calls setIsEditingControl on focus and blur for all inputs', () => {
|
|
151
|
+
render( <Controls { ...defaultProps } /> );
|
|
152
|
+
|
|
153
|
+
const textInput = screen.getByLabelText( 'Text' );
|
|
154
|
+
const urlInput = screen.getByLabelText( 'Link' );
|
|
155
|
+
|
|
156
|
+
// Test text input
|
|
157
|
+
fireEvent.focus( textInput );
|
|
158
|
+
expect( defaultProps.setIsEditingControl ).toHaveBeenCalledWith( true );
|
|
159
|
+
|
|
160
|
+
fireEvent.blur( textInput );
|
|
161
|
+
expect( defaultProps.setIsEditingControl ).toHaveBeenCalledWith(
|
|
162
|
+
false
|
|
163
|
+
);
|
|
164
|
+
|
|
165
|
+
// Test URL input
|
|
166
|
+
fireEvent.focus( urlInput );
|
|
167
|
+
expect( defaultProps.setIsEditingControl ).toHaveBeenCalledWith( true );
|
|
168
|
+
|
|
169
|
+
fireEvent.blur( urlInput );
|
|
170
|
+
expect( defaultProps.setIsEditingControl ).toHaveBeenCalledWith(
|
|
171
|
+
false
|
|
172
|
+
);
|
|
173
|
+
} );
|
|
174
|
+
|
|
175
|
+
it( 'handles all form field changes correctly', () => {
|
|
176
|
+
render( <Controls { ...defaultProps } /> );
|
|
177
|
+
|
|
178
|
+
// Test text change
|
|
179
|
+
const textInput = screen.getByLabelText( 'Text' );
|
|
180
|
+
fireEvent.change( textInput, { target: { value: 'New Label' } } );
|
|
181
|
+
expect( defaultProps.setAttributes ).toHaveBeenCalledWith( {
|
|
182
|
+
label: 'New Label',
|
|
183
|
+
} );
|
|
184
|
+
|
|
185
|
+
// Test description change
|
|
186
|
+
const descriptionInput = screen.getByLabelText( 'Description' );
|
|
187
|
+
fireEvent.change( descriptionInput, {
|
|
188
|
+
target: { value: 'New Description' },
|
|
189
|
+
} );
|
|
190
|
+
expect( defaultProps.setAttributes ).toHaveBeenCalledWith( {
|
|
191
|
+
description: 'New Description',
|
|
192
|
+
} );
|
|
193
|
+
|
|
194
|
+
// Test rel change
|
|
195
|
+
const relInput = screen.getByLabelText( 'Rel attribute' );
|
|
196
|
+
fireEvent.change( relInput, {
|
|
197
|
+
target: { value: 'nofollow noopener' },
|
|
198
|
+
} );
|
|
199
|
+
expect( defaultProps.setAttributes ).toHaveBeenCalledWith( {
|
|
200
|
+
rel: 'nofollow noopener',
|
|
201
|
+
} );
|
|
202
|
+
|
|
203
|
+
// Test checkbox change
|
|
204
|
+
const checkbox = screen.getByLabelText( 'Open in new tab' );
|
|
205
|
+
fireEvent.click( checkbox );
|
|
206
|
+
expect( defaultProps.setAttributes ).toHaveBeenCalledWith( {
|
|
207
|
+
opensInNewTab: true,
|
|
208
|
+
} );
|
|
209
|
+
} );
|
|
210
|
+
} );
|
|
@@ -7,15 +7,7 @@ import clsx from 'clsx';
|
|
|
7
7
|
* WordPress dependencies
|
|
8
8
|
*/
|
|
9
9
|
import { useSelect, useDispatch } from '@wordpress/data';
|
|
10
|
-
import {
|
|
11
|
-
CheckboxControl,
|
|
12
|
-
TextControl,
|
|
13
|
-
TextareaControl,
|
|
14
|
-
ToolbarButton,
|
|
15
|
-
ToolbarGroup,
|
|
16
|
-
__experimentalToolsPanel as ToolsPanel,
|
|
17
|
-
__experimentalToolsPanelItem as ToolsPanelItem,
|
|
18
|
-
} from '@wordpress/components';
|
|
10
|
+
import { ToolbarButton, ToolbarGroup } from '@wordpress/components';
|
|
19
11
|
import { displayShortcut, isKeyboardEvent } from '@wordpress/keycodes';
|
|
20
12
|
import { __ } from '@wordpress/i18n';
|
|
21
13
|
import {
|
|
@@ -41,11 +33,12 @@ import { useMergeRefs, usePrevious } from '@wordpress/compose';
|
|
|
41
33
|
import { ItemSubmenuIcon } from './icons';
|
|
42
34
|
import { LinkUI } from '../navigation-link/link-ui';
|
|
43
35
|
import { updateAttributes } from '../navigation-link/update-attributes';
|
|
36
|
+
import { Controls } from '../navigation-link/shared';
|
|
44
37
|
import {
|
|
45
38
|
getColors,
|
|
46
39
|
getNavigationChildBlockProps,
|
|
47
40
|
} from '../navigation/edit/utils';
|
|
48
|
-
import {
|
|
41
|
+
import { DEFAULT_BLOCK } from '../navigation/constants';
|
|
49
42
|
|
|
50
43
|
const ALLOWED_BLOCKS = [
|
|
51
44
|
'core/navigation-link',
|
|
@@ -53,10 +46,6 @@ const ALLOWED_BLOCKS = [
|
|
|
53
46
|
'core/page-list',
|
|
54
47
|
];
|
|
55
48
|
|
|
56
|
-
const DEFAULT_BLOCK = {
|
|
57
|
-
name: 'core/navigation-link',
|
|
58
|
-
};
|
|
59
|
-
|
|
60
49
|
/**
|
|
61
50
|
* A React hook to determine if it's dragging within the target element.
|
|
62
51
|
*
|
|
@@ -135,7 +124,7 @@ export default function NavigationSubmenuEdit( {
|
|
|
135
124
|
context,
|
|
136
125
|
clientId,
|
|
137
126
|
} ) {
|
|
138
|
-
const { label, url, description
|
|
127
|
+
const { label, url, description } = attributes;
|
|
139
128
|
|
|
140
129
|
const { showSubmenuIcon, maxNestingLevel, openSubmenusOnClick } = context;
|
|
141
130
|
|
|
@@ -154,7 +143,6 @@ export default function NavigationSubmenuEdit( {
|
|
|
154
143
|
const isDraggingWithin = useIsDraggingWithin( listItemRef );
|
|
155
144
|
const itemLabelPlaceholder = __( 'Add text…' );
|
|
156
145
|
const ref = useRef();
|
|
157
|
-
const dropdownMenuProps = useToolsPanelDropdownMenuProps();
|
|
158
146
|
|
|
159
147
|
const {
|
|
160
148
|
parentCount,
|
|
@@ -383,120 +371,11 @@ export default function NavigationSubmenuEdit( {
|
|
|
383
371
|
/>
|
|
384
372
|
</ToolbarGroup>
|
|
385
373
|
</BlockControls>
|
|
386
|
-
{ /* Warning, this duplicated in packages/block-library/src/navigation-link/edit.js */ }
|
|
387
374
|
<InspectorControls>
|
|
388
|
-
<
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
label: '',
|
|
393
|
-
url: '',
|
|
394
|
-
description: '',
|
|
395
|
-
rel: '',
|
|
396
|
-
opensInNewTab: false,
|
|
397
|
-
} );
|
|
398
|
-
} }
|
|
399
|
-
dropdownMenuProps={ dropdownMenuProps }
|
|
400
|
-
>
|
|
401
|
-
<ToolsPanelItem
|
|
402
|
-
label={ __( 'Text' ) }
|
|
403
|
-
isShownByDefault
|
|
404
|
-
hasValue={ () => !! label }
|
|
405
|
-
onDeselect={ () => setAttributes( { label: '' } ) }
|
|
406
|
-
>
|
|
407
|
-
<TextControl
|
|
408
|
-
__nextHasNoMarginBottom
|
|
409
|
-
__next40pxDefaultSize
|
|
410
|
-
value={ label || '' }
|
|
411
|
-
onChange={ ( labelValue ) => {
|
|
412
|
-
setAttributes( { label: labelValue } );
|
|
413
|
-
} }
|
|
414
|
-
label={ __( 'Text' ) }
|
|
415
|
-
autoComplete="off"
|
|
416
|
-
/>
|
|
417
|
-
</ToolsPanelItem>
|
|
418
|
-
|
|
419
|
-
<ToolsPanelItem
|
|
420
|
-
label={ __( 'Link' ) }
|
|
421
|
-
isShownByDefault
|
|
422
|
-
hasValue={ () => !! url }
|
|
423
|
-
onDeselect={ () => setAttributes( { url: '' } ) }
|
|
424
|
-
>
|
|
425
|
-
<TextControl
|
|
426
|
-
__nextHasNoMarginBottom
|
|
427
|
-
__next40pxDefaultSize
|
|
428
|
-
value={ url || '' }
|
|
429
|
-
onChange={ ( urlValue ) => {
|
|
430
|
-
setAttributes( { url: urlValue } );
|
|
431
|
-
} }
|
|
432
|
-
label={ __( 'Link' ) }
|
|
433
|
-
autoComplete="off"
|
|
434
|
-
type="url"
|
|
435
|
-
/>
|
|
436
|
-
</ToolsPanelItem>
|
|
437
|
-
|
|
438
|
-
<ToolsPanelItem
|
|
439
|
-
hasValue={ () => !! opensInNewTab }
|
|
440
|
-
label={ __( 'Open in new tab' ) }
|
|
441
|
-
onDeselect={ () =>
|
|
442
|
-
setAttributes( { opensInNewTab: false } )
|
|
443
|
-
}
|
|
444
|
-
isShownByDefault
|
|
445
|
-
>
|
|
446
|
-
<CheckboxControl
|
|
447
|
-
__nextHasNoMarginBottom
|
|
448
|
-
label={ __( 'Open in new tab' ) }
|
|
449
|
-
checked={ opensInNewTab }
|
|
450
|
-
onChange={ ( value ) =>
|
|
451
|
-
setAttributes( { opensInNewTab: value } )
|
|
452
|
-
}
|
|
453
|
-
/>
|
|
454
|
-
</ToolsPanelItem>
|
|
455
|
-
|
|
456
|
-
<ToolsPanelItem
|
|
457
|
-
label={ __( 'Description' ) }
|
|
458
|
-
isShownByDefault
|
|
459
|
-
hasValue={ () => !! description }
|
|
460
|
-
onDeselect={ () =>
|
|
461
|
-
setAttributes( { description: '' } )
|
|
462
|
-
}
|
|
463
|
-
>
|
|
464
|
-
<TextareaControl
|
|
465
|
-
__nextHasNoMarginBottom
|
|
466
|
-
value={ description || '' }
|
|
467
|
-
onChange={ ( descriptionValue ) => {
|
|
468
|
-
setAttributes( {
|
|
469
|
-
description: descriptionValue,
|
|
470
|
-
} );
|
|
471
|
-
} }
|
|
472
|
-
label={ __( 'Description' ) }
|
|
473
|
-
help={ __(
|
|
474
|
-
'The description will be displayed in the menu if the current theme supports it.'
|
|
475
|
-
) }
|
|
476
|
-
/>
|
|
477
|
-
</ToolsPanelItem>
|
|
478
|
-
|
|
479
|
-
<ToolsPanelItem
|
|
480
|
-
label={ __( 'Rel attribute' ) }
|
|
481
|
-
isShownByDefault
|
|
482
|
-
hasValue={ () => !! rel }
|
|
483
|
-
onDeselect={ () => setAttributes( { rel: '' } ) }
|
|
484
|
-
>
|
|
485
|
-
<TextControl
|
|
486
|
-
__nextHasNoMarginBottom
|
|
487
|
-
__next40pxDefaultSize
|
|
488
|
-
value={ rel || '' }
|
|
489
|
-
onChange={ ( relValue ) => {
|
|
490
|
-
setAttributes( { rel: relValue } );
|
|
491
|
-
} }
|
|
492
|
-
label={ __( 'Rel attribute' ) }
|
|
493
|
-
autoComplete="off"
|
|
494
|
-
help={ __(
|
|
495
|
-
'The relationship of the linked URL as space-separated link types.'
|
|
496
|
-
) }
|
|
497
|
-
/>
|
|
498
|
-
</ToolsPanelItem>
|
|
499
|
-
</ToolsPanel>
|
|
375
|
+
<Controls
|
|
376
|
+
attributes={ attributes }
|
|
377
|
+
setAttributes={ setAttributes }
|
|
378
|
+
/>
|
|
500
379
|
</InspectorControls>
|
|
501
380
|
<div { ...blockProps }>
|
|
502
381
|
{ /* eslint-disable jsx-a11y/anchor-is-valid */ }
|
package/src/pattern/block.json
CHANGED
package/src/post-date/edit.js
CHANGED
|
@@ -6,7 +6,7 @@ import clsx from 'clsx';
|
|
|
6
6
|
/**
|
|
7
7
|
* WordPress dependencies
|
|
8
8
|
*/
|
|
9
|
-
import {
|
|
9
|
+
import { store as coreStore } from '@wordpress/core-data';
|
|
10
10
|
import { useEffect, useMemo, useState } from '@wordpress/element';
|
|
11
11
|
import {
|
|
12
12
|
dateI18n,
|
|
@@ -81,22 +81,22 @@ export default function PostDateEdit( {
|
|
|
81
81
|
|
|
82
82
|
const isDescendentOfQueryLoop = Number.isFinite( queryId );
|
|
83
83
|
const dateSettings = getDateSettings();
|
|
84
|
-
const [ siteFormat = dateSettings.formats.date ] = useEntityProp(
|
|
85
|
-
'root',
|
|
86
|
-
'site',
|
|
87
|
-
'date_format'
|
|
88
|
-
);
|
|
89
|
-
const [ siteTimeFormat = dateSettings.formats.time ] = useEntityProp(
|
|
90
|
-
'root',
|
|
91
|
-
'site',
|
|
92
|
-
'time_format'
|
|
93
|
-
);
|
|
94
84
|
|
|
95
|
-
const
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
85
|
+
const {
|
|
86
|
+
postType,
|
|
87
|
+
siteFormat = dateSettings.formats.date,
|
|
88
|
+
siteTimeFormat = dateSettings.formats.time,
|
|
89
|
+
} = useSelect(
|
|
90
|
+
( select ) => {
|
|
91
|
+
const { getPostType, getEntityRecord } = select( coreStore );
|
|
92
|
+
const siteSettings = getEntityRecord( 'root', 'site' );
|
|
93
|
+
|
|
94
|
+
return {
|
|
95
|
+
siteFormat: siteSettings?.date_format,
|
|
96
|
+
siteTimeFormat: siteSettings?.time_format,
|
|
97
|
+
postType: postTypeSlug ? getPostType( postTypeSlug ) : null,
|
|
98
|
+
};
|
|
99
|
+
},
|
|
100
100
|
[ postTypeSlug ]
|
|
101
101
|
);
|
|
102
102
|
|
|
@@ -5,7 +5,7 @@
|
|
|
5
5
|
"name": "core/post-time-to-read",
|
|
6
6
|
"title": "Time to Read",
|
|
7
7
|
"category": "theme",
|
|
8
|
-
"description": "Show minutes required to finish reading the post.",
|
|
8
|
+
"description": "Show minutes required to finish reading the post. Can also show a word count.",
|
|
9
9
|
"textdomain": "default",
|
|
10
10
|
"usesContext": [ "postId", "postType" ],
|
|
11
11
|
"attributes": {
|
|
@@ -16,6 +16,10 @@
|
|
|
16
16
|
"type": "boolean",
|
|
17
17
|
"default": true
|
|
18
18
|
},
|
|
19
|
+
"displayMode": {
|
|
20
|
+
"type": "string",
|
|
21
|
+
"default": "time"
|
|
22
|
+
},
|
|
19
23
|
"averageReadingSpeed": {
|
|
20
24
|
"type": "number",
|
|
21
25
|
"default": 189
|
|
@@ -29,7 +29,9 @@ import { count as wordCount } from '@wordpress/wordcount';
|
|
|
29
29
|
import { useToolsPanelDropdownMenuProps } from '../utils/hooks';
|
|
30
30
|
|
|
31
31
|
function PostTimeToReadEdit( { attributes, setAttributes, context } ) {
|
|
32
|
-
const { textAlign, displayAsRange, averageReadingSpeed } =
|
|
32
|
+
const { textAlign, displayAsRange, displayMode, averageReadingSpeed } =
|
|
33
|
+
attributes;
|
|
34
|
+
|
|
33
35
|
const { postId, postType } = context;
|
|
34
36
|
const dropdownMenuProps = useToolsPanelDropdownMenuProps();
|
|
35
37
|
|
|
@@ -44,7 +46,7 @@ function PostTimeToReadEdit( { attributes, setAttributes, context } ) {
|
|
|
44
46
|
id: postId,
|
|
45
47
|
} );
|
|
46
48
|
|
|
47
|
-
const
|
|
49
|
+
const displayString = useMemo( () => {
|
|
48
50
|
// Replicates the logic found in getEditedPostContent().
|
|
49
51
|
let content;
|
|
50
52
|
if ( contentStructure instanceof Function ) {
|
|
@@ -69,38 +71,62 @@ function PostTimeToReadEdit( { attributes, setAttributes, context } ) {
|
|
|
69
71
|
);
|
|
70
72
|
|
|
71
73
|
const totalWords = wordCount( content || '', wordCountType );
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
74
|
+
|
|
75
|
+
// Add "time to read" part, if enabled.
|
|
76
|
+
if ( displayMode === 'time' ) {
|
|
77
|
+
if ( displayAsRange ) {
|
|
78
|
+
let maxMinutes = Math.max(
|
|
79
|
+
1,
|
|
80
|
+
Math.round( ( totalWords / averageReadingSpeed ) * 1.2 )
|
|
81
|
+
);
|
|
82
|
+
const minMinutes = Math.max(
|
|
83
|
+
1,
|
|
84
|
+
Math.round( ( totalWords / averageReadingSpeed ) * 0.8 )
|
|
85
|
+
);
|
|
86
|
+
|
|
87
|
+
if ( minMinutes === maxMinutes ) {
|
|
88
|
+
maxMinutes = maxMinutes + 1;
|
|
89
|
+
}
|
|
90
|
+
// translators: %1$s: minimum minutes, %2$s: maximum minutes to read the post.
|
|
91
|
+
const rangeLabel = _x(
|
|
92
|
+
'%1$s–%2$s minutes',
|
|
93
|
+
'Range of minutes to read'
|
|
94
|
+
);
|
|
95
|
+
return sprintf( rangeLabel, minMinutes, maxMinutes );
|
|
96
|
+
}
|
|
97
|
+
const minutesToRead = Math.max(
|
|
78
98
|
1,
|
|
79
|
-
Math.round(
|
|
99
|
+
Math.round( totalWords / averageReadingSpeed )
|
|
80
100
|
);
|
|
81
101
|
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
const rangeLabel = _x(
|
|
87
|
-
'%1$s–%2$s minutes',
|
|
88
|
-
'Range of minutes to read'
|
|
102
|
+
return sprintf(
|
|
103
|
+
/* translators: %s: the number of minutes to read the post. */
|
|
104
|
+
_n( '%s minute', '%s minutes', minutesToRead ),
|
|
105
|
+
minutesToRead
|
|
89
106
|
);
|
|
90
|
-
return sprintf( rangeLabel, minMinutes, maxMinutes );
|
|
91
107
|
}
|
|
92
108
|
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
109
|
+
// Add "word count" part, if enabled.
|
|
110
|
+
if ( displayMode === 'words' ) {
|
|
111
|
+
return wordCountType === 'words'
|
|
112
|
+
? sprintf(
|
|
113
|
+
/* translators: %s: the number of words in the post. */
|
|
114
|
+
_n( '%s word', '%s words', totalWords ),
|
|
115
|
+
totalWords.toLocaleString()
|
|
116
|
+
)
|
|
117
|
+
: sprintf(
|
|
118
|
+
/* translators: %s: the number of characters in the post. */
|
|
119
|
+
_n( '%s character', '%s characters', totalWords ),
|
|
120
|
+
totalWords.toLocaleString()
|
|
121
|
+
);
|
|
122
|
+
}
|
|
123
|
+
}, [
|
|
124
|
+
contentStructure,
|
|
125
|
+
blocks,
|
|
126
|
+
displayAsRange,
|
|
127
|
+
displayMode,
|
|
128
|
+
averageReadingSpeed,
|
|
129
|
+
] );
|
|
104
130
|
|
|
105
131
|
const blockProps = useBlockProps( {
|
|
106
132
|
className: clsx( {
|
|
@@ -118,43 +144,45 @@ function PostTimeToReadEdit( { attributes, setAttributes, context } ) {
|
|
|
118
144
|
} }
|
|
119
145
|
/>
|
|
120
146
|
</BlockControls>
|
|
121
|
-
|
|
122
|
-
<
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
displayAsRange: true,
|
|
127
|
-
} );
|
|
128
|
-
} }
|
|
129
|
-
dropdownMenuProps={ dropdownMenuProps }
|
|
130
|
-
>
|
|
131
|
-
<ToolsPanelItem
|
|
132
|
-
isShownByDefault
|
|
133
|
-
label={ _x(
|
|
134
|
-
'Display as range',
|
|
135
|
-
'Turns reading time range display on or off'
|
|
136
|
-
) }
|
|
137
|
-
hasValue={ () => ! displayAsRange }
|
|
138
|
-
onDeselect={ () => {
|
|
147
|
+
{ displayMode === 'time' && (
|
|
148
|
+
<InspectorControls>
|
|
149
|
+
<ToolsPanel
|
|
150
|
+
label={ __( 'Settings' ) }
|
|
151
|
+
resetAll={ () => {
|
|
139
152
|
setAttributes( {
|
|
140
153
|
displayAsRange: true,
|
|
141
154
|
} );
|
|
142
155
|
} }
|
|
156
|
+
dropdownMenuProps={ dropdownMenuProps }
|
|
143
157
|
>
|
|
144
|
-
<
|
|
145
|
-
|
|
146
|
-
label={
|
|
147
|
-
|
|
148
|
-
|
|
158
|
+
<ToolsPanelItem
|
|
159
|
+
isShownByDefault
|
|
160
|
+
label={ _x(
|
|
161
|
+
'Display as range',
|
|
162
|
+
'Turns reading time range display on or off'
|
|
163
|
+
) }
|
|
164
|
+
hasValue={ () => ! displayAsRange }
|
|
165
|
+
onDeselect={ () => {
|
|
149
166
|
setAttributes( {
|
|
150
|
-
displayAsRange:
|
|
151
|
-
} )
|
|
152
|
-
}
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
167
|
+
displayAsRange: true,
|
|
168
|
+
} );
|
|
169
|
+
} }
|
|
170
|
+
>
|
|
171
|
+
<ToggleControl
|
|
172
|
+
__nextHasNoMarginBottom
|
|
173
|
+
label={ __( 'Display as range' ) }
|
|
174
|
+
checked={ !! displayAsRange }
|
|
175
|
+
onChange={ () =>
|
|
176
|
+
setAttributes( {
|
|
177
|
+
displayAsRange: ! displayAsRange,
|
|
178
|
+
} )
|
|
179
|
+
}
|
|
180
|
+
/>
|
|
181
|
+
</ToolsPanelItem>
|
|
182
|
+
</ToolsPanel>
|
|
183
|
+
</InspectorControls>
|
|
184
|
+
) }
|
|
185
|
+
<div { ...blockProps }>{ displayString }</div>
|
|
158
186
|
</>
|
|
159
187
|
);
|
|
160
188
|
}
|
|
@@ -5,6 +5,7 @@ import initBlock from '../utils/init-block';
|
|
|
5
5
|
import metadata from './block.json';
|
|
6
6
|
import edit from './edit';
|
|
7
7
|
import icon from './icon';
|
|
8
|
+
import variations from './variations';
|
|
8
9
|
|
|
9
10
|
const { name } = metadata;
|
|
10
11
|
export { metadata, name };
|
|
@@ -12,6 +13,7 @@ export { metadata, name };
|
|
|
12
13
|
export const settings = {
|
|
13
14
|
icon,
|
|
14
15
|
edit,
|
|
16
|
+
variations,
|
|
15
17
|
example: {},
|
|
16
18
|
};
|
|
17
19
|
|