Skip to content

Commit 46b20d8

Browse files
Merge pull request #12111 from google/enhancement/11520-standardise-menu
Enhancement/11520 standardise menu
2 parents 775ab0b + 9919c97 commit 46b20d8

12 files changed

Lines changed: 324 additions & 131 deletions

File tree

Lines changed: 10 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
/**
2-
* UserMenu Item component.
2+
* HeaderMenu Divider component.
33
*
4-
* Site Kit by Google, Copyright 2023 Google LLC
4+
* Site Kit by Google, Copyright 2026 Google LLC
55
*
66
* Licensed under the Apache License, Version 2.0 (the "License");
77
* you may not use this file except in compliance with the License.
@@ -19,20 +19,18 @@
1919
/**
2020
* External dependencies
2121
*/
22+
import classnames from 'classnames';
2223
import PropTypes from 'prop-types';
2324

24-
export default function Item( { icon, label } ) {
25+
export default function Divider( { className } ) {
2526
return (
26-
<div className="googlesitekit-user-menu__item">
27-
<div className="googlesitekit-user-menu__item-icon">{ icon }</div>
28-
<span className="googlesitekit-user-menu__item-label">
29-
{ label }
30-
</span>
31-
</div>
27+
<li
28+
className={ classnames( 'mdc-list-divider', className ) }
29+
role="separator"
30+
/>
3231
);
3332
}
3433

35-
Item.propTypes = {
36-
icon: PropTypes.node,
37-
label: PropTypes.string,
34+
Divider.propTypes = {
35+
className: PropTypes.string,
3836
};
Lines changed: 98 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,98 @@
1+
/**
2+
* HeaderMenu MenuItem component.
3+
*
4+
* Site Kit by Google, Copyright 2026 Google LLC
5+
*
6+
* Licensed under the Apache License, Version 2.0 (the "License");
7+
* you may not use this file except in compliance with the License.
8+
* You may obtain a copy of the License at
9+
*
10+
* https://www.apache.org/licenses/LICENSE-2.0
11+
*
12+
* Unless required by applicable law or agreed to in writing, software
13+
* distributed under the License is distributed on an "AS IS" BASIS,
14+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15+
* See the License for the specific language governing permissions and
16+
* limitations under the License.
17+
*/
18+
19+
/**
20+
* External dependencies
21+
*/
22+
import classnames from 'classnames';
23+
import PropTypes from 'prop-types';
24+
25+
export default function MenuItem( {
26+
id,
27+
className,
28+
role,
29+
isInteractive = true,
30+
onClick,
31+
icon,
32+
label,
33+
description,
34+
trailing,
35+
children,
36+
itemClassName = 'googlesitekit-header-menu__item',
37+
iconClassName = 'googlesitekit-header-menu__item-icon',
38+
labelClassName = 'googlesitekit-header-menu__item-label',
39+
descriptionClassName = 'googlesitekit-header-menu__item-description',
40+
trailingClassName = 'googlesitekit-header-menu__item-trailing',
41+
} ) {
42+
const content = children || (
43+
<div className={ itemClassName }>
44+
{ icon && <div className={ iconClassName }>{ icon }</div> }
45+
{ label && <span className={ labelClassName }>{ label }</span> }
46+
{ description && (
47+
<span className={ descriptionClassName }>{ description }</span>
48+
) }
49+
{ trailing && (
50+
<span className={ trailingClassName }>{ trailing }</span>
51+
) }
52+
</div>
53+
);
54+
55+
const listRole = role === undefined && isInteractive ? 'menuitem' : role;
56+
const listClassName = classnames( className, {
57+
'mdc-list-item': isInteractive,
58+
} );
59+
const hasAction = typeof onClick === 'function';
60+
61+
return (
62+
<li
63+
id={ id }
64+
className={ listClassName || undefined }
65+
role={ listRole }
66+
>
67+
{ hasAction ? (
68+
<button
69+
type="button"
70+
className="googlesitekit-header-menu__item-button"
71+
onClick={ onClick }
72+
>
73+
{ content }
74+
</button>
75+
) : (
76+
content
77+
) }
78+
</li>
79+
);
80+
}
81+
82+
MenuItem.propTypes = {
83+
id: PropTypes.string,
84+
className: PropTypes.string,
85+
role: PropTypes.string,
86+
isInteractive: PropTypes.bool,
87+
onClick: PropTypes.func,
88+
icon: PropTypes.node,
89+
label: PropTypes.node,
90+
description: PropTypes.node,
91+
trailing: PropTypes.node,
92+
children: PropTypes.node,
93+
itemClassName: PropTypes.string,
94+
iconClassName: PropTypes.string,
95+
labelClassName: PropTypes.string,
96+
descriptionClassName: PropTypes.string,
97+
trailingClassName: PropTypes.string,
98+
};
Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
/**
2+
* HeaderMenu MenuSection component.
3+
*
4+
* Site Kit by Google, Copyright 2026 Google LLC
5+
*
6+
* Licensed under the Apache License, Version 2.0 (the "License");
7+
* you may not use this file except in compliance with the License.
8+
* You may obtain a copy of the License at
9+
*
10+
* https://www.apache.org/licenses/LICENSE-2.0
11+
*
12+
* Unless required by applicable law or agreed to in writing, software
13+
* distributed under the License is distributed on an "AS IS" BASIS,
14+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15+
* See the License for the specific language governing permissions and
16+
* limitations under the License.
17+
*/
18+
19+
/**
20+
* External dependencies
21+
*/
22+
import PropTypes from 'prop-types';
23+
24+
export default function MenuSection( { id, className, role, children } ) {
25+
return (
26+
<li id={ id } className={ className } role={ role }>
27+
{ children }
28+
</li>
29+
);
30+
}
31+
32+
MenuSection.propTypes = {
33+
id: PropTypes.string,
34+
className: PropTypes.string,
35+
role: PropTypes.string,
36+
children: PropTypes.node,
37+
};
Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
/**
2+
* HeaderMenu MenuWithHeading component.
3+
*
4+
* Site Kit by Google, Copyright 2026 Google LLC
5+
*
6+
* Licensed under the Apache License, Version 2.0 (the "License");
7+
* you may not use this file except in compliance with the License.
8+
* You may obtain a copy of the License at
9+
*
10+
* https://www.apache.org/licenses/LICENSE-2.0
11+
*
12+
* Unless required by applicable law or agreed to in writing, software
13+
* distributed under the License is distributed on an "AS IS" BASIS,
14+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15+
* See the License for the specific language governing permissions and
16+
* limitations under the License.
17+
*/
18+
19+
/**
20+
* External dependencies
21+
*/
22+
import PropTypes from 'prop-types';
23+
24+
/**
25+
* Internal dependencies
26+
*/
27+
import MenuSection from './MenuSection';
28+
29+
export default function MenuWithHeading( { heading, className, children } ) {
30+
return (
31+
<MenuSection className={ className }>
32+
{ heading }
33+
{ children }
34+
</MenuSection>
35+
);
36+
}
37+
38+
MenuWithHeading.propTypes = {
39+
heading: PropTypes.node,
40+
className: PropTypes.string,
41+
children: PropTypes.node,
42+
};
Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
/**
2+
* Site Kit by Google, Copyright 2026 Google LLC
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* https://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
17+
export { default as Divider } from './Divider';
18+
export { default as MenuItem } from './MenuItem';
19+
export { default as MenuSection } from './MenuSection';
20+
export { default as MenuWithHeading } from './MenuWithHeading';

assets/js/components/UserMenu/index.js

Lines changed: 27 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -34,11 +34,11 @@ import { ESCAPE, TAB } from '@wordpress/keycodes';
3434
import { useSelect, useDispatch } from 'googlesitekit-data';
3535
import { Button, Menu } from 'googlesitekit-components';
3636
import ModalDialog from '@/js/components/ModalDialog';
37+
import { MenuItem, MenuSection } from '@/js/components/HeaderMenu';
3738
import { trackEvent } from '@/js/util';
3839
import { clearCache } from '@/js/googlesitekit/api/cache';
3940
import Portal from '@/js/components/Portal';
4041
import Details from './Details';
41-
import Item from './Item';
4242
import DisconnectIcon from '@/svg/icons/disconnect.svg';
4343
import ManageSitesIcon from '@/svg/icons/manage-sites.svg';
4444
import ManageEmailReportsIcon from '@/svg/icons/manage-email-reports.svg';
@@ -282,49 +282,40 @@ export default function UserMenu() {
282282
onSelected={ handleMenuItemSelect }
283283
id="user-menu"
284284
>
285-
<li>
285+
<MenuSection>
286286
<Details />
287-
</li>
287+
</MenuSection>
288288
{ emailReportingEnabled && (
289-
<li
289+
<MenuItem
290290
id="manage-email-reports"
291-
className="mdc-list-item"
292-
role="menuitem"
293-
>
294-
<Item
295-
icon={ <ManageEmailReportsIcon width="24" /> }
296-
label={ __(
297-
'Manage email reports',
298-
'google-site-kit'
299-
) }
300-
/>
301-
</li>
291+
icon={ <ManageEmailReportsIcon width="24" /> }
292+
label={ __(
293+
'Manage email reports',
294+
'google-site-kit'
295+
) }
296+
itemClassName="googlesitekit-user-menu__item"
297+
iconClassName="googlesitekit-user-menu__item-icon"
298+
labelClassName="googlesitekit-user-menu__item-label"
299+
/>
302300
) }
303301
{ !! proxyPermissionsURL && (
304-
<li
302+
<MenuItem
305303
id="manage-sites"
306-
className="mdc-list-item"
307-
role="menuitem"
308-
>
309-
<Item
310-
icon={ <ManageSitesIcon width="24" /> }
311-
label={ __(
312-
'Manage Sites',
313-
'google-site-kit'
314-
) }
315-
/>
316-
</li>
304+
icon={ <ManageSitesIcon width="24" /> }
305+
label={ __( 'Manage Sites', 'google-site-kit' ) }
306+
itemClassName="googlesitekit-user-menu__item"
307+
iconClassName="googlesitekit-user-menu__item-icon"
308+
labelClassName="googlesitekit-user-menu__item-label"
309+
/>
317310
) }
318-
<li
311+
<MenuItem
319312
id="disconnect"
320-
className="mdc-list-item"
321-
role="menuitem"
322-
>
323-
<Item
324-
icon={ <DisconnectIcon width="24" /> }
325-
label={ __( 'Disconnect', 'google-site-kit' ) }
326-
/>
327-
</li>
313+
icon={ <DisconnectIcon width="24" /> }
314+
label={ __( 'Disconnect', 'google-site-kit' ) }
315+
itemClassName="googlesitekit-user-menu__item"
316+
iconClassName="googlesitekit-user-menu__item-icon"
317+
labelClassName="googlesitekit-user-menu__item-label"
318+
/>
328319
</Menu>
329320
</div>
330321
<Portal>

assets/js/components/ViewOnlyMenu/Description.js

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,11 @@
1919
/**
2020
* WordPress dependencies
2121
*/
22-
import { createInterpolateElement, useCallback } from '@wordpress/element';
22+
import {
23+
createInterpolateElement,
24+
Fragment,
25+
useCallback,
26+
} from '@wordpress/element';
2327
import { __, _x } from '@wordpress/i18n';
2428

2529
/**
@@ -116,7 +120,7 @@ export default function Description() {
116120
);
117121

118122
return (
119-
<li className="googlesitekit-view-only-menu__list-item googlesitekit-view-only-menu__description">
123+
<Fragment>
120124
<P>{ description }</P>
121125
{ canAuthenticate && (
122126
<Button onClick={ onButtonClick }>
@@ -127,6 +131,6 @@ export default function Description() {
127131
) }
128132
</Button>
129133
) }
130-
</li>
134+
</Fragment>
131135
);
132136
}

0 commit comments

Comments
 (0)