diff --git a/docs/pages/components/menu-item.md b/docs/pages/components/menu-item.md
index 6125d8ec7..633415003 100644
--- a/docs/pages/components/menu-item.md
+++ b/docs/pages/components/menu-item.md
@@ -60,35 +60,6 @@ const App = () => (
## Examples
-### Disabled
-
-Add the `disabled` attribute to disable the menu item so it cannot be selected.
-
-```html:preview
-
- Option 1
- Option 2
- Option 3
-
-```
-
-{% raw %}
-
-```jsx:react
-import WaMenu from '@shoelace-style/shoelace/dist/react/menu';
-import WaMenuItem from '@shoelace-style/shoelace/dist/react/menu-item';
-
-const App = () => (
-
- Option 1
- Option 2
- Option 3
-
-);
-```
-
-{% endraw %}
-
### Prefix & Suffix
Add content to the start and end of menu items using the `prefix` and `suffix` slots.
@@ -151,6 +122,64 @@ const App = () => (
{% endraw %}
+### Disabled
+
+Add the `disabled` attribute to disable the menu item so it cannot be selected.
+
+```html:preview
+
+ Option 1
+ Option 2
+ Option 3
+
+```
+
+{% raw %}
+
+```jsx:react
+import WaMenu from '@shoelace-style/shoelace/dist/react/menu';
+import WaMenuItem from '@shoelace-style/shoelace/dist/react/menu-item';
+
+const App = () => (
+
+ Option 1
+ Option 2
+ Option 3
+
+);
+```
+
+{% endraw %}
+
+### Loading
+
+Use the `loading` attribute to indicate that a menu item is busy. Like a disabled menu item, clicks will be suppressed until the loading state is removed.
+
+```html:preview
+
+ Option 1
+ Option 2
+ Option 3
+
+```
+
+{% raw %}
+
+```jsx:react
+import WaMenu from '@shoelace-style/shoelace/dist/react/menu';
+import WaMenuItem from '@shoelace-style/shoelace/dist/react/menu-item';
+
+const App = () => (
+
+ Option 1
+ Option 2
+ Option 3
+
+);
+```
+
+{% endraw %}
+
### Checkbox Menu Items
Set the `type` attribute to `checkbox` to create a menu item that will toggle on and off when selected. You can use the `checked` attribute to set the initial state.
diff --git a/docs/pages/resources/changelog.md b/docs/pages/resources/changelog.md
index cc5c39993..baff1dd55 100644
--- a/docs/pages/resources/changelog.md
+++ b/docs/pages/resources/changelog.md
@@ -26,6 +26,7 @@ New versions of Web Awesome are released as-needed and generally occur when a cr
## Next
+- Added the `loading` attribute and the `spinner` and `spinner__base` part to `` [#1700]
- Added the `hover-bridge` feature to `` to support better tooltip accessibility [#1734]
- Added the `loading` attribute and the `spinner` and `spinner__base` part to `` [#1700]
- Fixed files that did not have `.js` extensions. [#1770]
diff --git a/src/components/menu-item/menu-item.component.ts b/src/components/menu-item/menu-item.component.ts
index d1be3027b..f6c49d04c 100644
--- a/src/components/menu-item/menu-item.component.ts
+++ b/src/components/menu-item/menu-item.component.ts
@@ -8,6 +8,7 @@ import { watch } from '../../internal/watch.js';
import styles from './menu-item.styles.js';
import WaIcon from '../icon/icon.component.js';
import WaPopup from '../popup/popup.component.js';
+import WaSpinner from '../spinner/spinner.component.js';
import WebAwesomeElement from '../../internal/webawesome-element.js';
import type { CSSResultGroup } from 'lit';
@@ -30,6 +31,8 @@ import type { CSSResultGroup } from 'lit';
* @csspart prefix - The prefix container.
* @csspart label - The menu item label.
* @csspart suffix - The suffix container.
+ * @csspart spinner - The spinner that shows when the menu item is in the loading state.
+ * @csspart spinner__base - The spinner's base part.
* @csspart submenu-icon - The submenu icon, visible only when the menu item has a submenu (not yet implemented).
*
* @cssproperty [--submenu-offset=-2px] - The distance submenus shift to overlap the parent menu.
@@ -38,7 +41,8 @@ export default class WaMenuItem extends WebAwesomeElement {
static styles: CSSResultGroup = styles;
static dependencies = {
'wa-icon': WaIcon,
- 'wa-popup': WaPopup
+ 'wa-popup': WaPopup,
+ 'wa-spinner': WaSpinner
};
private cachedTextLabel: string;
@@ -55,6 +59,9 @@ export default class WaMenuItem extends WebAwesomeElement {
/** A unique value to store in the menu item. This can be used as a way to identify menu items when selected. */
@property() value = '';
+ /** Draws the menu item in a loading state. */
+ @property({ type: Boolean, reflect: true }) loading = false;
+
/** Draws the menu item in a disabled state, preventing selection. */
@property({ type: Boolean, reflect: true }) disabled = false;
@@ -158,6 +165,7 @@ export default class WaMenuItem extends WebAwesomeElement {
'menu-item--rtl': isRtl,
'menu-item--checked': this.checked,
'menu-item--disabled': this.disabled,
+ 'menu-item--loading': this.loading,
'menu-item--has-submenu': this.isSubmenu(),
'menu-item--submenu-expanded': isSubmenuExpanded
})}
@@ -183,7 +191,7 @@ export default class WaMenuItem extends WebAwesomeElement {
>
- ${this.submenuController.renderSubmenu()}
+ ${this.submenuController.renderSubmenu()} ${this.loading ? html`` : ''}
`;
}
diff --git a/src/components/menu-item/menu-item.styles.ts b/src/components/menu-item/menu-item.styles.ts
index 278e7a5ec..06c6ffed1 100644
--- a/src/components/menu-item/menu-item.styles.ts
+++ b/src/components/menu-item/menu-item.styles.ts
@@ -34,6 +34,25 @@ export default css`
cursor: not-allowed;
}
+ .menu-item.menu-item--loading {
+ outline: none;
+ cursor: wait;
+ }
+
+ .menu-item.menu-item--loading *:not(wa-spinner) {
+ opacity: 0.5;
+ }
+
+ .menu-item--loading wa-spinner {
+ --indicator-color: currentColor;
+ --track-width: 1px;
+ position: absolute;
+ font-size: 0.75em;
+ top: calc(50% - 0.5em);
+ left: 0.65rem;
+ opacity: 1;
+ }
+
.menu-item .menu-item__label {
flex: 1 1 auto;
display: inline-block;
diff --git a/src/components/menu-item/menu-item.test.ts b/src/components/menu-item/menu-item.test.ts
index e6477ea3c..a1b578b15 100644
--- a/src/components/menu-item/menu-item.test.ts
+++ b/src/components/menu-item/menu-item.test.ts
@@ -40,6 +40,7 @@ describe('', () => {
expect(el.value).to.equal('');
expect(el.disabled).to.be.false;
+ expect(el.loading).to.equal(false);
expect(el.getAttribute('aria-disabled')).to.equal('false');
});
@@ -48,6 +49,13 @@ describe('', () => {
expect(el.getAttribute('aria-disabled')).to.equal('true');
});
+ describe('when loading', () => {
+ it('should have a spinner present', async () => {
+ const el = await fixture(html` Menu Item Label `);
+ expect(el.shadowRoot!.querySelector('wa-spinner')).to.exist;
+ });
+ });
+
it('should return a text label when calling getTextLabel()', async () => {
const el = await fixture(html` Test `);
expect(el.getTextLabel()).to.equal('Test');