Refactor Wagtail userbar as a web component (#9816)

* Add a border around the userbar menu in Windows high-contrast mode so it can be identified
* Make sure browser font resizing applies to the userbar
* Switch userbar to initialise a Web Component to avoid styling clashes
* Refactor userbar stylesheets to use the same CSS loading as the rest of the admin
pull/9822/head
Albina 2022-12-27 17:55:19 +01:00 zatwierdzone przez GitHub
rodzic 5eb2064574
commit 5cf621660c
Nie znaleziono w bazie danych klucza dla tego podpisu
ID klucza GPG: 4AEE18F83AFDEB23
15 zmienionych plików z 320 dodań i 290 usunięć

Wyświetl plik

@ -51,6 +51,8 @@ Changelog
* Fix: Fix horizontal positioning of rich text inline toolbar (Thibaud Colas)
* Fix: Ensure that `DecimalBlock` correctly handles `None`, when `required=False`, values (Natarajan Balaji)
* Fix: Close the userbar when clicking its toggle (Albina Starykova)
* Fix: Add a border around the userbar menu in Windows high-contrast mode so it can be identified (Albina Starykova)
* Fix: Make sure browser font resizing applies to the userbar (Albina Starykova)
* Docs: Add custom permissions section to permissions documentation page (Dan Hayden)
* Docs: Add documentation for how to get started with contributing translations for the Wagtail admin (Ogunbanjo Oluwadamilare)
* Docs: Officially recommend `fnm` over `nvm` in development documentation (LB (Ben) Johnston)
@ -89,6 +91,8 @@ Changelog
* Maintenance: Update `tsconfig` to better support modern TypeScript development and clean up some code quality issues via Eslint (Loveth Omokaro)
* Maintenance: Set up Stimulus application initialisation according to RFC 78 (LB (Ben) Johnston)
* Maintenance: Refactor submit-on-change search filters for image and document listings to use Stimulus (LB (Ben) Johnston)
* Maintenance: Switch userbar to initialise a Web Component to avoid styling clashes (Albina Starykova)
* Maintenance: Refactor userbar stylesheets to use the same CSS loading as the rest of the admin (Albina Starykova)
4.1.2 (xx.xx.xxxx) - IN DEVELOPMENT

Wyświetl plik

@ -5,154 +5,166 @@
// This component implements a roving tab index for keyboard navigation
// Learn more about roving tabIndex: https://w3c.github.io/aria-practices/#kbd_roving_tabindex
document.addEventListener('DOMContentLoaded', () => {
const userbar = document.querySelector('[data-wagtail-userbar]');
const trigger = userbar.querySelector('[data-wagtail-userbar-trigger]');
const list = userbar.querySelector('[role=menu]');
const listItems = list.querySelectorAll('li');
const isActiveClass = 'is-active';
class Userbar extends HTMLElement {
connectedCallback() {
const template = document.getElementById('wagtail-userbar-template');
const shadowRoot = this.attachShadow({
mode: 'open',
});
shadowRoot.appendChild(template.content.cloneNode(true));
// Removes the template from html after it's being used
template.remove();
// querySelector for all items that can be focused
// tabIndex has been removed for roving tabindex compatibility
// source: https://stackoverflow.com/questions/1599660/which-html-elements-can-receive-focus
const focusableItemSelector = `a[href],
const userbar = shadowRoot.querySelector('[data-wagtail-userbar]');
const trigger = userbar.querySelector('[data-wagtail-userbar-trigger]');
const list = userbar.querySelector('[role=menu]');
const listItems = list.querySelectorAll('li');
const isActiveClass = 'is-active';
// Avoid Web Component FOUC while stylesheets are loading.
userbar.style.display = 'none';
// querySelector for all items that can be focused
// tabIndex has been removed for roving tabindex compatibility
// source: https://stackoverflow.com/questions/1599660/which-html-elements-can-receive-focus
const focusableItemSelector = `a[href],
button:not([disabled]),
input:not([disabled])`;
// eslint-disable-next-line @typescript-eslint/no-use-before-define
trigger.addEventListener('click', toggleUserbar, false);
// make sure userbar is hidden when navigating back
// eslint-disable-next-line @typescript-eslint/no-use-before-define
window.addEventListener('pageshow', hideUserbar, false);
// Handle keyboard events on the trigger
// eslint-disable-next-line @typescript-eslint/no-use-before-define
userbar.addEventListener('keydown', handleTriggerKeyDown);
// eslint-disable-next-line @typescript-eslint/no-use-before-define
list.addEventListener('focusout', handleFocusChange);
// eslint-disable-next-line @typescript-eslint/no-use-before-define
resetItemsTabIndex(); // On initialisation, all menu items should be disabled for roving tab index
function showUserbar(shouldFocus) {
userbar.classList.add(isActiveClass);
trigger.setAttribute('aria-expanded', 'true');
// eslint-disable-next-line @typescript-eslint/no-use-before-define
list.addEventListener('click', sandboxClick, false);
// eslint-disable-next-line @typescript-eslint/no-use-before-define
window.addEventListener('click', clickOutside, false);
trigger.addEventListener('click', toggleUserbar, false);
// Start handling keyboard input now that the userbar is open.
// make sure userbar is hidden when navigating back
// eslint-disable-next-line @typescript-eslint/no-use-before-define
userbar.addEventListener('keydown', handleUserbarItemsKeyDown, false);
window.addEventListener('pageshow', hideUserbar, false);
// The userbar has role=menu which means that the first link should be focused on popup
// For weird reasons shifting focus only works after some amount of delay
// Which is why we are forced to use setTimeout
if (shouldFocus) {
// Find the first focusable element (if any) and focus it
if (list.querySelector(focusableItemSelector)) {
setTimeout(() => {
// eslint-disable-next-line @typescript-eslint/no-use-before-define
setFocusToFirstItem();
}, 300); // Less than 300ms doesn't seem to work
// Handle keyboard events on the trigger
// eslint-disable-next-line @typescript-eslint/no-use-before-define
userbar.addEventListener('keydown', handleTriggerKeyDown);
// eslint-disable-next-line @typescript-eslint/no-use-before-define
list.addEventListener('focusout', handleFocusChange);
// eslint-disable-next-line @typescript-eslint/no-use-before-define
resetItemsTabIndex(); // On initialisation, all menu items should be disabled for roving tab index
function showUserbar(shouldFocus) {
userbar.classList.add(isActiveClass);
trigger.setAttribute('aria-expanded', 'true');
// eslint-disable-next-line @typescript-eslint/no-use-before-define
list.addEventListener('click', sandboxClick, false);
// eslint-disable-next-line @typescript-eslint/no-use-before-define
window.addEventListener('click', clickOutside, false);
// Start handling keyboard input now that the userbar is open.
// eslint-disable-next-line @typescript-eslint/no-use-before-define
userbar.addEventListener('keydown', handleUserbarItemsKeyDown, false);
// The userbar has role=menu which means that the first link should be focused on popup
// For weird reasons shifting focus only works after some amount of delay
// Which is why we are forced to use setTimeout
if (shouldFocus) {
// Find the first focusable element (if any) and focus it
if (list.querySelector(focusableItemSelector)) {
setTimeout(() => {
// eslint-disable-next-line @typescript-eslint/no-use-before-define
setFocusToFirstItem();
}, 300); // Less than 300ms doesn't seem to work
}
}
}
}
function hideUserbar() {
userbar.classList.remove(isActiveClass);
trigger.setAttribute('aria-expanded', 'false');
// eslint-disable-next-line @typescript-eslint/no-use-before-define
list.addEventListener('click', sandboxClick, false);
// eslint-disable-next-line @typescript-eslint/no-use-before-define
window.removeEventListener('click', clickOutside, false);
function hideUserbar() {
userbar.classList.remove(isActiveClass);
trigger.setAttribute('aria-expanded', 'false');
// eslint-disable-next-line @typescript-eslint/no-use-before-define
list.addEventListener('click', sandboxClick, false);
// eslint-disable-next-line @typescript-eslint/no-use-before-define
window.removeEventListener('click', clickOutside, false);
// Cease handling keyboard input now that the userbar is closed.
// eslint-disable-next-line @typescript-eslint/no-use-before-define
userbar.removeEventListener('keydown', handleUserbarItemsKeyDown, false);
}
function toggleUserbar(e2) {
e2.stopPropagation();
if (userbar.classList.contains(isActiveClass)) {
hideUserbar();
} else {
showUserbar(true);
// Cease handling keyboard input now that the userbar is closed.
// eslint-disable-next-line @typescript-eslint/no-use-before-define
userbar.removeEventListener('keydown', handleUserbarItemsKeyDown, false);
}
}
function isFocusOnItems() {
return (
document.activeElement &&
!!document.activeElement.closest('.wagtail-userbar-items')
);
}
function toggleUserbar(e2) {
e2.stopPropagation();
if (userbar.classList.contains(isActiveClass)) {
hideUserbar();
} else {
showUserbar(true);
}
}
/** Reset all focusable menu items to `tabIndex = -1` */
function resetItemsTabIndex() {
listItems.forEach((listItem) => {
function isFocusOnItems() {
return (
shadowRoot.activeElement &&
!!shadowRoot.activeElement.closest('.w-userbar-nav')
);
}
/** Reset all focusable menu items to `tabIndex = -1` */
function resetItemsTabIndex() {
listItems.forEach((listItem) => {
// eslint-disable-next-line no-param-reassign
listItem.firstElementChild.tabIndex = -1;
});
}
/** Focus element using a roving tab index */
function focusElement(el) {
resetItemsTabIndex();
// eslint-disable-next-line no-param-reassign
listItem.firstElementChild.tabIndex = -1;
});
}
/** Focus element using a roving tab index */
function focusElement(el) {
resetItemsTabIndex();
// eslint-disable-next-line no-param-reassign
el.tabIndex = 0;
setTimeout(() => {
el.focus();
}, 100); // Workaround, changing focus only works after a timeout
}
function setFocusToTrigger() {
setTimeout(() => trigger.focus(), 300);
resetItemsTabIndex();
}
function setFocusToFirstItem() {
if (listItems.length > 0) {
focusElement(listItems[0].firstElementChild);
el.tabIndex = 0;
setTimeout(() => {
el.focus();
}, 100); // Workaround, changing focus only works after a timeout
}
}
function setFocusToLastItem() {
if (listItems.length > 0) {
focusElement(listItems[listItems.length - 1].firstElementChild);
function setFocusToTrigger() {
setTimeout(() => trigger.focus(), 300);
resetItemsTabIndex();
}
}
function setFocusToNextItem() {
listItems.forEach((element, idx) => {
// Check which item is currently focused
if (element.firstElementChild === document.activeElement) {
if (idx + 1 < listItems.length) {
focusElement(listItems[idx + 1].firstElementChild);
} else {
// Loop around
setFocusToFirstItem();
}
function setFocusToFirstItem() {
if (listItems.length > 0) {
focusElement(listItems[0].firstElementChild);
}
});
}
}
function setFocusToPreviousItem() {
listItems.forEach((element, idx) => {
// Check which item is currently focused
if (element.firstElementChild === document.activeElement) {
if (idx > 0) {
focusElement(listItems[idx - 1].firstElementChild);
} else {
setFocusToLastItem();
}
function setFocusToLastItem() {
if (listItems.length > 0) {
focusElement(listItems[listItems.length - 1].firstElementChild);
}
});
}
}
/**
function setFocusToNextItem() {
listItems.forEach((element, idx) => {
// Check which item is currently focused
if (element.firstElementChild === shadowRoot.activeElement) {
if (idx + 1 < listItems.length) {
focusElement(listItems[idx + 1].firstElementChild);
} else {
// Loop around
setFocusToFirstItem();
}
}
});
}
function setFocusToPreviousItem() {
listItems.forEach((element, idx) => {
// Check which item is currently focused
if (element.firstElementChild === shadowRoot.activeElement) {
if (idx > 0) {
focusElement(listItems[idx - 1].firstElementChild);
} else {
setFocusToLastItem();
}
}
});
}
/**
This handler is responsible for keyboard input when items inside the userbar are focused.
It should only listen when the userbar is open.
@ -160,94 +172,96 @@ document.addEventListener('DOMContentLoaded', () => {
- Shifting focus using the arrow / home / end keys.
- Closing the menu when 'Escape' is pressed.
*/
function handleUserbarItemsKeyDown(event) {
// Only handle keyboard input if the userbar is open
if (trigger.getAttribute('aria-expanded') === 'true') {
if (event.key === 'Escape') {
hideUserbar();
setFocusToTrigger();
return false;
}
function handleUserbarItemsKeyDown(event) {
// Only handle keyboard input if the userbar is open
if (trigger.getAttribute('aria-expanded') === 'true') {
if (event.key === 'Escape') {
hideUserbar();
setFocusToTrigger();
return false;
}
// List items are in focus, move focus if needed
if (isFocusOnItems()) {
// List items are in focus, move focus if needed
if (isFocusOnItems()) {
switch (event.key) {
case 'ArrowDown':
event.preventDefault();
setFocusToNextItem();
return false;
case 'ArrowUp':
event.preventDefault();
setFocusToPreviousItem();
return false;
case 'Home':
event.preventDefault();
setFocusToFirstItem();
return false;
case 'End':
event.preventDefault();
setFocusToLastItem();
return false;
default:
break;
}
}
}
return true;
}
function handleFocusChange(event) {
// Is the focus is still in the menu? If so, don't to anything
if (
event.relatedTarget == null ||
(event.relatedTarget && event.relatedTarget.closest('.w-userbar-nav'))
) {
return;
}
// List items not in focus - the menu should close
resetItemsTabIndex();
hideUserbar();
}
/**
This handler is responsible for opening the userbar with the arrow keys
if it's focused and not open yet. It should always be listening.
*/
function handleTriggerKeyDown(event) {
// Check if the userbar is focused (but not open yet) and should be opened by keyboard input
if (
trigger === document.activeElement &&
trigger.getAttribute('aria-expanded') === 'false'
) {
switch (event.key) {
case 'ArrowDown':
event.preventDefault();
setFocusToNextItem();
return false;
case 'ArrowUp':
event.preventDefault();
setFocusToPreviousItem();
return false;
case 'Home':
showUserbar(false);
// Workaround for focus bug
// Needs extra delay to account for the userbar open animation. Otherwise won't focus properly.
setTimeout(() => setFocusToLastItem(), 300);
break;
case 'ArrowDown':
event.preventDefault();
setFocusToFirstItem();
return false;
case 'End':
event.preventDefault();
setFocusToLastItem();
return false;
showUserbar(false);
// Workaround for focus bug
// Needs extra delay to account for the userbar open animation. Otherwise won't focus properly.
setTimeout(() => setFocusToFirstItem(), 300);
break;
default:
break;
}
}
}
return true;
}
function handleFocusChange(event) {
// Is the focus is still in the menu? If so, don't to anything
if (
event.relatedTarget == null ||
(event.relatedTarget &&
event.relatedTarget.closest('.wagtail-userbar-nav'))
) {
return;
function sandboxClick(e2) {
e2.stopPropagation();
}
// List items not in focus - the menu should close
resetItemsTabIndex();
hideUserbar();
}
/**
This handler is responsible for opening the userbar with the arrow keys
if it's focused and not open yet. It should always be listening.
*/
function handleTriggerKeyDown(event) {
// Check if the userbar is focused (but not open yet) and should be opened by keyboard input
if (
trigger === document.activeElement &&
trigger.getAttribute('aria-expanded') === 'false'
) {
switch (event.key) {
case 'ArrowUp':
event.preventDefault();
showUserbar(false);
// Workaround for focus bug
// Needs extra delay to account for the userbar open animation. Otherwise won't focus properly.
setTimeout(() => setFocusToLastItem(), 300);
break;
case 'ArrowDown':
event.preventDefault();
showUserbar(false);
// Workaround for focus bug
// Needs extra delay to account for the userbar open animation. Otherwise won't focus properly.
setTimeout(() => setFocusToFirstItem(), 300);
break;
default:
break;
}
function clickOutside() {
hideUserbar();
}
}
}
function sandboxClick(e2) {
e2.stopPropagation();
}
function clickOutside() {
hideUserbar();
}
});
customElements.define('wagtail-userbar', Userbar);

Wyświetl plik

@ -146,7 +146,8 @@ module.exports = {
*/
plugin(({ addBase }) => {
addBase({
':root': {
/** Support for web components */
':root, :host': {
'--w-font-sans': fontFamily.sans.join(', '),
'--w-font-mono': fontFamily.mono.join(', '),
...generateColorVariables(colors),

Wyświetl plik

@ -70,6 +70,8 @@ This feature was developed by Sage Abdullah.
* Fix horizontal positioning of rich text inline toolbar (Thibaud Colas)
* Ensure that `DecimalBlock` correctly handles `None`, when `required=False`, values (Natarajan Balaji)
* Close the userbar when clicking its toggle (Albina Starykova)
* Add a border around the userbar menu in Windows high-contrast mode so it can be identified (Albina Starykova)
* Make sure browser font resizing applies to the userbar (Albina Starykova)
### Documentation
@ -113,6 +115,8 @@ This feature was developed by Sage Abdullah.
* Move `identity` JavaScript util into shared utils folder (LB (Ben Johnston))
* Remove unnecessary declaration of function to determine URL query params, instead use `URLSearchParams` (Loveth Omokaro)
* Update `tsconfig` to better support modern TypeScript development and clean up some code quality issues via Eslint (Loveth Omokaro)
* Switch userbar to initialise a Web Component to avoid styling clashes (Albina Starykova)
* Refactor userbar stylesheets to use the same CSS loading as the rest of the admin (Albina Starykova)
## Upgrade considerations
@ -152,3 +156,15 @@ Python code that uses the `InlinePanel` panel type is not affected by this chang
### `WAGTAILADMIN_GLOBAL_PAGE_EDIT_LOCK` setting is now `WAGTAILADMIN_GLOBAL_EDIT_LOCK`
The `WAGTAILADMIN_GLOBAL_PAGE_EDIT_LOCK` setting has been renamed to [`WAGTAILADMIN_GLOBAL_EDIT_LOCK`](wagtailadmin_global_edit_lock).
### Wagtail userbar as a web component
The [`wagtailuserbar`](wagtailuserbar_tag) template tag now initialises the userbar as a [Web Component](https://developer.mozilla.org/en-US/docs/Web/Web_Components), with a `wagtail-userbar` custom element using shadow DOM to apply styles without any collisions with the host page.
For any site customising the position of the userbar, target the styles to `wagtail-userbar::part(userbar)` instead of `.wagtail-userbar`. For example:
```css
wagtail-userbar::part(userbar) {
bottom: 30px;
}
```

Wyświetl plik

@ -223,7 +223,7 @@ This tag provides a contextual flyout menu for logged-in users. The menu gives e
This tag may be used on standard Django views, without page object. The user bar will contain one item pointing to the admin.
We recommend putting the tag near the top of the `<body>` element to allow keyboard users to reach it. You should consider putting the tag after any `[skip links](https://webaim.org/techniques/skipnav/)` but before the navigation and main content of your page.
We recommend putting the tag near the top of the `<body>` element to allow keyboard users to reach it. You should consider putting the tag after any [skip links](https://webaim.org/techniques/skipnav/)` but before the navigation and main content of your page.
```html+django
{% load wagtailuserbar %}
@ -254,11 +254,10 @@ By default, the User Bar appears in the bottom right of the browser window, inse
The userbar can be positioned where it works best with your design. Alternatively, you can position it with a CSS rule in your own CSS files, for example:
```css
.wagtail-userbar {
top: 200px !important;
left: 10px !important;
wagtail-userbar::part(userbar) {
bottom: 30px;
}
```
```
## Varying output between preview and live

Wyświetl plik

@ -2,6 +2,9 @@
@use 'sass:math';
@use 'sass:string';
@tailwind base;
@tailwind components;
@import '../../../../../client/scss/settings';
@import '../../../../../client/scss/tools';
@ -12,15 +15,10 @@
$size-home-button: 3.5em;
$position: 2em;
$width-arrow: 0.6em;
$box-shadow-props: 0 0 1px 0 rgba(107, 214, 230, 1);
$box-shadow-props: 0 0 1px 0 rgba(107, 214, 230, 1),
0 1px 10px 0 rgba(107, 214, 230, 0.7);
$max-items: 12;
$userbar-radius: 6px;
$color-black: #000;
$color-white: #fff;
$color-grey-1: #262626;
// Classnames will start with this parameter, eg .wagtail-
$namespace: 'wagtail';
// Possible positions for the userbar to exist in. These are set through the
// {% wagtailuserbar 'bottom-left' %} template tag.
@ -50,23 +48,16 @@ $positions: (
// =============================================================================
// Wagtail userbar proper
// =============================================================================
.#{$namespace}-userbar-reset {
all: initial;
// Copy our font sans variable so it can be used without Tailwind.
--w-font-sans: -apple-system, BlinkMacSystemFont, 'Segoe UI', system-ui,
Roboto, 'Helvetica Neue', Arial, sans-serif, Apple Color Emoji,
'Segoe UI Emoji', 'Segoe UI Symbol', 'Noto Color Emoji';
}
.#{$namespace}-userbar {
.w-userbar {
position: fixed;
z-index: 9999;
// stylelint-disable-next-line declaration-no-important
font-size: initial !important;
font-size: initial;
line-height: initial;
margin: 0;
padding: 0;
display: block;
// Stop hiding the userbar once stylesheets are loaded.
// stylelint-disable-next-line declaration-no-important
display: block !important;
border: 0;
width: auto;
height: auto;
@ -77,34 +68,32 @@ $positions: (
}
@media print {
.#{$namespace}-userbar {
.w-userbar {
display: none;
}
}
// stylelint-disable declaration-no-important
.#{$namespace}-userbar-trigger {
all: initial;
.w-userbar-trigger {
display: flex;
align-items: center;
justify-content: center;
width: $size-home-button;
height: $size-home-button;
margin: 0 !important;
margin: 0;
overflow: hidden;
background-color: $color-white;
border: 2px solid transparent;
border-radius: 50%;
color: $color-black;
padding: 0 !important;
padding: 0;
cursor: pointer;
box-shadow: $box-shadow-props, 0 1px 10px 0 rgba(107, 214, 230, 0.7);
box-shadow: $box-shadow-props;
transition: all 0.2s ease-in-out;
font-size: 16px;
text-decoration: none !important;
font-size: 1rem;
text-decoration: none;
position: relative;
.#{$namespace}-userbar-help-text {
.w-userbar-help-text {
// Visually hide the help text
clip: rect(0 0 0 0);
clip-path: inset(50%);
@ -115,9 +104,9 @@ $positions: (
width: 1px;
}
.#{$namespace}-icon:before {
.w-icon:before {
transition: color 0.2s ease;
font-size: 32px;
font-size: 2rem;
width: auto;
margin: 0;
}
@ -127,8 +116,7 @@ $positions: (
}
}
.#{$namespace}-userbar-items {
all: revert;
.w-userbar-items {
display: block;
list-style: none;
position: absolute;
@ -136,17 +124,17 @@ $positions: (
min-width: 210px;
visibility: hidden;
font-family: $font-sans;
font-size: 14px;
font-size: 0.875rem;
padding-inline-start: 0;
text-decoration: none;
.#{$namespace}-userbar.is-active & {
.w-userbar.is-active & {
visibility: visible;
}
}
// Arrow
.#{$namespace}-userbar-items:after {
.w-userbar-items:after {
content: '';
position: absolute;
width: 0;
@ -157,29 +145,28 @@ $positions: (
transition-timing-function: cubic-bezier(0.55, 0, 0.1, 1);
@media (prefers-reduced-motion: reduce) {
transition: none !important;
transition: none;
}
.#{$namespace}-userbar.is-active & {
.w-userbar.is-active & {
opacity: 1;
transform: translateY(0);
transition-delay: 0.3s;
}
}
.#{$namespace}-userbar-nav {
background: transparent !important;
.w-userbar-nav {
background: transparent;
padding: 0;
margin: 0 !important;
display: block !important;
margin: 0;
display: block;
.#{$namespace}-action {
.w-action {
background: transparent;
}
}
.#{$namespace}-userbar__item {
all: revert;
.w-userbar__item {
margin: 0;
background-color: $color-grey-1;
opacity: 0;
@ -187,14 +174,14 @@ $positions: (
transition-duration: 0.125s;
transition-timing-function: cubic-bezier(0.55, 0, 0.1, 1);
font-family: $font-sans;
font-size: 16px !important;
text-decoration: none !important;
font-size: 1rem;
text-decoration: none;
@media (prefers-reduced-motion: reduce) {
transition: none !important;
transition: none;
// Force disable transitions for all items
transition-delay: 0s !important;
transition-delay: 0s;
}
&:first-child {
@ -224,14 +211,14 @@ $positions: (
}
a,
.#{$namespace}-action {
.w-action {
color: $color-white;
display: block;
text-decoration: none !important;
transform: none !important;
transition: none !important;
margin: 0 !important;
font-size: 14px !important;
text-decoration: none;
transform: none;
transition: none;
margin: 0;
font-size: 0.875rem;
&:hover,
&:focus {
@ -250,7 +237,7 @@ $positions: (
}
}
.#{$namespace}-icon {
.w-icon {
position: relative;
&:before {
@ -263,7 +250,7 @@ $positions: (
a,
button {
font-size: 14px !important;
font-size: 0.875rem;
text-align: start;
padding: 0.8em;
}
@ -279,9 +266,17 @@ $positions: (
//Media for Windows High Contrast
@media (forced-colors: $media-forced-colours) {
.#{$namespace}-userbar-icon {
.w-userbar-icon {
fill: $system-color-link-text;
}
.w-userbar__item {
border: 1px solid $system-color-button-text;
}
.w-userbar-items::after {
border: $width-arrow solid Canvas;
}
}
// =============================================================================
@ -293,17 +288,17 @@ $positions: (
$horizontal: map.get($attrs, horizontal);
$arrow: map.get($attrs, arrow);
.#{$namespace}-userbar--#{$pos} {
.w-userbar--#{$pos} {
#{$vertical}: $position;
#{$horizontal}: $position;
.#{$namespace}-userbar-items {
.w-userbar-items {
#{$vertical}: 100%;
#{$horizontal}: 0;
padding-#{$vertical}: $width-arrow * 2;
}
.#{$namespace}-userbar-nav .#{$namespace}-userbar__item {
.w-userbar-nav .w-userbar__item {
@if $vertical == 'bottom' {
transform: translateY(1em);
} @else {
@ -311,21 +306,21 @@ $positions: (
}
}
.#{$namespace}-userbar-items:after {
.w-userbar-items:after {
#{$vertical}: 2px;
#{$horizontal}: math.div($size-home-button, 2) -
math.div($width-arrow, 2);
border-#{$arrow}-color: $color-grey-1;
@if $vertical == 'bottom' {
transform: translateY(-$width-arrow);
transform: translateY($width-arrow);
}
@if $vertical == 'top' {
transform: translateY($width-arrow);
}
}
&.is-active .#{$namespace}-userbar__item {
&.is-active .w-userbar__item {
@for $i from 1 through $max-items {
@if $vertical == 'bottom' {
&:nth-last-child(#{$i}) {
@ -348,7 +343,7 @@ $positions: (
// =============================================================================
// Active state for the list items comes last.
.#{$namespace}-userbar.is-active .#{$namespace}-userbar__item {
.w-userbar.is-active .w-userbar__item {
transform: translateY(0);
opacity: 1;
}

Wyświetl plik

@ -1,10 +1,10 @@
{% load wagtailadmin_tags i18n %}
<!-- Wagtail user bar embed code -->
<div class="wagtail-userbar-reset">
<div class="wagtail-userbar wagtail-userbar--{{ position|default:'bottom-right' }}" data-wagtail-userbar>
<template id="wagtail-userbar-template">
<div class="w-userbar w-userbar--{{ position|default:'bottom-right' }}" data-wagtail-userbar part="userbar">
<link rel="stylesheet" href="{% versioned_static 'wagtailadmin/css/userbar.css' %}">
<div class="wagtail-userbar-nav">
<button aria-controls="wagtail-userbar-items" aria-haspopup="true" class="wagtail-userbar-trigger" id="wagtail-userbar-trigger" data-wagtail-userbar-trigger>
<div class="w-userbar-nav">
<button aria-controls="wagtail-userbar-items" aria-haspopup="true" class="w-userbar-trigger" id="wagtail-userbar-trigger" data-wagtail-userbar-trigger>
{% block branding_logo %}
<div style="display: none">
<svg>
@ -19,19 +19,20 @@
</svg>
</div>
{% comment %} Intentionally not using the icon template tag to show as SVG only {% endcomment %}
<svg class="wagtail-userbar-icon" aria-hidden="true">
<svg class="w-userbar-icon" aria-hidden="true">
<use href="#icon-wagtail-icon"></use>
</svg>
{% endblock %}
<span class="wagtail-userbar-help-text">{% trans 'View Wagtail quick actions' %}</span>
<span class="w-userbar-help-text">{% trans 'View Wagtail quick actions' %}</span>
</button>
<ul aria-labelledby="wagtail-userbar-trigger" class="wagtail-userbar-items" id="wagtail-userbar-items" role="menu">
<ul aria-labelledby="wagtail-userbar-trigger" class="w-userbar-items" id="wagtail-userbar-items" role="menu">
{% for item in items %}
{{ item|safe }}
{% endfor %}
</ul>
</div>
<script src="{% versioned_static 'wagtailadmin/js/userbar.js' %}"></script>
</div>
</div>
</template>
<wagtail-userbar></wagtail-userbar>
<script src="{% versioned_static 'wagtailadmin/js/userbar.js' %}"></script>
<!-- end Wagtail user bar embed code -->

Wyświetl plik

@ -2,8 +2,8 @@
{% load i18n wagtailadmin_tags %}
{% block item_content %}
<a href="{% url 'wagtailadmin_home' %}" target="_parent" class="wagtail-userbar-link" role="menuitem">
{% icon name="wagtail-icon" class_name="wagtail-action-icon" %}
<a href="{% url 'wagtailadmin_home' %}" target="_parent" role="menuitem">
{% icon name="wagtail-icon" class_name="w-action-icon" %}
{% trans 'Go to Wagtail admin' %}
</a>
{% endblock %}

Wyświetl plik

@ -1,3 +1,3 @@
<li class="wagtail-userbar__item {% block item_classes %}{% endblock %}" role="presentation">
<li class="w-userbar__item {% block item_classes %}{% endblock %}" role="presentation">
{% block item_content %}{% endblock %}
</li>

Wyświetl plik

@ -3,7 +3,7 @@
{% block item_content %}
<a href="{% url 'wagtailadmin_pages:add_subpage' self.page.id %}" target="_parent" role="menuitem">
{% icon name="plus" class_name="wagtail-action-icon" %}
{% icon name="plus" class_name="w-action-icon" %}
{% trans 'Add a child page' %}
</a>
{% endblock %}

Wyświetl plik

@ -5,7 +5,7 @@
<form action="{% url 'wagtailadmin_pages:approve_moderation' self.revision.id %}" target="_parent" method="post">
{% csrf_token %}
<button type="submit" value="{% trans 'Approve' %}" class="button" role="menuitem">
{% icon name="tick" class_name="wagtail-action-icon" %}
{% icon name="tick" class_name="w-action-icon" %}
{% trans 'Approve' %}
</button>
</form>

Wyświetl plik

@ -3,7 +3,7 @@
{% block item_content %}
<a href="{% url 'wagtailadmin_pages:edit' self.page.id %}" target="_parent" role="menuitem">
{% icon name="edit" class_name="wagtail-action-icon" %}
{% icon name="edit" class_name="w-action-icon" %}
{% trans 'Edit this page' %}
</a>
{% endblock %}

Wyświetl plik

@ -3,7 +3,7 @@
{% block item_content %}
<a href="{% url 'wagtailadmin_explore' self.parent_page.id %}" target="_parent" role="menuitem">
{% icon name="folder-open-inverse" class_name="wagtail-action-icon" %}
{% icon name="folder-open-inverse" class_name="w-action-icon" %}
{% trans 'Show in Explorer' %}
</a>
{% endblock %}

Wyświetl plik

@ -5,7 +5,7 @@
<form action="{% url 'wagtailadmin_pages:reject_moderation' self.revision.id %}" target="_parent" method="post">
{% csrf_token %}
<button type="submit" value="{% trans 'Reject' %}" class="button" role="menuitem">
{% icon name="cross" class_name="wagtail-action-icon" %}
{% icon name="cross" class_name="w-action-icon" %}
{% trans 'Reject' %}
</button>
</form>

Wyświetl plik

@ -197,7 +197,7 @@ class TestUserbarAddLink(TestCase, WagtailTestUtils):
)
needle = f"""
<a href="{expected_url}" target="_parent" role="menuitem">
<svg class="icon icon-plus wagtail-action-icon" aria-hidden="true">
<svg class="icon icon-plus w-action-icon" aria-hidden="true">
<use href="#icon-plus"></use>
</svg>
Add a child page
@ -238,7 +238,7 @@ class TestUserbarModeration(TestCase, WagtailTestUtils):
expected_approve_html = """
<form action="/admin/pages/moderation/{}/approve/" target="_parent" method="post">
<input type="hidden" name="csrfmiddlewaretoken">
<div class="wagtail-action">
<div class="w-action">
<input type="submit" value="Approve" class="button" />
</div>
</form>
@ -250,7 +250,7 @@ class TestUserbarModeration(TestCase, WagtailTestUtils):
expected_reject_html = """
<form action="/admin/pages/moderation/{}/reject/" target="_parent" method="post">
<input type="hidden" name="csrfmiddlewaretoken">
<div class="wagtail-action">
<div class="w-action">
<input type="submit" value="Reject" class="button" />
</div>
</form>