浏览代码

Remove legacy sidebar, refactoring slim sidebar implementation

Thibaud Colas 3 年之前
父节点
当前提交
4dc5f3c595
共有 74 个文件被更改,包括 59 次插入4387 次删除
  1. 10 0
      client/scss/components/_grid.legacy.scss
  2. 2 1
      client/scss/components/_header.scss
  3. 0 50
      client/scss/components/_logo.scss
  4. 0 560
      client/scss/components/_main-nav.scss
  5. 3 4
      client/scss/core.scss
  6. 0 40
      client/scss/overrides/_utilities.hidden.scss
  7. 5 0
      client/scss/overrides/_utilities.legacy.scss
  8. 0 24
      client/scss/settings/_variables.scss
  9. 0 67
      client/scss/sidebar.scss
  10. 0 2
      client/src/components/Draftail/Draftail.scss
  11. 0 227
      client/src/components/Explorer/Explorer.scss
  12. 0 62
      client/src/components/Explorer/Explorer.test.js
  13. 0 50
      client/src/components/Explorer/Explorer.tsx
  14. 0 53
      client/src/components/Explorer/ExplorerHeader.test.js
  15. 0 97
      client/src/components/Explorer/ExplorerHeader.tsx
  16. 0 93
      client/src/components/Explorer/ExplorerItem.scss
  17. 0 54
      client/src/components/Explorer/ExplorerItem.test.js
  18. 0 83
      client/src/components/Explorer/ExplorerItem.tsx
  19. 0 228
      client/src/components/Explorer/ExplorerPanel.test.js
  20. 0 210
      client/src/components/Explorer/ExplorerPanel.tsx
  21. 0 39
      client/src/components/Explorer/ExplorerToggle.test.js
  22. 0 43
      client/src/components/Explorer/ExplorerToggle.tsx
  23. 0 29
      client/src/components/Explorer/PageCount.test.js
  24. 0 32
      client/src/components/Explorer/PageCount.tsx
  25. 0 117
      client/src/components/Explorer/__snapshots__/Explorer.test.js.snap
  26. 0 73
      client/src/components/Explorer/__snapshots__/ExplorerHeader.test.js.snap
  27. 0 180
      client/src/components/Explorer/__snapshots__/ExplorerItem.test.js.snap
  28. 0 349
      client/src/components/Explorer/__snapshots__/ExplorerPanel.test.js.snap
  29. 0 20
      client/src/components/Explorer/__snapshots__/ExplorerToggle.test.js.snap
  30. 0 31
      client/src/components/Explorer/__snapshots__/PageCount.test.js.snap
  31. 0 109
      client/src/components/Explorer/__snapshots__/actions.test.js.snap
  32. 0 87
      client/src/components/Explorer/actions.test.js
  33. 0 157
      client/src/components/Explorer/actions.ts
  34. 0 29
      client/src/components/Explorer/index.test.js
  35. 0 65
      client/src/components/Explorer/index.tsx
  36. 0 25
      client/src/components/Explorer/reducers/__snapshots__/explorer.test.js.snap
  37. 0 208
      client/src/components/Explorer/reducers/__snapshots__/nodes.test.js.snap
  38. 0 39
      client/src/components/Explorer/reducers/explorer.test.js
  39. 0 68
      client/src/components/Explorer/reducers/explorer.ts
  40. 0 9
      client/src/components/Explorer/reducers/index.ts
  41. 0 64
      client/src/components/Explorer/reducers/nodes.test.js
  42. 0 239
      client/src/components/Explorer/reducers/nodes.ts
  43. 1 1
      client/src/components/PageExplorer/PageExplorerPanel.test.js
  44. 8 0
      client/src/components/Sidebar/Sidebar.scss
  45. 2 9
      client/src/components/Sidebar/modules/MainMenu.scss
  46. 0 77
      client/src/entrypoints/admin/core.js
  47. 0 15
      client/src/entrypoints/admin/sidebar-legacy.js
  48. 0 27
      client/src/entrypoints/admin/sidebar-legacy.test.js
  49. 2 1
      client/src/entrypoints/admin/wagtailadmin.js
  50. 0 49
      client/src/includes/initSubmenus.js
  51. 8 17
      client/src/index.ts
  52. 0 1
      client/storybook/preview.js
  53. 0 9
      client/webpack.config.js
  54. 0 9
      docs/reference/settings.rst
  55. 0 10
      wagtail/admin/menu.py
  56. 6 1
      wagtail/admin/static_src/wagtailadmin/scss/layouts/404.scss
  57. 0 1
      wagtail/admin/static_src/wagtailadmin/scss/sidebar.scss
  58. 3 1
      wagtail/admin/templates/wagtailadmin/404.html
  59. 2 4
      wagtail/admin/templates/wagtailadmin/admin_base.html
  60. 2 37
      wagtail/admin/templates/wagtailadmin/base.html
  61. 0 5
      wagtail/admin/templates/wagtailadmin/shared/animated_logo.html
  62. 0 14
      wagtail/admin/templates/wagtailadmin/shared/animated_logo.stories.tsx
  63. 0 10
      wagtail/admin/templates/wagtailadmin/shared/explorer_menu_item.html
  64. 0 22
      wagtail/admin/templates/wagtailadmin/shared/main_nav.html
  65. 0 10
      wagtail/admin/templates/wagtailadmin/shared/menu_item.html
  66. 0 10
      wagtail/admin/templates/wagtailadmin/shared/menu_search.html
  67. 0 6
      wagtail/admin/templates/wagtailadmin/shared/menu_settings_menu_item.html
  68. 0 19
      wagtail/admin/templates/wagtailadmin/shared/menu_submenu_item.html
  69. 1 2
      wagtail/admin/templates/wagtailadmin/skeleton.html
  70. 0 61
      wagtail/admin/templatetags/wagtailadmin_tags.py
  71. 0 22
      wagtail/admin/tests/test_admin_search.py
  72. 1 9
      wagtail/admin/tests/test_menu.py
  73. 1 9
      wagtail/admin/wagtail_hooks.py
  74. 2 2
      wagtail/images/templates/wagtailimages/images/edit.html

+ 10 - 0
client/scss/components/_grid.legacy.scss

@@ -2,6 +2,16 @@
   @include clearfix();
   @include clearfix();
   height: 100vh;
   height: 100vh;
   transition: transform 0.2s ease;
   transition: transform 0.2s ease;
+
+  @include media-breakpoint-up(sm) {
+    @include transition(padding-inline-start $menu-transition-duration ease);
+    transform: none;
+    padding-inline-start: $menu-width;
+
+    .sidebar-collapsed & {
+      padding-inline-start: $menu-width-slim;
+    }
+  }
 }
 }
 
 
 .content-wrapper {
 .content-wrapper {

+ 2 - 1
client/scss/components/_header.scss

@@ -1,4 +1,5 @@
 @use 'sass:math';
 @use 'sass:math';
+@use 'sass:color';
 
 
 header {
 header {
   padding-top: 1em;
   padding-top: 1em;
@@ -49,7 +50,7 @@ header {
 
 
   .search-form .icon {
   .search-form .icon {
     @include svg-icon(1.3rem);
     @include svg-icon(1.3rem);
-    color: $nav-search-color;
+    color: color.adjust($color-white, $lightness: -20%);
     position: absolute;
     position: absolute;
     top: 0.3em;
     top: 0.3em;
     // Remove once we drop support for Safari 13.
     // Remove once we drop support for Safari 13.

+ 0 - 50
client/scss/components/_logo.scss

@@ -1,50 +0,0 @@
-@keyframes tail-wag {
-  from {
-    transform: rotate(-3deg);
-  }
-
-  to {
-    transform: rotate(7deg);
-  }
-}
-
-.logo {
-  display: flex;
-  align-items: center;
-  padding: 0.6em 1.2em;
-  color: #aaa;
-  -webkit-font-smoothing: auto;
-  position: relative;
-
-  &:hover {
-    color: $color-white;
-  }
-
-  @include media-breakpoint-up(sm) {
-    display: block;
-    margin: 2em auto;
-    text-align: center;
-  }
-}
-
-// Backwards-compatibility for branding_logo customisations in legacy sidebar.
-//RemovedInWagtail40Warning Remove when removing the legacy sidebar.
-.wagtail-logo-container__mobile {
-  margin-inline-end: 10px;
-  background-color: #555;
-  border-radius: 50%;
-  padding: 5px 7.5px;
-
-  .wagtail-logo {
-    width: 20px;
-    float: left;
-    border: 0;
-  }
-}
-
-// Media for Windows High Contrast mode
-@media (forced-colors: $media-forced-colours) {
-  .wagtail-logo-container__desktop {
-    background-color: $system-color-link-text;
-  }
-}

+ 0 - 560
client/scss/components/_main-nav.scss

@@ -1,560 +0,0 @@
-@use 'sass:map';
-
-.nav-wrapper {
-  position: relative;
-  margin-inline-start: -$menu-width;
-  width: $menu-width;
-  float: left;
-  display: flex;
-  flex-direction: column;
-  height: 100%;
-  background: $nav-grey-1;
-
-  .inner {
-    background: $nav-grey-1;
-    border-inline-end: 1px solid transparent; // ensure visible separation in Windows High Contrast mode
-
-    @include media-breakpoint-up(sm) {
-      // On medium, make it possible for the nav links to scroll.
-      display: flex;
-      flex-flow: column nowrap;
-    }
-  }
-}
-
-.nav-toggle.icon {
-  position: absolute;
-  padding-inline-start: $mobile-nice-padding;
-  cursor: pointer;
-  border: 1px solid transparent; // ensure visible separation in Windows High Contrast mode
-  background-color: transparent;
-
-  &:before {
-    position: relative;
-    top: 3px;
-    font-size: 40px;
-    color: $color-white;
-    line-height: 40px;
-    content: '\2261';
-  }
-}
-
-.nav-main {
-  ul,
-  li {
-    margin: 0;
-    padding: 0;
-    list-style-type: none;
-  }
-
-  li {
-    @include transition(border-color 0.2s ease);
-    position: relative;
-  }
-
-  a {
-    @include transition(border-color 0.2s ease);
-    -webkit-font-smoothing: auto;
-    text-decoration: none;
-    display: block;
-    color: $color-menu-text;
-    padding: 0.8em 1.7em;
-    font-size: 1em;
-    font-weight: normal;
-    // Note, font-weights lower than normal,
-    // and font-size smaller than 1em (80% ~= 12.8px),
-    // makes the strokes thinner than 1px on non-retina screens
-    // making the text semi-transparent
-    &:hover,
-    &:focus {
-      background-color: $nav-item-hover-bg;
-      color: $color-white;
-      text-shadow: -1px -1px 0 rgba(0, 0, 0, 0.3);
-    }
-  }
-
-  .menu-item a {
-    position: relative;
-    white-space: nowrap;
-    border-inline-start: 3px solid transparent;
-
-    &:before {
-      font-size: 1rem;
-      vertical-align: -15%;
-      margin-inline-end: 0.5em;
-    }
-
-    // only really used for spinners and settings menu
-    &:after {
-      font-size: 1.5em;
-      margin: 0;
-      position: absolute;
-      // Remove once we drop support for Safari 13.
-      // stylelint-disable-next-line property-disallowed-list
-      right: 0.5em;
-      inset-inline-end: 0.5em;
-      top: 0.5em;
-      margin-top: 0;
-    }
-  }
-
-  .menu-active {
-    background: $nav-item-active-bg;
-    text-shadow: -1px -1px 0 rgba(0, 0, 0, 0.3);
-
-    > a {
-      border-inline-start-color: $color-salmon;
-      color: $color-white;
-    }
-  }
-
-  .nav-footer-submenu {
-    a {
-      border-inline-start: 3px solid transparent;
-      overflow: hidden;
-      text-overflow: ellipsis;
-      white-space: nowrap;
-
-      &:before {
-        font-size: 1rem;
-        margin-inline-end: 0.5em;
-        vertical-align: -10%;
-      }
-    }
-  }
-
-  .account {
-    display: none;
-  }
-
-  *:focus {
-    @include show-focus-outline-inside;
-  }
-}
-
-.icon--menuitem {
-  width: 1.25em;
-  height: 1.25em;
-  margin-inline-end: 0.5em;
-  vertical-align: text-top;
-}
-
-.icon--submenu-trigger {
-  // The menus are collapsible on desktop only.
-  display: none;
-  @include media-breakpoint-up(sm) {
-    display: block;
-    width: 1.5em;
-    height: 1.5em;
-    position: absolute;
-    top: 0.8125em;
-    // Remove once we drop support for Safari 13.
-    // stylelint-disable-next-line property-disallowed-list
-    right: 0.5em;
-    inset-inline-end: 0.5em;
-    @include transition(transform 0.3s ease);
-
-    .menu-item.submenu-active & {
-      transform-origin: 50% 50%;
-      transform: rotate(180deg);
-    }
-  }
-}
-
-.icon--submenu-header {
-  display: block;
-  width: 4rem;
-  height: 4rem;
-  margin: 75px auto 0.8em;
-  opacity: 0.15;
-}
-
-.nav-submenu {
-  background: $nav-submenu-bg;
-
-  h2 {
-    display: none;
-  }
-
-  .menu-item a {
-    white-space: normal;
-    padding: 0.9em 1.7em 0.9em 4.5em;
-
-    &:before {
-      margin-inline-start: -1.5em;
-    }
-
-    .icon--menuitem {
-      margin-inline-start: -1.75em;
-    }
-
-    &:hover {
-      background-color: rgba(100, 100, 100, 0.2);
-    }
-  }
-
-  li {
-    border: 0;
-  }
-
-  &__footer {
-    margin: 0;
-    padding: 0.9em 1.7em;
-    text-align: center;
-    color: $color-menu-text;
-  }
-}
-
-.nav-search {
-  position: relative;
-  padding: 0 1em 1em;
-  margin: 0;
-  width: 100%;
-  box-sizing: border-box;
-
-  label {
-    @include visuallyhidden();
-  }
-
-  input,
-  button {
-    border-radius: 0;
-    font-size: 1em;
-    border: 0;
-  }
-
-  input {
-    cursor: pointer;
-    border: 1px solid $nav-search-border;
-    background-color: $nav-search-bg;
-    color: $nav-search-color;
-    padding: 0.8em 2.5em 0.8em 1em;
-    font-weight: 600;
-
-    &:hover {
-      background-color: $nav-search-hover-bg;
-    }
-
-    &:active,
-    &:focus {
-      background-color: $nav-search-focus-bg;
-      color: $nav-search-focus-color;
-    }
-
-    &::placeholder {
-      color: $color-menu-text;
-    }
-  }
-
-  button {
-    background-color: transparent;
-    position: absolute;
-    top: 0;
-    // Remove once we drop support for Safari 13.
-    // stylelint-disable-next-line property-disallowed-list
-    right: 1em;
-    inset-inline-end: 1em;
-    bottom: 0;
-    padding: 0;
-    width: 3em;
-
-    &:hover {
-      background-color: $nav-item-hover-bg;
-    }
-
-    &:active {
-      background-color: $nav-item-active-bg;
-    }
-  }
-}
-
-// Navigation open condition
-body.nav-open {
-  .wrapper {
-    transform: translate3d($menu-width, 0, 0);
-  }
-
-  .content-wrapper {
-    position: fixed;
-  }
-
-  footer {
-    bottom: 1px;
-  }
-}
-
-// Explorer open condition, widens navigation area
-body.explorer-open {
-  .wrapper {
-    transform: translate3d($menu-width-max, 0, 0);
-  }
-
-  .nav-wrapper {
-    margin-inline-start: -$menu-width-max;
-    width: $menu-width-max;
-  }
-
-  .nav-main {
-    display: none;
-  }
-}
-
-@include media-breakpoint-up(sm) {
-  .wrapper,
-  body.nav-open .wrapper {
-    transform: none;
-    padding-inline-start: $menu-width;
-
-    @include transition(padding-inline-start $menu-transition-duration ease);
-  }
-
-  body.sidebar-collapsed .wrapper {
-    padding-inline-start: $menu-width-slim;
-  }
-
-  .nav-wrapper {
-    // height and position necessary to force it to 100% height of screen (with some JS help)
-    position: absolute;
-    // Remove once we drop support for Safari 13.
-    // stylelint-disable-next-line property-disallowed-list
-    left: 0;
-    inset-inline-start: 0;
-    height: 100%;
-    margin-inline-start: 0;
-
-    .inner {
-      height: 100%;
-      position: fixed;
-      width: $menu-width;
-      z-index: $nav-wrapper-inner-z-index;
-    }
-  }
-
-  .nav-toggle.unbutton {
-    display: none;
-  }
-
-  .nav-main {
-    overflow: auto;
-    margin-bottom: $nav-footer-closed-height;
-    @include transition(margin-bottom 0.2s ease);
-
-    .nav-footer {
-      position: fixed;
-      width: $menu-width;
-      bottom: 0;
-      background-color: $nav-footer-submenu-bg;
-    }
-
-    .nav-footer-submenu {
-      @include transition(max-height 0.2s ease);
-      max-height: 0;
-    }
-
-    &--open-footer {
-      margin-bottom: $nav-footer-open-height;
-
-      .nav-footer-submenu {
-        max-height: $nav-footer-submenu-height;
-      }
-    }
-
-    .account {
-      @include clearfix;
-      background: $nav-footer-account-bg;
-      color: $color-menu-text;
-      display: block;
-      cursor: pointer;
-
-      &:hover {
-        background-color: rgba(100, 100, 100, 0.15);
-        color: $color-white;
-        text-shadow: -1px -1px 0 rgba(0, 0, 0, 0.3);
-      }
-
-      .avatar {
-        float: left;
-        margin-inline-end: 0.9em;
-
-        &:before {
-          color: inherit;
-          border-color: inherit;
-        }
-      }
-
-      em {
-        box-sizing: border-box;
-        padding-inline-end: 1.8em;
-        margin-top: 1.2em;
-        font-style: normal;
-        font-weight: 700;
-        width: 110px;
-        overflow: hidden;
-        white-space: nowrap;
-        text-overflow: ellipsis;
-        float: left;
-
-        &:after {
-          font-size: 1.5em;
-          position: absolute;
-          // Remove once we drop support for Safari 13.
-          // stylelint-disable-next-line property-disallowed-list
-          right: 0.25em;
-          inset-inline-end: 0.25em;
-        }
-      }
-    }
-  }
-
-  .nav-submenu {
-    transform: translate3d(0, 0, 0);
-    position: fixed;
-    height: 100vh;
-    width: 0;
-    padding: 0;
-    top: 0;
-    // Remove once we drop support for Safari 13.
-    // stylelint-disable-next-line property-disallowed-list
-    left: $menu-width;
-    inset-inline-start: $menu-width;
-    overflow: hidden;
-    display: flex;
-    flex-direction: column;
-
-    h2,
-    &__list {
-      width: $menu-width;
-    }
-
-    h2 {
-      display: block;
-      padding: 0.2em 0;
-      font-size: 1.2em;
-      font-weight: 500;
-      text-align: center;
-      color: $color-menu-text;
-
-      &:before {
-        font-size: 4em;
-        display: block;
-        text-align: center;
-        margin: 0 0 0.2em;
-        width: 100%;
-        opacity: 0.15;
-      }
-    }
-
-    &__list {
-      overflow: auto;
-      flex-grow: 1;
-    }
-
-    &__footer {
-      line-height: $nav-footer-closed-height;
-      padding: 0;
-    }
-  }
-
-  li.submenu-active {
-    background: $nav-submenu-bg;
-
-    > a {
-      text-shadow: -1px -1px 0 rgba(0, 0, 0, 0.3);
-
-      &:hover {
-        background-color: transparent;
-      }
-    }
-
-    .nav-submenu {
-      @include transition(width 0.2s ease);
-      box-shadow: 2px 0 2px rgba(0, 0, 0, 0.35);
-      width: $menu-width;
-
-      a {
-        padding-inline-start: 3.5em;
-      }
-    }
-  }
-
-  body.nav-open {
-    .content-wrapper {
-      position: relative;
-    }
-  }
-
-  body.explorer-open {
-    overflow: hidden;
-
-    &:after {
-      opacity: 1;
-      visibility: visible;
-    }
-
-    .wrapper {
-      transform: none;
-    }
-
-    .nav-wrapper {
-      margin-inline-start: 0;
-      width: $menu-width;
-    }
-
-    .nav-main {
-      display: block;
-    }
-  }
-}
-
-///////////////
-// Z-indexes //
-///////////////
-.nav-toggle {
-  z-index: 5;
-}
-
-// stylelint-disable-next-line no-duplicate-selectors
-.nav-wrapper {
-  z-index: 2;
-}
-
-// Avoiding a stacking context for the content-wrapper saves us a world
-// of pain when dealing with overlays that are appended to the end of
-// <body>, e.g. calendars. As long as content-wrapper remains floated,
-// the z-index shouldn't be required.
-
-// .content-wrapper {
-// z-index: 3;
-// }
-// stylelint-disable-next-line no-duplicate-selectors
-.nav-submenu {
-  z-index: 6;
-}
-
-footer,
-.logo {
-  z-index: 100;
-}
-
-@include media-breakpoint-up(sm) {
-  .nav-main {
-    .nav-footer {
-      z-index: 2;
-    }
-  }
-
-  .nav-submenu {
-    z-index: 500;
-  }
-
-  // Allows overspill of messages banner onto left menu, but also explorer
-  // to spill over main content
-  .nav-wrapper {
-    z-index: auto;
-  }
-
-  // footer is z-index: 100, so ensure the navigation sits on top of it.
-  .nav-wrapper.submenu-active {
-    z-index: 200;
-  }
-}

+ 3 - 4
client/scss/core.scss

@@ -100,7 +100,7 @@ These are classes for components.
 @import '../src/components/Transition/Transition';
 @import '../src/components/Transition/Transition';
 @import '../src/components/LoadingSpinner/LoadingSpinner';
 @import '../src/components/LoadingSpinner/LoadingSpinner';
 @import '../src/components/PublicationStatus/PublicationStatus';
 @import '../src/components/PublicationStatus/PublicationStatus';
-@import '../src/components/Explorer/Explorer';
+@import '../src/components/PageExplorer/PageExplorer';
 @import '../src/components/CommentApp/main';
 @import '../src/components/CommentApp/main';
 
 
 // Legacy
 // Legacy
@@ -120,9 +120,7 @@ These are classes for components.
 @import 'components/messages.status';
 @import 'components/messages.status';
 @import 'components/header';
 @import 'components/header';
 @import 'components/progressbar';
 @import 'components/progressbar';
-@import 'components/main-nav';
 @import 'components/tooltips';
 @import 'components/tooltips';
-@import 'components/logo';
 @import 'components/grid.legacy';
 @import 'components/grid.legacy';
 @import 'components/breadcrumb';
 @import 'components/breadcrumb';
 @import 'components/footer';
 @import 'components/footer';
@@ -140,6 +138,8 @@ These are classes for components.
 @import 'components/comments-notification-dropdown';
 @import 'components/comments-notification-dropdown';
 @import 'components/bulk_actions';
 @import 'components/bulk_actions';
 
 
+@import '../src/components/Sidebar/Sidebar';
+
 /* OVERRIDES
 /* OVERRIDES
 These are classes that provide overrides.
 These are classes that provide overrides.
 * Higher specificity is allowed here because these are overrides and imported last.
 * Higher specificity is allowed here because these are overrides and imported last.
@@ -151,7 +151,6 @@ These are classes that provide overrides.
 @import 'overrides/vendor.tippy';
 @import 'overrides/vendor.tippy';
 
 
 // UTILITIES: classes that do one simple thing.
 // UTILITIES: classes that do one simple thing.
-@import 'overrides/utilities.hidden';
 @import 'overrides/utilities.dropdowns';
 @import 'overrides/utilities.dropdowns';
 @import 'overrides/utilities.focus';
 @import 'overrides/utilities.focus';
 @import 'overrides/utilities.visuallyhidden';
 @import 'overrides/utilities.visuallyhidden';

+ 0 - 40
client/scss/overrides/_utilities.hidden.scss

@@ -1,40 +0,0 @@
-// stylelint-disable declaration-no-important
-.u-hidden {
-  display: none !important;
-}
-
-.u-hidden\@sm {
-  @include media-breakpoint-up(sm) {
-    display: none !important;
-  }
-}
-
-.u-hidden\@xs {
-  @include media-breakpoint-down(xs) {
-    display: none !important;
-  }
-}
-
-.u-inline\@sm {
-  @include media-breakpoint-up(sm) {
-    display: inline !important;
-  }
-}
-
-.u-inline\@xs {
-  @include media-breakpoint-down(xs) {
-    display: inline !important;
-  }
-}
-
-.u-block\@sm {
-  @include media-breakpoint-up(sm) {
-    display: block !important;
-  }
-}
-
-.u-block\@xs {
-  @include media-breakpoint-down(xs) {
-    display: block !important;
-  }
-}

+ 5 - 0
client/scss/overrides/_utilities.legacy.scss

@@ -1,3 +1,8 @@
+.u-hidden {
+  // stylelint-disable-next-line declaration-no-important
+  display: none !important;
+}
+
 .clearfix {
 .clearfix {
   @include clearfix();
   @include clearfix();
 }
 }

+ 0 - 24
client/scss/settings/_variables.scss

@@ -142,32 +142,8 @@ $menu-transition-duration: 150ms;
 
 
 $focus-outline-width: 3px;
 $focus-outline-width: 3px;
 
 
-$nav-wrapper-inner-z-index: 26;
-$draftail-editor-z-index: $nav-wrapper-inner-z-index + 1;
-
 $object-title-height: 40px;
 $object-title-height: 40px;
 
 
-// Nav
-$nav-grey-1: color.adjust($color-white, $lightness: -80%);
-$nav-grey-2: color.adjust($color-white, $lightness: -60%);
-$nav-grey-3: #262626;
-$nav-item-hover-bg: rgba(100, 100, 100, 0.15);
-$nav-item-active-bg: color.adjust($color-white, $lightness: -90%);
-$nav-submenu-bg: color.adjust($color-white, $lightness: -85%);
-$nav-footer-account-bg: $nav-item-active-bg;
-$nav-footer-submenu-bg: $nav-submenu-bg;
-$nav-footer-closed-height: 50px;
-$nav-footer-submenu-height: 85px;
-$nav-footer-open-height: $nav-footer-closed-height + $nav-footer-submenu-height;
-
-// Nav search
-$nav-search-color: color.adjust($color-white, $lightness: -20%);
-$nav-search-border: color.adjust($color-white, $lightness: -40%);
-$nav-search-bg: $nav-grey-1;
-$nav-search-hover-bg: $nav-item-hover-bg;
-$nav-search-focus-color: $color-white;
-$nav-search-focus-bg: $nav-item-hover-bg;
-
 // Form Errors
 // Form Errors
 $color-text-error: color.change($color-red, $saturation: 69%, $lightness: 52%);
 $color-text-error: color.change($color-red, $saturation: 69%, $lightness: 52%);
 $color-text-error-forced-color: color.change(
 $color-text-error-forced-color: color.change(

+ 0 - 67
client/scss/sidebar.scss

@@ -1,67 +0,0 @@
-/* =============================================================================
-/*  Wagtail CMS sidebar stylesheet entrypoint
-/* =============================================================================
-
-The sidebar is implemented in its own stylesheet so that it can be used
-by different frontend frameworks.
-
-This stylesheet follows the same conventions as the rest of Wagtail,
-see core.scss for details.
-
-==============================================================================*/
-
-/* SETTINGS
-These are variables, maps, and fonts.
-* No CSS should be produced by these files
-*/
-
-@import 'settings';
-
-/* TOOLS
-These are functions and mixins.
-* No CSS should be produced by these files.
-*/
-
-@import 'tools';
-
-/* GENERIC
-This is for resets and other rules that affect large collections of bare elements.
-* Changes to them should be very rare.
-*/
-
-@import 'generic/generic';
-
-/* ELEMENTS
-These are base styles for bare HTML elements.
-* Changes to them should be very rare.
-*/
-
-@import 'elements/elements';
-@import 'elements/typography';
-@import 'elements/forms';
-@import 'elements/root';
-
-/* OBJECTS
-These are classes related to layout, known as 'objects' in ITCSS or OOCSS.
-* This is for grids, wrappers, and other non-consmetic layout utilities.
-* These classes are prefixed with `.o-`.
-*/
-
-@import 'objects/objects';
-@import 'objects/avatar';
-
-/* COMPONENTS
-These are classes for components.
-* These classes (unless legacy) are prefixed with `.c-`.
-* React component styles live in the same folders as their React components,
-  which is the preferred pattern over housing them in the scss folder.
-*/
-
-@import '../src/components/Transition/Transition';
-@import '../src/components/LoadingSpinner/LoadingSpinner';
-@import '../src/components/PublicationStatus/PublicationStatus';
-@import '../src/components/PageExplorer/PageExplorer';
-@import '../src/components/Sidebar/Sidebar';
-
-// Legacy
-@import 'components/icons';

+ 0 - 2
client/src/components/Draftail/Draftail.scss

@@ -1,5 +1,3 @@
-$editor-z-index: $draftail-editor-z-index;
-
 $draftail-editor-text: $color-text-input;
 $draftail-editor-text: $color-text-input;
 $draftail-editor-chrome: $color-white;
 $draftail-editor-chrome: $color-white;
 $draftail-editor-chrome-text: $color-grey-2;
 $draftail-editor-chrome-text: $color-grey-2;

+ 0 - 227
client/src/components/Explorer/Explorer.scss

@@ -1,227 +0,0 @@
-$c-explorer-bg: #4c4e4d;
-$c-explorer-bg-dark: $nav-grey-1;
-$c-explorer-bg-active: rgba(0, 0, 0, 0.425);
-$c-explorer-secondary: #a5a5a5;
-$c-explorer-easing: cubic-bezier(0.075, 0.82, 0.165, 1);
-$menu-footer-height: 50px;
-
-@use 'sass:map';
-
-@import 'ExplorerItem';
-
-.explorer__wrapper,
-.explorer__wrapper * {
-  box-sizing: border-box;
-}
-
-.explorer__wrapper {
-  display: flex;
-  flex: 1;
-}
-
-.explorer {
-  width: 100%;
-  display: flex;
-  flex-direction: column;
-
-  @include media-breakpoint-up(sm) {
-    width: 485px;
-    height: 100vh;
-    position: fixed;
-    z-index: 500;
-    // Remove once we drop support for Safari 13.
-    // stylelint-disable-next-line property-disallowed-list
-    left: $menu-width;
-    inset-inline-start: $menu-width;
-  }
-
-  *:focus {
-    @include show-focus-outline-inside;
-  }
-}
-
-.c-explorer {
-  flex: 1;
-  position: relative;
-  overflow: hidden;
-  background: $c-explorer-bg;
-
-  @include media-breakpoint-up(sm) {
-    box-shadow: 2px 2px 5px $c-explorer-bg-active;
-  }
-}
-
-.c-explorer > .c-transition-group {
-  display: flex;
-  flex-direction: column;
-  height: 100%;
-  z-index: 150;
-}
-
-.c-explorer__close {
-  padding: 1em;
-  color: $c-explorer-secondary;
-  border-bottom: 1px solid rgba(200, 200, 200, 0.1);
-  cursor: pointer;
-  display: none;
-
-  &:focus {
-    background-color: $c-explorer-bg-active;
-    color: $color-white;
-  }
-
-  // Overrides for default link hover.
-  &:hover {
-    color: $c-explorer-secondary;
-  }
-
-  @include media-breakpoint-down(xs) {
-    .explorer-open & {
-      display: block;
-    }
-  }
-}
-
-.c-explorer__drawer {
-  flex: 1;
-  overflow-y: auto;
-  -webkit-overflow-scrolling: touch;
-}
-
-.c-explorer__header {
-  display: block;
-  background-color: $c-explorer-bg-dark;
-  border-bottom: 1px solid $c-explorer-bg-dark;
-  color: $color-white;
-}
-
-.c-explorer__header__title {
-  color: inherit;
-
-  &:focus {
-    background-color: $c-explorer-bg-active;
-    color: $color-white;
-  }
-
-  // Overrides for default link hover.
-  &:hover {
-    color: $color-white;
-  }
-
-  @include hover {
-    background-color: $c-explorer-bg-active;
-  }
-}
-
-.c-explorer__header__title__inner {
-  width: 70%;
-  float: left;
-  padding: 1em 0.75em;
-  overflow: hidden;
-  text-overflow: ellipsis;
-  white-space: nowrap;
-
-  .icon {
-    color: $c-explorer-secondary;
-    margin-inline-end: 0.25rem;
-    font-size: 1rem;
-  }
-
-  .icon--explorer-header {
-    width: 1.25em;
-    height: 1.25em;
-    margin-inline-end: 0.25rem;
-    vertical-align: text-top;
-    color: $c-explorer-secondary;
-  }
-
-  @include media-breakpoint-up(sm) {
-    padding: 1em 1.5em;
-  }
-}
-
-.c-explorer__header__select {
-  $margin: 10px;
-  position: relative;
-
-  > select {
-    width: calc(30% - #{$margin * 2});
-    height: calc(100% - #{$margin * 2});
-    margin-top: $margin;
-    margin-inline-end: $margin;
-    float: right;
-    padding: 0;
-    padding-inline-start: 10px;
-
-    background-color: $c-explorer-bg-dark;
-    border-radius: 0;
-    border-color: #4c4e4d;
-    color: $color-white;
-
-    &:disabled {
-      border: 0;
-    }
-
-    &:hover:enabled {
-      cursor: pointer;
-    }
-
-    &:hover:disabled {
-      color: inherit;
-      background-color: inherit;
-      cursor: inherit;
-    }
-  }
-
-  // Add select arrow back on browsers where native ui has been removed
-  &-icon {
-    position: absolute;
-    // Remove once we drop support for Safari 13.
-    // stylelint-disable-next-line property-disallowed-list
-    right: 1rem;
-    inset-inline-end: 1rem;
-    top: 1rem;
-    width: 1.25rem;
-    height: 1.25rem;
-    color: $color-grey-3;
-
-    .ie & {
-      display: none;
-    }
-  }
-}
-
-.c-explorer__placeholder {
-  padding: 1em;
-  color: $color-white;
-
-  @include media-breakpoint-up(sm) {
-    padding: 1em 1.75em;
-  }
-}
-
-.c-explorer__see-more {
-  display: block;
-  padding: 1em;
-  background: rgba(0, 0, 0, 0.3);
-  color: $color-white;
-
-  &:focus {
-    color: $c-explorer-secondary;
-    background: $c-explorer-bg-active;
-  }
-
-  // Overrides for default link hover.
-  &:hover {
-    color: $color-white;
-  }
-
-  @include hover {
-    background: $c-explorer-bg-active;
-  }
-
-  @include media-breakpoint-up(sm) {
-    padding: 1em 1.75em;
-    height: $menu-footer-height;
-  }
-}

+ 0 - 62
client/src/components/Explorer/Explorer.test.js

@@ -1,62 +0,0 @@
-import React from 'react';
-import { shallow } from 'enzyme';
-import { Provider } from 'react-redux';
-import { createStore, applyMiddleware, combineReducers } from 'redux';
-import thunkMiddleware from 'redux-thunk';
-import * as actions from './actions';
-import explorer from './reducers/explorer';
-import nodes from './reducers/nodes';
-import Explorer from './Explorer';
-
-const rootReducer = combineReducers({
-  explorer,
-  nodes,
-});
-
-const store = createStore(rootReducer, {}, applyMiddleware(thunkMiddleware));
-
-describe('Explorer', () => {
-  it('exists', () => {
-    expect(Explorer).toBeDefined();
-  });
-
-  it('renders', () => {
-    expect(shallow(<Explorer store={store} />).dive()).toMatchSnapshot();
-    expect(
-      shallow(
-        <Provider store={store}>
-          <Explorer />
-        </Provider>,
-      ).dive(),
-    ).toMatchSnapshot();
-  });
-
-  it('visible', () => {
-    store.dispatch(actions.toggleExplorer(1));
-    expect(shallow(<Explorer store={store} />).dive()).toMatchSnapshot();
-    expect(
-      shallow(<Explorer store={store} />)
-        .dive()
-        .dive(),
-    ).toMatchSnapshot();
-  });
-
-  describe('actions', () => {
-    let wrapper;
-
-    beforeEach(() => {
-      store.dispatch = jest.fn();
-      wrapper = shallow(<Explorer store={store} />);
-    });
-
-    it('gotoPage', () => {
-      wrapper.dive().prop('gotoPage')();
-      expect(store.dispatch).toHaveBeenCalled();
-    });
-
-    it('onClose', () => {
-      wrapper.dive().prop('onClose')();
-      expect(store.dispatch).toHaveBeenCalled();
-    });
-  });
-});

+ 0 - 50
client/src/components/Explorer/Explorer.tsx

@@ -1,50 +0,0 @@
-import React from 'react';
-import { connect } from 'react-redux';
-
-import * as actions from './actions';
-import { State as NodeState } from './reducers/nodes';
-import { State } from './reducers';
-
-import ExplorerPanel from './ExplorerPanel';
-
-interface ExplorerProps {
-  isVisible: boolean;
-  depth: number;
-  currentPageId: number | null;
-  nodes: NodeState;
-  onClose(): void;
-  gotoPage(id: number, transition: number): void;
-}
-
-const Explorer: React.FunctionComponent<ExplorerProps> = ({
-  isVisible,
-  depth,
-  currentPageId,
-  nodes,
-  gotoPage,
-  onClose,
-}) =>
-  isVisible && currentPageId ? (
-    <ExplorerPanel
-      depth={depth}
-      page={nodes[currentPageId]}
-      nodes={nodes}
-      gotoPage={gotoPage}
-      onClose={onClose}
-    />
-  ) : null;
-
-const mapStateToProps = (state: State) => ({
-  isVisible: state.explorer.isVisible,
-  depth: state.explorer.depth,
-  currentPageId: state.explorer.currentPageId,
-  nodes: state.nodes,
-});
-
-const mapDispatchToProps = (dispatch) => ({
-  gotoPage: (id: number, transition: number) =>
-    dispatch(actions.gotoPage(id, transition)),
-  onClose: () => dispatch(actions.closeExplorer()),
-});
-
-export default connect(mapStateToProps, mapDispatchToProps)(Explorer);

+ 0 - 53
client/src/components/Explorer/ExplorerHeader.test.js

@@ -1,53 +0,0 @@
-import React from 'react';
-import { mount, shallow } from 'enzyme';
-
-import ExplorerHeader from './ExplorerHeader';
-
-const mockProps = {
-  page: {
-    meta: {
-      parent: {
-        id: 1,
-      },
-    },
-  },
-  depth: 2,
-  onClick: jest.fn(),
-};
-
-describe('ExplorerHeader', () => {
-  it('exists', () => {
-    expect(ExplorerHeader).toBeDefined();
-  });
-
-  it('basic', () => {
-    expect(shallow(<ExplorerHeader {...mockProps} />)).toMatchSnapshot();
-  });
-
-  it('#depth at root', () => {
-    expect(
-      shallow(<ExplorerHeader {...mockProps} depth={0} />),
-    ).toMatchSnapshot();
-  });
-
-  it('#page', () => {
-    const wrapper = shallow(
-      <ExplorerHeader
-        {...mockProps}
-        page={{
-          id: 'a',
-          admin_display_title: 'test',
-          meta: { parent: { id: 1 } },
-        }}
-      />,
-    );
-    expect(wrapper).toMatchSnapshot();
-  });
-
-  it('#onClick', () => {
-    const wrapper = mount(<ExplorerHeader {...mockProps} />);
-    wrapper.find('Button').simulate('click');
-
-    expect(mockProps.onClick).toHaveBeenCalledTimes(1);
-  });
-});

+ 0 - 97
client/src/components/Explorer/ExplorerHeader.tsx

@@ -1,97 +0,0 @@
-import React from 'react';
-import { ADMIN_URLS } from '../../config/wagtailConfig';
-
-import { gettext } from '../../utils/gettext';
-import Button from '../../components/Button/Button';
-import Icon from '../../components/Icon/Icon';
-import { PageState } from './reducers/nodes';
-
-interface SelectLocaleProps {
-  locale?: string;
-  translations: Map<string, number>;
-  gotoPage(id: number, transition: number): void;
-}
-
-const SelectLocale: React.FunctionComponent<SelectLocaleProps> = ({
-  locale,
-  translations,
-  gotoPage,
-}) => {
-  /* eslint-disable camelcase */
-  const options = wagtailConfig.LOCALES.filter(
-    ({ code }) => code === locale || translations.get(code),
-  ).map(({ code, display_name }) => (
-    <option key={code} value={code}>
-      {display_name}
-    </option>
-  ));
-  /* eslint-enable camelcase */
-
-  const onChange = (e) => {
-    e.preventDefault();
-    const translation = translations.get(e.target.value);
-    if (translation) {
-      gotoPage(translation, 0);
-    }
-  };
-
-  return (
-    <div className="c-explorer__header__select">
-      <select value={locale} onChange={onChange} disabled={options.length < 2}>
-        {options}
-      </select>
-      <Icon name="arrow-down" className="c-explorer__header__select-icon" />
-    </div>
-  );
-};
-
-interface ExplorerHeaderProps {
-  page: PageState;
-  depth: number;
-  onClick(e: any): void;
-  gotoPage(id: number, transition: number): void;
-}
-
-/**
- * The bar at the top of the explorer, displaying the current level
- * and allowing access back to the parent level.
- */
-const ExplorerHeader: React.FunctionComponent<ExplorerHeaderProps> = ({
-  page,
-  depth,
-  onClick,
-  gotoPage,
-}) => {
-  const isRoot = depth === 0;
-  const isSiteRoot = page.id === 0;
-
-  return (
-    <div className="c-explorer__header">
-      <Button
-        href={!isSiteRoot ? `${ADMIN_URLS.PAGES}${page.id}/` : ADMIN_URLS.PAGES}
-        className="c-explorer__header__title"
-        onClick={onClick}
-      >
-        <div className="c-explorer__header__title__inner">
-          <Icon
-            name={isRoot ? 'home' : 'arrow-left'}
-            className="icon--explorer-header"
-          />
-          <span>{page.admin_display_title || gettext('Pages')}</span>
-        </div>
-      </Button>
-      {!isSiteRoot &&
-        page.meta.locale &&
-        page.translations &&
-        page.translations.size > 0 && (
-          <SelectLocale
-            locale={page.meta.locale}
-            translations={page.translations}
-            gotoPage={gotoPage}
-          />
-        )}
-    </div>
-  );
-};
-
-export default ExplorerHeader;

+ 0 - 93
client/src/components/Explorer/ExplorerItem.scss

@@ -1,93 +0,0 @@
-.c-explorer__item {
-  display: flex;
-  flex-flow: row nowrap;
-  border-bottom: 1px solid $c-explorer-bg-dark;
-}
-
-.c-explorer__item__link {
-  display: inline-flex;
-  align-items: center;
-  flex-grow: 1;
-  padding: 1.45em 1em;
-  cursor: pointer;
-
-  &:focus {
-    background: $c-explorer-bg-active;
-    color: $color-white;
-  }
-
-  // Overrides for default link hover.
-  &:hover {
-    color: $color-white;
-  }
-
-  @include hover {
-    background: $c-explorer-bg-active;
-  }
-
-  @include media-breakpoint-up(sm) {
-    padding: 1.45em 1.75em;
-  }
-}
-
-.c-explorer__item__link .icon {
-  width: 2em;
-  height: 2em;
-  color: $c-explorer-secondary;
-  margin-inline-end: 0.75rem;
-}
-
-.c-explorer__item__title {
-  margin: 0;
-  color: $color-white;
-  display: inline-block;
-}
-
-.c-explorer__item__action {
-  display: inline-flex;
-  align-items: center;
-  justify-content: center;
-  flex-shrink: 0;
-  width: 50px;
-  padding: 0 0.5em;
-  line-height: 1;
-  font-size: 2em;
-  cursor: pointer;
-  color: $c-explorer-secondary;
-  border: 0;
-  border-inline-start: solid 1px $c-explorer-bg-dark;
-
-  &:focus {
-    background: $c-explorer-bg-active;
-    color: $color-white;
-  }
-
-  // Overrides for default link hover.
-  &:hover {
-    color: $c-explorer-secondary;
-  }
-
-  @include hover {
-    background: $c-explorer-bg-active;
-    color: $color-white;
-  }
-
-  .icon::before {
-    margin-inline-end: 0;
-  }
-}
-
-.c-explorer__item__action--small {
-  font-size: 1.2em;
-}
-
-.icon--item-action {
-  width: 1em;
-  height: 1em;
-}
-
-.c-explorer__meta {
-  margin-inline-start: 0.5rem;
-  color: $c-explorer-secondary;
-  font-size: 12px;
-}

+ 0 - 54
client/src/components/Explorer/ExplorerItem.test.js

@@ -1,54 +0,0 @@
-import React from 'react';
-import { shallow } from 'enzyme';
-
-import ExplorerItem from './ExplorerItem';
-
-const mockProps = {
-  item: {
-    id: 5,
-    admin_display_title: 'test',
-    meta: {
-      latest_revision_created_at: null,
-      status: {
-        live: true,
-        status: 'test',
-        has_unpublished_changes: false,
-      },
-      descendants: {
-        count: 0,
-      },
-      children: {
-        count: 0,
-      },
-    },
-  },
-  onClick: () => {},
-};
-
-describe('ExplorerItem', () => {
-  it('exists', () => {
-    expect(ExplorerItem).toBeDefined();
-  });
-
-  it('renders', () => {
-    expect(shallow(<ExplorerItem {...mockProps} />)).toMatchSnapshot();
-  });
-
-  it('children', () => {
-    const props = Object.assign({}, mockProps);
-    props.item.meta.children.count = 5;
-    expect(shallow(<ExplorerItem {...props} />)).toMatchSnapshot();
-  });
-
-  it('should show a publication status with unpublished changes', () => {
-    const props = Object.assign({}, mockProps);
-    props.item.meta.status.has_unpublished_changes = true;
-    expect(shallow(<ExplorerItem {...props} />)).toMatchSnapshot();
-  });
-
-  it('should show a publication status if not live', () => {
-    const props = Object.assign({}, mockProps);
-    props.item.meta.status.live = false;
-    expect(shallow(<ExplorerItem {...props} />)).toMatchSnapshot();
-  });
-});

+ 0 - 83
client/src/components/Explorer/ExplorerItem.tsx

@@ -1,83 +0,0 @@
-import React from 'react';
-
-import { gettext } from '../../utils/gettext';
-import { ADMIN_URLS, LOCALE_NAMES } from '../../config/wagtailConfig';
-import Icon from '../../components/Icon/Icon';
-import Button from '../../components/Button/Button';
-import PublicationStatus from '../../components/PublicationStatus/PublicationStatus';
-import { PageState } from './reducers/nodes';
-
-// Hoist icons in the explorer item, as it is re-rendered many times.
-const childrenIcon = <Icon name="folder-inverse" className="icon--menuitem" />;
-
-interface ExplorerItemProps {
-  item: PageState;
-  onClick(): void;
-}
-
-/**
- * One menu item in the page explorer, with different available actions
- * and information depending on the metadata of the page.
- */
-const ExplorerItem: React.FunctionComponent<ExplorerItemProps> = ({
-  item,
-  onClick,
-}) => {
-  const { id, admin_display_title: title, meta } = item;
-  const hasChildren = meta.children.count > 0;
-  const isPublished = meta.status.live && !meta.status.has_unpublished_changes;
-  const localeName =
-    meta.parent?.id === 1 &&
-    meta.locale &&
-    (LOCALE_NAMES.get(meta.locale) || meta.locale);
-
-  return (
-    <div className="c-explorer__item">
-      <Button
-        href={`${ADMIN_URLS.PAGES}${id}/`}
-        className="c-explorer__item__link"
-      >
-        {hasChildren ? childrenIcon : null}
-
-        <h3 className="c-explorer__item__title">{title}</h3>
-
-        {(!isPublished || localeName) && (
-          <span className="c-explorer__meta">
-            {localeName && (
-              <span className="o-pill c-status">{localeName}</span>
-            )}
-            {!isPublished && <PublicationStatus status={meta.status} />}
-          </span>
-        )}
-      </Button>
-      <Button
-        href={`${ADMIN_URLS.PAGES}${id}/edit/`}
-        className="c-explorer__item__action c-explorer__item__action--small"
-      >
-        <Icon
-          name="edit"
-          title={gettext("Edit '{title}'").replace('{title}', title || '')}
-          className="icon--item-action"
-        />
-      </Button>
-      {hasChildren ? (
-        <Button
-          className="c-explorer__item__action"
-          onClick={onClick}
-          href={`${ADMIN_URLS.PAGES}${id}/`}
-        >
-          <Icon
-            name="arrow-right"
-            title={gettext("View child pages of '{title}'").replace(
-              '{title}',
-              title || '',
-            )}
-            className="icon--item-action"
-          />
-        </Button>
-      ) : null}
-    </div>
-  );
-};
-
-export default ExplorerItem;

+ 0 - 228
client/src/components/Explorer/ExplorerPanel.test.js

@@ -1,228 +0,0 @@
-import React from 'react';
-import { shallow } from 'enzyme';
-import ExplorerPanel from './ExplorerPanel';
-
-const mockProps = {
-  page: {
-    children: {
-      items: [],
-    },
-    meta: {
-      parent: null,
-    },
-  },
-  depth: 1,
-  onClose: jest.fn(),
-  gotoPage: jest.fn(),
-  nodes: {},
-};
-
-describe('ExplorerPanel', () => {
-  describe('general rendering', () => {
-    beforeEach(() => {
-      document.body.innerHTML = '<div data-explorer-menu-item></div>';
-    });
-
-    it('exists', () => {
-      expect(ExplorerPanel).toBeDefined();
-    });
-
-    it('renders', () => {
-      expect(shallow(<ExplorerPanel {...mockProps} />)).toMatchSnapshot();
-    });
-
-    it('#isFetching', () => {
-      expect(
-        shallow(
-          <ExplorerPanel
-            {...mockProps}
-            page={Object.assign({ isFetching: true }, mockProps.page)}
-          />,
-        ),
-      ).toMatchSnapshot();
-    });
-
-    it('#isError', () => {
-      expect(
-        shallow(
-          <ExplorerPanel
-            {...mockProps}
-            page={Object.assign({ isError: true }, mockProps.page)}
-          />,
-        ),
-      ).toMatchSnapshot();
-    });
-
-    it('no children', () => {
-      expect(
-        shallow(<ExplorerPanel {...mockProps} page={{ children: {} }} />),
-      ).toMatchSnapshot();
-    });
-
-    it('#items', () => {
-      expect(
-        shallow(
-          <ExplorerPanel
-            {...mockProps}
-            page={{ children: { items: [1, 2] } }}
-            nodes={{
-              1: {
-                id: 1,
-                admin_display_title: 'Test',
-                meta: { status: {}, type: 'test' },
-              },
-              2: {
-                id: 2,
-                admin_display_title: 'Foo',
-                meta: { status: {}, type: 'foo' },
-              },
-            }}
-          />,
-        ),
-      ).toMatchSnapshot();
-    });
-  });
-
-  describe('onHeaderClick', () => {
-    beforeEach(() => {
-      mockProps.gotoPage.mockReset();
-    });
-
-    it('calls gotoPage', () => {
-      shallow(
-        <ExplorerPanel
-          {...mockProps}
-          depth={2}
-          page={{ children: { items: [] }, meta: { parent: { id: 1 } } }}
-        />,
-      )
-        .find('ExplorerHeader')
-        .prop('onClick')({
-        preventDefault() {},
-        stopPropagation() {},
-      });
-
-      expect(mockProps.gotoPage).toHaveBeenCalled();
-    });
-
-    it('does not call gotoPage for first page', () => {
-      shallow(
-        <ExplorerPanel
-          {...mockProps}
-          depth={0}
-          page={{ children: { items: [] }, meta: { parent: { id: 1 } } }}
-        />,
-      )
-        .find('ExplorerHeader')
-        .prop('onClick')({
-        preventDefault() {},
-        stopPropagation() {},
-      });
-
-      expect(mockProps.gotoPage).not.toHaveBeenCalled();
-    });
-  });
-
-  describe('onItemClick', () => {
-    beforeEach(() => {
-      mockProps.gotoPage.mockReset();
-    });
-
-    it('calls gotoPage', () => {
-      shallow(
-        <ExplorerPanel
-          {...mockProps}
-          path={[1]}
-          page={{ children: { items: [1] } }}
-          nodes={{
-            1: {
-              id: 1,
-              admin_display_title: 'Test',
-              meta: { status: {}, type: 'test' },
-            },
-          }}
-        />,
-      )
-        .find('ExplorerItem')
-        .prop('onClick')({
-        preventDefault() {},
-        stopPropagation() {},
-      });
-
-      expect(mockProps.gotoPage).toHaveBeenCalled();
-    });
-  });
-
-  describe('hooks', () => {
-    it('componentWillReceiveProps push', () => {
-      const wrapper = shallow(<ExplorerPanel {...mockProps} />);
-      expect(wrapper.setProps({ depth: 2 }).state('transition')).toBe('push');
-    });
-
-    it('componentWillReceiveProps pop', () => {
-      const wrapper = shallow(<ExplorerPanel {...mockProps} />);
-      expect(wrapper.setProps({ depth: 0 }).state('transition')).toBe('pop');
-    });
-
-    it('componentDidMount', () => {
-      document.body.innerHTML = '<div data-explorer-menu-item></div>';
-      const wrapper = shallow(<ExplorerPanel {...mockProps} />);
-      wrapper.instance().componentDidMount();
-      expect(
-        document
-          .querySelector('[data-explorer-menu-item]')
-          .classList.contains('submenu-active'),
-      ).toBe(true);
-      expect(document.body.classList.contains('explorer-open')).toBe(true);
-    });
-
-    it('componentWillUnmount', () => {
-      document.body.innerHTML =
-        '<div class="submenu-active" data-explorer-menu-item></div>';
-      const wrapper = shallow(<ExplorerPanel {...mockProps} />);
-      wrapper.instance().componentWillUnmount();
-      expect(
-        document
-          .querySelector('[data-explorer-menu-item]')
-          .classList.contains('submenu-active'),
-      ).toBe(false);
-      expect(document.body.classList.contains('explorer-open')).toBe(false);
-    });
-  });
-
-  describe('clickOutside', () => {
-    afterEach(() => {
-      mockProps.onClose.mockReset();
-    });
-
-    it('triggers onClose when click is outside', () => {
-      document.body.innerHTML =
-        '<div data-explorer-menu-item></div><div data-explorer-menu></div><div id="t"></div>';
-      const wrapper = shallow(<ExplorerPanel {...mockProps} />);
-      wrapper.instance().clickOutside({
-        target: document.querySelector('#t'),
-      });
-      expect(mockProps.onClose).toHaveBeenCalled();
-    });
-
-    it('does not trigger onClose when click is inside', () => {
-      document.body.innerHTML =
-        '<div data-explorer-menu-item></div><div data-explorer-menu><div id="t"></div></div>';
-      const wrapper = shallow(<ExplorerPanel {...mockProps} />);
-      wrapper.instance().clickOutside({
-        target: document.querySelector('#t'),
-      });
-      expect(mockProps.onClose).not.toHaveBeenCalled();
-    });
-
-    it('pauses focus trap inside toggle', () => {
-      document.body.innerHTML =
-        '<div data-explorer-menu-item><div id="t"></div></div><div data-explorer-menu></div>';
-      const wrapper = shallow(<ExplorerPanel {...mockProps} />);
-      wrapper.instance().clickOutside({
-        target: document.querySelector('#t'),
-      });
-      expect(wrapper.state('paused')).toEqual(true);
-    });
-  });
-});

+ 0 - 210
client/src/components/Explorer/ExplorerPanel.tsx

@@ -1,210 +0,0 @@
-import React from 'react';
-import FocusTrap from 'focus-trap-react';
-
-import { gettext } from '../../utils/gettext';
-import { MAX_EXPLORER_PAGES } from '../../config/wagtailConfig';
-
-import Button from '../Button/Button';
-import LoadingSpinner from '../LoadingSpinner/LoadingSpinner';
-import Transition, { PUSH, POP } from '../Transition/Transition';
-import ExplorerHeader from './ExplorerHeader';
-import ExplorerItem from './ExplorerItem';
-import PageCount from './PageCount';
-import { State as NodeState, PageState } from './reducers/nodes';
-
-interface ExplorerPanelProps {
-  nodes: NodeState;
-  depth: number;
-  page: PageState;
-  onClose(): void;
-  gotoPage(id: number, transition: number): void;
-}
-
-interface ExplorerPanelState {
-  transition: typeof PUSH | typeof POP;
-  paused: boolean;
-}
-
-/**
- * The main panel of the page explorer menu, with heading,
- * menu items, and special states.
- */
-class ExplorerPanel extends React.Component<
-  ExplorerPanelProps,
-  ExplorerPanelState
-> {
-  constructor(props) {
-    super(props);
-
-    this.state = {
-      transition: PUSH,
-      paused: false,
-    };
-
-    this.onItemClick = this.onItemClick.bind(this);
-    this.onHeaderClick = this.onHeaderClick.bind(this);
-    this.clickOutside = this.clickOutside.bind(this);
-  }
-
-  componentWillReceiveProps(newProps) {
-    const { depth } = this.props;
-    const isPush = newProps.depth > depth;
-
-    this.setState({
-      transition: isPush ? PUSH : POP,
-    });
-  }
-
-  componentDidMount() {
-    document
-      .querySelector('[data-explorer-menu-item]')
-      ?.classList.add('submenu-active');
-    document.body.classList.add('explorer-open');
-    document.addEventListener('mousedown', this.clickOutside);
-    document.addEventListener('touchend', this.clickOutside);
-  }
-
-  componentWillUnmount() {
-    document
-      .querySelector('[data-explorer-menu-item]')
-      ?.classList.remove('submenu-active');
-    document.body.classList.remove('explorer-open');
-    document.removeEventListener('mousedown', this.clickOutside);
-    document.removeEventListener('touchend', this.clickOutside);
-  }
-
-  clickOutside(e) {
-    const { onClose } = this.props;
-    const explorer = document.querySelector('[data-explorer-menu]');
-    const toggle = document.querySelector('[data-explorer-menu-item]');
-
-    if (!explorer || !toggle) {
-      return;
-    }
-
-    const isInside = explorer.contains(e.target) || toggle.contains(e.target);
-    if (!isInside) {
-      onClose();
-    }
-
-    if (toggle.contains(e.target)) {
-      this.setState({
-        paused: true,
-      });
-    }
-  }
-
-  onItemClick(id, e) {
-    const { gotoPage } = this.props;
-
-    e.preventDefault();
-    e.stopPropagation();
-
-    gotoPage(id, 1);
-  }
-
-  onHeaderClick(e) {
-    const { page, depth, gotoPage } = this.props;
-    const parent = page.meta.parent?.id;
-
-    // Note: Checking depth as well in case the user started deep in the tree
-    if (depth > 0 && parent) {
-      e.preventDefault();
-      e.stopPropagation();
-
-      gotoPage(parent, -1);
-    }
-  }
-
-  renderChildren() {
-    const { page, nodes } = this.props;
-    let children;
-
-    if (!page.isFetchingChildren && !page.children.items) {
-      children = (
-        <div key="empty" className="c-explorer__placeholder">
-          {gettext('No results')}
-        </div>
-      );
-    } else {
-      children = (
-        <div key="children">
-          {page.children.items.map((id) => (
-            <ExplorerItem
-              key={id}
-              item={nodes[id]}
-              onClick={this.onItemClick.bind(null, id)}
-            />
-          ))}
-        </div>
-      );
-    }
-
-    return (
-      <div className="c-explorer__drawer">
-        {children}
-        {page.isFetchingChildren || page.isFetchingTranslations ? (
-          <div key="fetching" className="c-explorer__placeholder">
-            <LoadingSpinner />
-          </div>
-        ) : null}
-        {page.isError ? (
-          <div key="error" className="c-explorer__placeholder">
-            {gettext('Server Error')}
-          </div>
-        ) : null}
-      </div>
-    );
-  }
-
-  render() {
-    const { page, onClose, depth, gotoPage } = this.props;
-    const { transition, paused } = this.state;
-
-    return (
-      <FocusTrap
-        paused={
-          paused ||
-          !page ||
-          page.isFetchingChildren ||
-          page.isFetchingTranslations
-        }
-        focusTrapOptions={{
-          initialFocus: '.c-explorer__header__title',
-          onDeactivate: onClose,
-        }}
-      >
-        <div role="dialog" className="explorer">
-          <Button className="c-explorer__close">
-            {gettext('Close explorer')}
-          </Button>
-          <Transition
-            name={transition}
-            className="c-explorer"
-            component="nav"
-            label={gettext('Page explorer')}
-          >
-            <div key={depth} className="c-transition-group">
-              <ExplorerHeader
-                depth={depth}
-                page={page}
-                onClick={this.onHeaderClick}
-                gotoPage={gotoPage}
-              />
-
-              {this.renderChildren()}
-
-              {page.isError ||
-              (page.children.items &&
-                page.children.count > MAX_EXPLORER_PAGES) ? (
-                <PageCount page={page} />
-              ) : null}
-            </div>
-          </Transition>
-        </div>
-      </FocusTrap>
-    );
-  }
-}
-
-export default ExplorerPanel;

+ 0 - 39
client/src/components/Explorer/ExplorerToggle.test.js

@@ -1,39 +0,0 @@
-import React from 'react';
-import { shallow } from 'enzyme';
-import configureMockStore from 'redux-mock-store';
-
-import ExplorerToggle from './ExplorerToggle';
-
-const store = configureMockStore()({});
-
-describe('ExplorerToggle', () => {
-  it('exists', () => {
-    expect(ExplorerToggle).toBeDefined();
-  });
-
-  it('basic', () => {
-    expect(
-      shallow(
-        <ExplorerToggle store={store}>
-          <span>To infinity and beyond!</span>
-        </ExplorerToggle>,
-      )
-        .find('ExplorerToggle')
-        .dive(),
-    ).toMatchSnapshot();
-  });
-
-  describe('actions', () => {
-    let wrapper;
-
-    beforeEach(() => {
-      store.dispatch = jest.fn();
-      wrapper = shallow(<ExplorerToggle store={store}>Test</ExplorerToggle>);
-    });
-
-    it('onToggle', () => {
-      wrapper.dive().prop('onToggle')();
-      expect(store.dispatch).toHaveBeenCalled();
-    });
-  });
-});

+ 0 - 43
client/src/components/Explorer/ExplorerToggle.tsx

@@ -1,43 +0,0 @@
-import React from 'react';
-import { connect } from 'react-redux';
-
-import * as actions from './actions';
-
-import Button from '../../components/Button/Button';
-import Icon from '../../components/Icon/Icon';
-
-interface ExplorerToggleProps {
-  onToggle(): void;
-  children: React.ReactNode;
-}
-
-/**
- * A Button which toggles the explorer.
- */
-const ExplorerToggle: React.FunctionComponent<ExplorerToggleProps> = ({
-  children,
-  onToggle,
-}) => (
-  <Button dialogTrigger={true} onClick={onToggle}>
-    <Icon name="folder-open-inverse" className="icon--menuitem" />
-    {children}
-    <Icon name="arrow-right" className="icon--submenu-trigger" />
-  </Button>
-);
-
-const mapStateToProps = () => ({});
-
-const mapDispatchToProps = (dispatch) => ({
-  onToggle: (page) => dispatch(actions.toggleExplorer(page)),
-});
-
-const mergeProps = (_stateProps, dispatchProps, ownProps) => ({
-  children: ownProps.children,
-  onToggle: dispatchProps.onToggle.bind(null, ownProps.startPage),
-});
-
-export default connect(
-  mapStateToProps,
-  mapDispatchToProps,
-  mergeProps,
-)(ExplorerToggle);

+ 0 - 29
client/src/components/Explorer/PageCount.test.js

@@ -1,29 +0,0 @@
-import React from 'react';
-import { shallow } from 'enzyme';
-
-import PageCount from './PageCount';
-
-const mockProps = {
-  page: {
-    id: 1,
-    children: {
-      count: 1,
-    },
-  },
-};
-
-describe('PageCount', () => {
-  it('exists', () => {
-    expect(PageCount).toBeDefined();
-  });
-
-  it('works', () => {
-    expect(shallow(<PageCount {...mockProps} />)).toMatchSnapshot();
-  });
-
-  it('plural', () => {
-    const props = Object.assign({}, mockProps);
-    props.page.children.count = 5;
-    expect(shallow(<PageCount {...props} />)).toMatchSnapshot();
-  });
-});

+ 0 - 32
client/src/components/Explorer/PageCount.tsx

@@ -1,32 +0,0 @@
-import React from 'react';
-
-import { gettext } from '../../utils/gettext';
-import { ADMIN_URLS } from '../../config/wagtailConfig';
-import Icon from '../Icon/Icon';
-
-interface PageCountProps {
-  page: {
-    id: number;
-    children: {
-      count: number;
-    };
-  };
-}
-
-const PageCount: React.FunctionComponent<PageCountProps> = ({ page }) => {
-  const count = page.children.count;
-
-  return (
-    <a href={`${ADMIN_URLS.PAGES}${page.id}/`} className="c-explorer__see-more">
-      {gettext('See all')}
-      <span>{` ${count} ${
-        count === 1
-          ? gettext('Page').toLowerCase()
-          : gettext('Pages').toLowerCase()
-      }`}</span>
-      <Icon name="arrow-right" />
-    </a>
-  );
-};
-
-export default PageCount;

+ 0 - 117
client/src/components/Explorer/__snapshots__/Explorer.test.js.snap

@@ -1,117 +0,0 @@
-// Jest Snapshot v1, https://goo.gl/fbAQLP
-
-exports[`Explorer renders 1`] = `
-<Explorer
-  currentPageId={null}
-  depth={0}
-  gotoPage={[Function]}
-  isVisible={false}
-  nodes={Object {}}
-  onClose={[Function]}
-  store={
-    Object {
-      "@@observable": [Function],
-      "dispatch": [Function],
-      "getState": [Function],
-      "replaceReducer": [Function],
-      "subscribe": [Function],
-    }
-  }
-/>
-`;
-
-exports[`Explorer renders 2`] = `<Connect(Explorer) />`;
-
-exports[`Explorer visible 1`] = `
-<Explorer
-  currentPageId={1}
-  depth={0}
-  gotoPage={[Function]}
-  isVisible={true}
-  nodes={
-    Object {
-      "1": Object {
-        "children": Object {
-          "count": 0,
-          "items": Array [],
-        },
-        "id": 0,
-        "isError": false,
-        "isFetchingChildren": true,
-        "isFetchingTranslations": false,
-        "meta": Object {
-          "children": Object {},
-          "parent": null,
-          "status": Object {
-            "has_unpublished_changes": true,
-            "live": false,
-            "status": "",
-          },
-        },
-      },
-    }
-  }
-  onClose={[Function]}
-  store={
-    Object {
-      "@@observable": [Function],
-      "dispatch": [Function],
-      "getState": [Function],
-      "replaceReducer": [Function],
-      "subscribe": [Function],
-    }
-  }
-/>
-`;
-
-exports[`Explorer visible 2`] = `
-<ExplorerPanel
-  depth={0}
-  gotoPage={[Function]}
-  nodes={
-    Object {
-      "1": Object {
-        "children": Object {
-          "count": 0,
-          "items": Array [],
-        },
-        "id": 0,
-        "isError": false,
-        "isFetchingChildren": true,
-        "isFetchingTranslations": false,
-        "meta": Object {
-          "children": Object {},
-          "parent": null,
-          "status": Object {
-            "has_unpublished_changes": true,
-            "live": false,
-            "status": "",
-          },
-        },
-      },
-    }
-  }
-  onClose={[Function]}
-  page={
-    Object {
-      "children": Object {
-        "count": 0,
-        "items": Array [],
-      },
-      "id": 0,
-      "isError": false,
-      "isFetchingChildren": true,
-      "isFetchingTranslations": false,
-      "meta": Object {
-        "children": Object {},
-        "parent": null,
-        "status": Object {
-          "has_unpublished_changes": true,
-          "live": false,
-          "status": "",
-        },
-      },
-    }
-  }
-/>
-`;

+ 0 - 73
client/src/components/Explorer/__snapshots__/ExplorerHeader.test.js.snap

@@ -1,73 +0,0 @@
-// Jest Snapshot v1, https://goo.gl/fbAQLP
-
-exports[`ExplorerHeader #depth at root 1`] = `
-<div
-  className="c-explorer__header"
->
-  <Button
-    className="c-explorer__header__title"
-    href="/admin/pages/undefined/"
-    onClick={[MockFunction]}
-  >
-    <div
-      className="c-explorer__header__title__inner"
-    >
-      <Icon
-        className="icon--explorer-header"
-        name="home"
-      />
-      <span>
-        Pages
-      </span>
-    </div>
-  </Button>
-</div>
-`;
-
-exports[`ExplorerHeader #page 1`] = `
-<div
-  className="c-explorer__header"
->
-  <Button
-    className="c-explorer__header__title"
-    href="/admin/pages/a/"
-    onClick={[MockFunction]}
-  >
-    <div
-      className="c-explorer__header__title__inner"
-    >
-      <Icon
-        className="icon--explorer-header"
-        name="arrow-left"
-      />
-      <span>
-        test
-      </span>
-    </div>
-  </Button>
-</div>
-`;
-
-exports[`ExplorerHeader basic 1`] = `
-<div
-  className="c-explorer__header"
->
-  <Button
-    className="c-explorer__header__title"
-    href="/admin/pages/undefined/"
-    onClick={[MockFunction]}
-  >
-    <div
-      className="c-explorer__header__title__inner"
-    >
-      <Icon
-        className="icon--explorer-header"
-        name="arrow-left"
-      />
-      <span>
-        Pages
-      </span>
-    </div>
-  </Button>
-</div>
-`;

+ 0 - 180
client/src/components/Explorer/__snapshots__/ExplorerItem.test.js.snap

@@ -1,180 +0,0 @@
-// Jest Snapshot v1, https://goo.gl/fbAQLP
-
-exports[`ExplorerItem children 1`] = `
-<div
-  className="c-explorer__item"
->
-  <Button
-    className="c-explorer__item__link"
-    href="/admin/pages/5/"
-  >
-    <Icon
-      className="icon--menuitem"
-      name="folder-inverse"
-    />
-    <h3
-      className="c-explorer__item__title"
-    >
-      test
-    </h3>
-  </Button>
-  <Button
-    className="c-explorer__item__action c-explorer__item__action--small"
-    href="/admin/pages/5/edit/"
-  >
-    <Icon
-      className="icon--item-action"
-      name="edit"
-      title="Edit 'test'"
-    />
-  </Button>
-  <Button
-    className="c-explorer__item__action"
-    href="/admin/pages/5/"
-    onClick={[Function]}
-  >
-    <Icon
-      className="icon--item-action"
-      name="arrow-right"
-      title="View child pages of 'test'"
-    />
-  </Button>
-</div>
-`;
-
-exports[`ExplorerItem renders 1`] = `
-<div
-  className="c-explorer__item"
->
-  <Button
-    className="c-explorer__item__link"
-    href="/admin/pages/5/"
-  >
-    <h3
-      className="c-explorer__item__title"
-    >
-      test
-    </h3>
-  </Button>
-  <Button
-    className="c-explorer__item__action c-explorer__item__action--small"
-    href="/admin/pages/5/edit/"
-  >
-    <Icon
-      className="icon--item-action"
-      name="edit"
-      title="Edit 'test'"
-    />
-  </Button>
-</div>
-`;
-
-exports[`ExplorerItem should show a publication status if not live 1`] = `
-<div
-  className="c-explorer__item"
->
-  <Button
-    className="c-explorer__item__link"
-    href="/admin/pages/5/"
-  >
-    <Icon
-      className="icon--menuitem"
-      name="folder-inverse"
-    />
-    <h3
-      className="c-explorer__item__title"
-    >
-      test
-    </h3>
-    <span
-      className="c-explorer__meta"
-    >
-      <PublicationStatus
-        status={
-          Object {
-            "has_unpublished_changes": true,
-            "live": false,
-            "status": "test",
-          }
-        }
-      />
-    </span>
-  </Button>
-  <Button
-    className="c-explorer__item__action c-explorer__item__action--small"
-    href="/admin/pages/5/edit/"
-  >
-    <Icon
-      className="icon--item-action"
-      name="edit"
-      title="Edit 'test'"
-    />
-  </Button>
-  <Button
-    className="c-explorer__item__action"
-    href="/admin/pages/5/"
-    onClick={[Function]}
-  >
-    <Icon
-      className="icon--item-action"
-      name="arrow-right"
-      title="View child pages of 'test'"
-    />
-  </Button>
-</div>
-`;
-
-exports[`ExplorerItem should show a publication status with unpublished changes 1`] = `
-<div
-  className="c-explorer__item"
->
-  <Button
-    className="c-explorer__item__link"
-    href="/admin/pages/5/"
-  >
-    <Icon
-      className="icon--menuitem"
-      name="folder-inverse"
-    />
-    <h3
-      className="c-explorer__item__title"
-    >
-      test
-    </h3>
-    <span
-      className="c-explorer__meta"
-    >
-      <PublicationStatus
-        status={
-          Object {
-            "has_unpublished_changes": true,
-            "live": true,
-            "status": "test",
-          }
-        }
-      />
-    </span>
-  </Button>
-  <Button
-    className="c-explorer__item__action c-explorer__item__action--small"
-    href="/admin/pages/5/edit/"
-  >
-    <Icon
-      className="icon--item-action"
-      name="edit"
-      title="Edit 'test'"
-    />
-  </Button>
-  <Button
-    className="c-explorer__item__action"
-    href="/admin/pages/5/"
-    onClick={[Function]}
-  >
-    <Icon
-      className="icon--item-action"
-      name="arrow-right"
-      title="View child pages of 'test'"
-    />
-  </Button>
-</div>
-`;

+ 0 - 349
client/src/components/Explorer/__snapshots__/ExplorerPanel.test.js.snap

@@ -1,349 +0,0 @@
-// Jest Snapshot v1, https://goo.gl/fbAQLP
-
-exports[`ExplorerPanel general rendering #isError 1`] = `
-<FocusTrap
-  _createFocusTrap={[Function]}
-  active={true}
-  focusTrapOptions={
-    Object {
-      "initialFocus": ".c-explorer__header__title",
-      "onDeactivate": [MockFunction],
-    }
-  }
-  paused={false}
->
-  <div
-    className="explorer"
-    role="dialog"
-  >
-    <Button
-      className="c-explorer__close"
-    >
-      Close explorer
-    </Button>
-    <Transition
-      className="c-explorer"
-      component="nav"
-      duration={210}
-      label="Page explorer"
-      name="push"
-    >
-      <div
-        className="c-transition-group"
-        key="1"
-      >
-        <ExplorerHeader
-          depth={1}
-          gotoPage={[MockFunction]}
-          onClick={[Function]}
-          page={
-            Object {
-              "children": Object {
-                "items": Array [],
-              },
-              "isError": true,
-              "meta": Object {
-                "parent": null,
-              },
-            }
-          }
-        />
-        <div
-          className="c-explorer__drawer"
-        >
-          <div
-            key="children"
-          />
-          <div
-            className="c-explorer__placeholder"
-            key="error"
-          >
-            Server Error
-          </div>
-        </div>
-        <PageCount
-          page={
-            Object {
-              "children": Object {
-                "items": Array [],
-              },
-              "isError": true,
-              "meta": Object {
-                "parent": null,
-              },
-            }
-          }
-        />
-      </div>
-    </Transition>
-  </div>
-</FocusTrap>
-`;
-
-exports[`ExplorerPanel general rendering #isFetching 1`] = `
-<FocusTrap
-  _createFocusTrap={[Function]}
-  active={true}
-  focusTrapOptions={
-    Object {
-      "initialFocus": ".c-explorer__header__title",
-      "onDeactivate": [MockFunction],
-    }
-  }
-  paused={false}
->
-  <div
-    className="explorer"
-    role="dialog"
-  >
-    <Button
-      className="c-explorer__close"
-    >
-      Close explorer
-    </Button>
-    <Transition
-      className="c-explorer"
-      component="nav"
-      duration={210}
-      label="Page explorer"
-      name="push"
-    >
-      <div
-        className="c-transition-group"
-        key="1"
-      >
-        <ExplorerHeader
-          depth={1}
-          gotoPage={[MockFunction]}
-          onClick={[Function]}
-          page={
-            Object {
-              "children": Object {
-                "items": Array [],
-              },
-              "isFetching": true,
-              "meta": Object {
-                "parent": null,
-              },
-            }
-          }
-        />
-        <div
-          className="c-explorer__drawer"
-        >
-          <div
-            key="children"
-          />
-        </div>
-      </div>
-    </Transition>
-  </div>
-</FocusTrap>
-`;
-
-exports[`ExplorerPanel general rendering #items 1`] = `
-<FocusTrap
-  _createFocusTrap={[Function]}
-  active={true}
-  focusTrapOptions={
-    Object {
-      "initialFocus": ".c-explorer__header__title",
-      "onDeactivate": [MockFunction],
-    }
-  }
-  paused={false}
->
-  <div
-    className="explorer"
-    role="dialog"
-  >
-    <Button
-      className="c-explorer__close"
-    >
-      Close explorer
-    </Button>
-    <Transition
-      className="c-explorer"
-      component="nav"
-      duration={210}
-      label="Page explorer"
-      name="push"
-    >
-      <div
-        className="c-transition-group"
-        key="1"
-      >
-        <ExplorerHeader
-          depth={1}
-          gotoPage={[MockFunction]}
-          onClick={[Function]}
-          page={
-            Object {
-              "children": Object {
-                "items": Array [
-                  1,
-                  2,
-                ],
-              },
-            }
-          }
-        />
-        <div
-          className="c-explorer__drawer"
-        >
-          <div
-            key="children"
-          >
-            <ExplorerItem
-              item={
-                Object {
-                  "admin_display_title": "Test",
-                  "id": 1,
-                  "meta": Object {
-                    "status": Object {},
-                    "type": "test",
-                  },
-                }
-              }
-              key="1"
-              onClick={[Function]}
-            />
-            <ExplorerItem
-              item={
-                Object {
-                  "admin_display_title": "Foo",
-                  "id": 2,
-                  "meta": Object {
-                    "status": Object {},
-                    "type": "foo",
-                  },
-                }
-              }
-              key="2"
-              onClick={[Function]}
-            />
-          </div>
-        </div>
-      </div>
-    </Transition>
-  </div>
-</FocusTrap>
-`;
-
-exports[`ExplorerPanel general rendering no children 1`] = `
-<FocusTrap
-  _createFocusTrap={[Function]}
-  active={true}
-  focusTrapOptions={
-    Object {
-      "initialFocus": ".c-explorer__header__title",
-      "onDeactivate": [MockFunction],
-    }
-  }
-  paused={false}
->
-  <div
-    className="explorer"
-    role="dialog"
-  >
-    <Button
-      className="c-explorer__close"
-    >
-      Close explorer
-    </Button>
-    <Transition
-      className="c-explorer"
-      component="nav"
-      duration={210}
-      label="Page explorer"
-      name="push"
-    >
-      <div
-        className="c-transition-group"
-        key="1"
-      >
-        <ExplorerHeader
-          depth={1}
-          gotoPage={[MockFunction]}
-          onClick={[Function]}
-          page={
-            Object {
-              "children": Object {},
-            }
-          }
-        />
-        <div
-          className="c-explorer__drawer"
-        >
-          <div
-            className="c-explorer__placeholder"
-            key="empty"
-          >
-            No results
-          </div>
-        </div>
-      </div>
-    </Transition>
-  </div>
-</FocusTrap>
-`;
-
-exports[`ExplorerPanel general rendering renders 1`] = `
-<FocusTrap
-  _createFocusTrap={[Function]}
-  active={true}
-  focusTrapOptions={
-    Object {
-      "initialFocus": ".c-explorer__header__title",
-      "onDeactivate": [MockFunction],
-    }
-  }
-  paused={false}
->
-  <div
-    className="explorer"
-    role="dialog"
-  >
-    <Button
-      className="c-explorer__close"
-    >
-      Close explorer
-    </Button>
-    <Transition
-      className="c-explorer"
-      component="nav"
-      duration={210}
-      label="Page explorer"
-      name="push"
-    >
-      <div
-        className="c-transition-group"
-        key="1"
-      >
-        <ExplorerHeader
-          depth={1}
-          gotoPage={[MockFunction]}
-          onClick={[Function]}
-          page={
-            Object {
-              "children": Object {
-                "items": Array [],
-              },
-              "meta": Object {
-                "parent": null,
-              },
-            }
-          }
-        />
-        <div
-          className="c-explorer__drawer"
-        >
-          <div
-            key="children"
-          />
-        </div>
-      </div>
-    </Transition>
-  </div>
-</FocusTrap>
-`;

+ 0 - 20
client/src/components/Explorer/__snapshots__/ExplorerToggle.test.js.snap

@@ -1,20 +0,0 @@
-// Jest Snapshot v1, https://goo.gl/fbAQLP
-
-exports[`ExplorerToggle basic 1`] = `
-<Button
-  dialogTrigger={true}
-  onClick={[Function]}
->
-  <Icon
-    className="icon--menuitem"
-    name="folder-open-inverse"
-  />
-  <span>
-    To infinity and beyond!
-  </span>
-  <Icon
-    className="icon--submenu-trigger"
-    name="arrow-right"
-  />
-</Button>
-`;

+ 0 - 31
client/src/components/Explorer/__snapshots__/PageCount.test.js.snap

@@ -1,31 +0,0 @@
-// Jest Snapshot v1, https://goo.gl/fbAQLP
-
-exports[`PageCount plural 1`] = `
-<a
-  className="c-explorer__see-more"
-  href="/admin/pages/1/"
->
-  See all
-  <span>
-     5 pages
-  </span>
-  <Icon
-    name="arrow-right"
-  />
-</a>
-`;
-
-exports[`PageCount works 1`] = `
-<a
-  className="c-explorer__see-more"
-  href="/admin/pages/1/"
->
-  See all
-  <span>
-     1 page
-  </span>
-  <Icon
-    name="arrow-right"
-  />
-</a>
-`;

+ 0 - 109
client/src/components/Explorer/__snapshots__/actions.test.js.snap

@@ -1,109 +0,0 @@
-// Jest Snapshot v1, https://goo.gl/fbAQLP
-
-exports[`actions gotoPage creates action 1`] = `
-Array [
-  Object {
-    "payload": Object {
-      "id": 5,
-      "transition": 1,
-    },
-    "type": "GOTO_PAGE",
-  },
-  Object {
-    "payload": Object {
-      "id": 5,
-    },
-    "type": "GET_CHILDREN_START",
-  },
-  Object {
-    "payload": Object {
-      "id": 5,
-    },
-    "type": "GET_TRANSLATIONS_START",
-  },
-]
-`;
-
-exports[`actions gotoPage triggers getChildren 1`] = `
-Array [
-  Object {
-    "payload": Object {
-      "id": 5,
-      "transition": 1,
-    },
-    "type": "GOTO_PAGE",
-  },
-  Object {
-    "payload": Object {
-      "id": 5,
-    },
-    "type": "GET_CHILDREN_START",
-  },
-  Object {
-    "payload": Object {
-      "id": 5,
-    },
-    "type": "GET_TRANSLATIONS_START",
-  },
-]
-`;
-
-exports[`actions toggleExplorer close 1`] = `
-Array [
-  Object {
-    "payload": undefined,
-    "type": "CLOSE_EXPLORER",
-  },
-]
-`;
-
-exports[`actions toggleExplorer open 1`] = `
-Array [
-  Object {
-    "payload": Object {
-      "id": 5,
-    },
-    "type": "OPEN_EXPLORER",
-  },
-]
-`;
-
-exports[`actions toggleExplorer open at root 1`] = `
-Array [
-  Object {
-    "payload": Object {
-      "id": 1,
-    },
-    "type": "OPEN_EXPLORER",
-  },
-  Object {
-    "payload": Object {
-      "id": 1,
-    },
-    "type": "GET_CHILDREN_START",
-  },
-]
-`;
-
-exports[`actions toggleExplorer open first time 1`] = `
-Array [
-  Object {
-    "payload": Object {
-      "id": 5,
-    },
-    "type": "OPEN_EXPLORER",
-  },
-  Object {
-    "payload": Object {
-      "id": 5,
-    },
-    "type": "GET_CHILDREN_START",
-  },
-  Object {
-    "payload": Object {
-      "id": 5,
-    },
-    "type": "GET_TRANSLATIONS_START",
-  },
-]
-`;

+ 0 - 87
client/src/components/Explorer/actions.test.js

@@ -1,87 +0,0 @@
-import configureMockStore from 'redux-mock-store';
-import thunk from 'redux-thunk';
-
-import * as actions from './actions';
-
-const middlewares = [thunk];
-const mockStore = configureMockStore(middlewares);
-
-const stubState = {
-  explorer: {
-    isVisible: true,
-  },
-  nodes: {
-    5: {
-      isFetching: true,
-      children: {},
-    },
-  },
-};
-
-describe('actions', () => {
-  describe('closeExplorer', () => {
-    it('exists', () => {
-      expect(actions.closeExplorer).toBeDefined();
-    });
-
-    it('creates action', () => {
-      expect(actions.closeExplorer().type).toEqual('CLOSE_EXPLORER');
-    });
-  });
-
-  describe('toggleExplorer', () => {
-    it('exists', () => {
-      expect(actions.toggleExplorer).toBeDefined();
-    });
-
-    it('close', () => {
-      const store = mockStore(stubState);
-      store.dispatch(actions.toggleExplorer(5));
-      expect(store.getActions()).toMatchSnapshot();
-    });
-
-    it('open', () => {
-      const stub = Object.assign({}, stubState);
-      stub.explorer.isVisible = false;
-      const store = mockStore(stub);
-      store.dispatch(actions.toggleExplorer(5));
-      expect(store.getActions()).toMatchSnapshot();
-    });
-
-    it('open first time', () => {
-      const stub = { explorer: stubState.explorer, nodes: {} };
-      stub.explorer.isVisible = false;
-      const store = mockStore(stub);
-      store.dispatch(actions.toggleExplorer(5));
-      expect(store.getActions()).toMatchSnapshot();
-    });
-
-    it('open at root', () => {
-      const stub = Object.assign({}, stubState);
-      stub.explorer.isVisible = false;
-      const store = mockStore(stub);
-      store.dispatch(actions.toggleExplorer(1));
-      expect(store.getActions()).toMatchSnapshot();
-    });
-  });
-
-  describe('gotoPage', () => {
-    it('exists', () => {
-      expect(actions.gotoPage).toBeDefined();
-    });
-
-    it('creates action', () => {
-      const store = mockStore(stubState);
-      store.dispatch(actions.gotoPage(5, 1));
-      expect(store.getActions()).toMatchSnapshot();
-    });
-
-    it('triggers getChildren', () => {
-      const stub = Object.assign({}, stubState);
-      stub.nodes[5].isFetching = false;
-      const store = mockStore(stub);
-      store.dispatch(actions.gotoPage(5, 1));
-      expect(store.getActions()).toMatchSnapshot();
-    });
-  });
-});

+ 0 - 157
client/src/components/Explorer/actions.ts

@@ -1,157 +0,0 @@
-import { ThunkAction } from 'redux-thunk';
-
-import * as admin from '../../api/admin';
-import { createAction } from '../../utils/actions';
-import { MAX_EXPLORER_PAGES } from '../../config/wagtailConfig';
-
-import { State, Action } from './reducers';
-
-type ThunkActionType = ThunkAction<void, State, unknown, Action>;
-
-const getPageSuccess = createAction(
-  'GET_PAGE_SUCCESS',
-  (id: number, data: admin.WagtailPageAPI) => ({ id, data }),
-);
-const getPageFailure = createAction(
-  'GET_PAGE_FAILURE',
-  (id: number, error: Error) => ({ id, error }),
-);
-
-/**
- * Gets a page from the API.
- */
-function getPage(id: number): ThunkActionType {
-  return (dispatch) =>
-    admin.getPage(id).then(
-      (data) => {
-        dispatch(getPageSuccess(id, data));
-      },
-      (error) => {
-        dispatch(getPageFailure(id, error));
-      },
-    );
-}
-
-const getChildrenStart = createAction('GET_CHILDREN_START', (id: number) => ({
-  id,
-}));
-const getChildrenSuccess = createAction(
-  'GET_CHILDREN_SUCCESS',
-  (id, items: admin.WagtailPageAPI[], meta: any) => ({ id, items, meta }),
-);
-const getChildrenFailure = createAction(
-  'GET_CHILDREN_FAILURE',
-  (id: number, error: Error) => ({ id, error }),
-);
-
-/**
- * Gets the children of a node from the API.
- */
-function getChildren(id: number, offset = 0): ThunkActionType {
-  return (dispatch) => {
-    dispatch(getChildrenStart(id));
-
-    return admin
-      .getPageChildren(id, {
-        offset: offset,
-      })
-      .then(
-        ({ items, meta }) => {
-          const nbPages = offset + items.length;
-          dispatch(getChildrenSuccess(id, items, meta));
-
-          // Load more pages if necessary. Only one request is created even though
-          // more might be needed, thus naturally throttling the loading.
-          if (nbPages < meta.total_count && nbPages < MAX_EXPLORER_PAGES) {
-            dispatch(getChildren(id, nbPages));
-          }
-        },
-        (error) => {
-          dispatch(getChildrenFailure(id, error));
-        },
-      );
-  };
-}
-
-const getTranslationsStart = createAction('GET_TRANSLATIONS_START', (id) => ({
-  id,
-}));
-const getTranslationsSuccess = createAction(
-  'GET_TRANSLATIONS_SUCCESS',
-  (id, items) => ({ id, items }),
-);
-const getTranslationsFailure = createAction(
-  'GET_TRANSLATIONS_FAILURE',
-  (id, error) => ({ id, error }),
-);
-
-/**
- * Gets the translations of a node from the API.
- */
-function getTranslations(id) {
-  return (dispatch) => {
-    dispatch(getTranslationsStart(id));
-
-    return admin.getAllPageTranslations(id, { onlyWithChildren: true }).then(
-      (items) => {
-        dispatch(getTranslationsSuccess(id, items));
-      },
-      (error) => {
-        dispatch(getTranslationsFailure(id, error));
-      },
-    );
-  };
-}
-
-const openExplorer = createAction('OPEN_EXPLORER', (id) => ({ id }));
-export const closeExplorer = createAction('CLOSE_EXPLORER');
-
-export function toggleExplorer(id: number): ThunkActionType {
-  return (dispatch, getState) => {
-    const { explorer, nodes } = getState();
-
-    if (explorer.isVisible) {
-      dispatch(closeExplorer());
-    } else {
-      const page = nodes[id];
-
-      dispatch(openExplorer(id));
-
-      if (!page) {
-        dispatch(getChildren(id));
-
-        if (id !== 1) {
-          dispatch(getTranslations(id));
-        }
-      }
-
-      // We need to get the title of the starting page, only if it is not the site's root.
-      const isNotRoot = id !== 1;
-      if (isNotRoot) {
-        dispatch(getPage(id));
-      }
-    }
-  };
-}
-
-const gotoPagePrivate = createAction(
-  'GOTO_PAGE',
-  (id: number, transition: number) => ({ id, transition }),
-);
-
-export function gotoPage(id: number, transition: number): ThunkActionType {
-  return (dispatch, getState) => {
-    const { nodes } = getState();
-    const page = nodes[id];
-
-    dispatch(gotoPagePrivate(id, transition));
-
-    if (page && !page.isFetchingChildren && !(page.children.count > 0)) {
-      dispatch(getChildren(id));
-    }
-
-    if (page && !page.isFetchingTranslations && page.translations == null) {
-      dispatch(getTranslations(id));
-    }
-  };
-}

+ 0 - 29
client/src/components/Explorer/index.test.js

@@ -1,29 +0,0 @@
-import Explorer, { ExplorerToggle, initExplorer } from './index';
-
-describe('Explorer index', () => {
-  it('exists', () => {
-    expect(Explorer).toBeDefined();
-  });
-
-  describe('ExplorerToggle', () => {
-    it('exists', () => {
-      expect(ExplorerToggle).toBeDefined();
-    });
-  });
-
-  describe('initExplorer', () => {
-    it('exists', () => {
-      expect(initExplorer).toBeInstanceOf(Function);
-    });
-
-    it('works', () => {
-      document.body.innerHTML =
-        '<div><div id="e"></div><div id="t">Test</div></div>';
-      const explorerNode = document.querySelector('#e');
-      const toggleNode = document.querySelector('#t');
-
-      initExplorer(explorerNode, toggleNode);
-      expect(document.body.innerHTML).toContain('href');
-    });
-  });
-});

+ 0 - 65
client/src/components/Explorer/index.tsx

@@ -1,65 +0,0 @@
-import React from 'react';
-import ReactDOM from 'react-dom';
-import { Provider } from 'react-redux';
-import { createStore, combineReducers, applyMiddleware, compose } from 'redux';
-import thunkMiddleware from 'redux-thunk';
-
-// import { perfMiddleware } from '../../utils/performance';
-import Explorer from './Explorer';
-import ExplorerToggle from './ExplorerToggle';
-import explorer from './reducers/explorer';
-import nodes from './reducers/nodes';
-
-/**
- * Initialises the explorer component on the given nodes.
- */
-const initExplorer = (explorerNode, toggleNode) => {
-  const rootReducer = combineReducers({
-    explorer,
-    nodes,
-  });
-
-  const middleware = [thunkMiddleware];
-
-  // Uncomment this to use performance measurements.
-  // if (process.env.NODE_ENV !== 'production') {
-  //   middleware.push(perfMiddleware);
-  // }
-
-  const store = createStore(
-    rootReducer,
-    {},
-    compose(
-      applyMiddleware(...middleware),
-      // Expose store to Redux DevTools extension.
-      window.__REDUX_DEVTOOLS_EXTENSION__
-        ? window.__REDUX_DEVTOOLS_EXTENSION__()
-        : (func) => func,
-    ),
-  );
-
-  const startPage = parseInt(
-    toggleNode.getAttribute('data-explorer-start-page'),
-    10,
-  );
-
-  ReactDOM.render(
-    <Provider store={store}>
-      <ExplorerToggle startPage={startPage}>
-        {toggleNode.textContent}
-      </ExplorerToggle>
-    </Provider>,
-    toggleNode.parentNode,
-  );
-
-  ReactDOM.render(
-    <Provider store={store}>
-      <Explorer />
-    </Provider>,
-    explorerNode,
-  );
-};
-
-export default Explorer;
-
-export { ExplorerToggle, initExplorer };

+ 0 - 25
client/src/components/Explorer/reducers/__snapshots__/explorer.test.js.snap

@@ -1,25 +0,0 @@
-// Jest Snapshot v1, https://goo.gl/fbAQLP
-
-exports[`explorer OPEN_EXPLORER 1`] = `
-Object {
-  "currentPageId": 1,
-  "depth": 0,
-  "isVisible": true,
-}
-`;
-
-exports[`explorer POP_PAGE 1`] = `
-Object {
-  "currentPageId": null,
-  "depth": 0,
-  "isVisible": false,
-}
-`;
-
-exports[`explorer PUSH_PAGE 1`] = `
-Object {
-  "currentPageId": null,
-  "depth": 0,
-  "isVisible": false,
-}
-`;

+ 0 - 208
client/src/components/Explorer/reducers/__snapshots__/nodes.test.js.snap

@@ -1,208 +0,0 @@
-// Jest Snapshot v1, https://goo.gl/fbAQLP
-
-exports[`nodes GET_CHILDREN_FAILURE 1`] = `
-Object {
-  "1": Object {
-    "children": Object {
-      "count": 0,
-      "items": Array [],
-    },
-    "id": 0,
-    "isError": true,
-    "isFetchingChildren": false,
-    "isFetchingTranslations": true,
-    "meta": Object {
-      "children": Object {},
-      "parent": null,
-      "status": Object {
-        "has_unpublished_changes": true,
-        "live": false,
-        "status": "",
-      },
-    },
-  },
-}
-`;
-
-exports[`nodes GET_CHILDREN_START 1`] = `
-Object {
-  "1": Object {
-    "children": Object {
-      "count": 0,
-      "items": Array [],
-    },
-    "id": 0,
-    "isError": false,
-    "isFetchingChildren": true,
-    "isFetchingTranslations": false,
-    "meta": Object {
-      "children": Object {},
-      "parent": null,
-      "status": Object {
-        "has_unpublished_changes": true,
-        "live": false,
-        "status": "",
-      },
-    },
-  },
-}
-`;
-
-exports[`nodes GET_CHILDREN_SUCCESS 1`] = `
-Object {
-  "1": Object {
-    "children": Object {
-      "count": 3,
-      "items": Array [
-        3,
-        4,
-        5,
-      ],
-    },
-    "id": 0,
-    "isError": false,
-    "isFetchingChildren": false,
-    "isFetchingTranslations": false,
-    "meta": Object {
-      "children": Object {},
-      "parent": null,
-      "status": Object {
-        "has_unpublished_changes": true,
-        "live": false,
-        "status": "",
-      },
-    },
-  },
-  "3": Object {
-    "children": Object {
-      "count": 0,
-      "items": Array [],
-    },
-    "id": 3,
-    "isError": false,
-    "isFetchingChildren": false,
-    "isFetchingTranslations": false,
-    "meta": Object {
-      "children": Object {},
-      "parent": null,
-      "status": Object {
-        "has_unpublished_changes": true,
-        "live": false,
-        "status": "",
-      },
-    },
-  },
-  "4": Object {
-    "children": Object {
-      "count": 0,
-      "items": Array [],
-    },
-    "id": 4,
-    "isError": false,
-    "isFetchingChildren": false,
-    "isFetchingTranslations": false,
-    "meta": Object {
-      "children": Object {},
-      "parent": null,
-      "status": Object {
-        "has_unpublished_changes": true,
-        "live": false,
-        "status": "",
-      },
-    },
-  },
-  "5": Object {
-    "children": Object {
-      "count": 0,
-      "items": Array [],
-    },
-    "id": 5,
-    "isError": false,
-    "isFetchingChildren": false,
-    "isFetchingTranslations": false,
-    "meta": Object {
-      "children": Object {},
-      "parent": null,
-      "status": Object {
-        "has_unpublished_changes": true,
-        "live": false,
-        "status": "",
-      },
-    },
-  },
-}
-`;
-
-exports[`nodes GET_PAGE_FAILURE 1`] = `
-Object {
-  "1": Object {
-    "children": Object {
-      "count": 0,
-      "items": Array [],
-    },
-    "id": 0,
-    "isError": true,
-    "isFetchingChildren": false,
-    "isFetchingTranslations": true,
-    "meta": Object {
-      "children": Object {},
-      "parent": null,
-      "status": Object {
-        "has_unpublished_changes": true,
-        "live": false,
-        "status": "",
-      },
-    },
-  },
-}
-`;
-
-exports[`nodes GET_PAGE_SUCCESS 1`] = `
-Object {
-  "1": Object {
-    "children": Object {
-      "count": 0,
-      "items": Array [],
-    },
-    "id": 0,
-    "isError": false,
-    "isFetchingChildren": false,
-    "isFetchingTranslations": false,
-    "meta": Object {
-      "children": Object {},
-      "parent": null,
-      "status": Object {
-        "has_unpublished_changes": true,
-        "live": false,
-        "status": "",
-      },
-    },
-  },
-}
-`;
-
-exports[`nodes OPEN_EXPLORER 1`] = `
-Object {
-  "1": Object {
-    "children": Object {
-      "count": 0,
-      "items": Array [],
-    },
-    "id": 0,
-    "isError": false,
-    "isFetchingChildren": false,
-    "isFetchingTranslations": false,
-    "meta": Object {
-      "children": Object {},
-      "parent": null,
-      "status": Object {
-        "has_unpublished_changes": true,
-        "live": false,
-        "status": "",
-      },
-    },
-  },
-}
-`;
-
-exports[`nodes empty state 1`] = `Object {}`;

+ 0 - 39
client/src/components/Explorer/reducers/explorer.test.js

@@ -1,39 +0,0 @@
-import explorer from './explorer';
-
-describe('explorer', () => {
-  const initialState = explorer(undefined, {});
-
-  it('exists', () => {
-    expect(explorer).toBeDefined();
-  });
-
-  it('returns the initial state if no input is provided', () => {
-    expect(explorer(undefined, {})).toEqual(initialState);
-  });
-
-  it('OPEN_EXPLORER', () => {
-    const action = { type: 'OPEN_EXPLORER', payload: { id: 1 } };
-    expect(explorer(initialState, action)).toMatchSnapshot();
-  });
-
-  it('CLOSE_EXPLORER', () => {
-    expect(explorer(initialState, { type: 'CLOSE_EXPLORER' })).toEqual(
-      initialState,
-    );
-  });
-
-  it('PUSH_PAGE', () => {
-    expect(
-      explorer(initialState, { type: 'PUSH_PAGE', payload: { id: 100 } }),
-    ).toMatchSnapshot();
-  });
-
-  it('POP_PAGE', () => {
-    const state = explorer(initialState, {
-      type: 'PUSH_PAGE',
-      payload: { id: 100 },
-    });
-    const action = { type: 'POP_PAGE', payload: { id: 100 } };
-    expect(explorer(state, action)).toMatchSnapshot();
-  });
-});

+ 0 - 68
client/src/components/Explorer/reducers/explorer.ts

@@ -1,68 +0,0 @@
-export interface State {
-  isVisible: boolean;
-  depth: number;
-  currentPageId: number | null;
-}
-
-const defaultState: State = {
-  isVisible: false,
-  depth: 0,
-  currentPageId: null,
-};
-
-export const OPEN_EXPLORER = 'OPEN_EXPLORER';
-interface OpenExplorerAction {
-  type: typeof OPEN_EXPLORER;
-  payload: {
-    id: number;
-  };
-}
-
-export const CLOSE_EXPLORER = 'CLOSE_EXPLORER';
-interface CloseExplorerAction {
-  type: typeof CLOSE_EXPLORER;
-}
-
-export const GOTO_PAGE = 'GOTO_PAGE';
-interface GotoPageAction {
-  type: typeof GOTO_PAGE;
-  payload: {
-    id: number;
-    transition: number;
-  };
-}
-
-export type Action = OpenExplorerAction | CloseExplorerAction | GotoPageAction;
-
-/**
- * Oversees the state of the explorer. Defines:
- * - Where in the page tree the explorer is at.
- * - Whether the explorer is open or not.
- */
-export default function explorer(
-  prevState = defaultState,
-  action: Action,
-): State {
-  switch (action.type) {
-    case OPEN_EXPLORER:
-      // Provide a starting page when opening the explorer.
-      return {
-        isVisible: true,
-        depth: 0,
-        currentPageId: action.payload.id,
-      };
-
-    case CLOSE_EXPLORER:
-      return defaultState;
-
-    case GOTO_PAGE:
-      return {
-        isVisible: prevState.isVisible,
-        depth: prevState.depth + action.payload.transition,
-        currentPageId: action.payload.id,
-      };
-
-    default:
-      return prevState;
-  }
-}

+ 0 - 9
client/src/components/Explorer/reducers/index.ts

@@ -1,9 +0,0 @@
-import { State as ExplorerState, Action as ExplorerAction } from './explorer';
-import { State as NodeState, Action as NodeAction } from './nodes';
-
-export interface State {
-  explorer: ExplorerState;
-  nodes: NodeState;
-}
-
-export type Action = ExplorerAction | NodeAction;

+ 0 - 64
client/src/components/Explorer/reducers/nodes.test.js

@@ -1,64 +0,0 @@
-import nodes from './nodes';
-
-describe('nodes', () => {
-  const initialState = nodes(undefined, {});
-
-  it('exists', () => {
-    expect(nodes).toBeDefined();
-  });
-
-  it('empty state', () => {
-    expect(initialState).toMatchSnapshot();
-  });
-
-  it('OPEN_EXPLORER', () => {
-    const action = { type: 'OPEN_EXPLORER', payload: { id: 1 } };
-    expect(nodes(initialState, action)).toMatchSnapshot();
-  });
-
-  it('GET_PAGE_SUCCESS', () => {
-    const action = { type: 'GET_PAGE_SUCCESS', payload: { id: 1, data: {} } };
-    expect(nodes(initialState, action)).toMatchSnapshot();
-  });
-
-  it('GET_PAGE_FAILURE', () => {
-    const state = nodes(initialState, {
-      type: 'OPEN_EXPLORER',
-      payload: { id: 1 },
-    });
-    const action = { type: 'GET_PAGE_FAILURE', payload: { id: 1 } };
-    expect(nodes(state, action)).toMatchSnapshot();
-  });
-
-  it('GET_CHILDREN_START', () => {
-    const action = { type: 'GET_CHILDREN_START', payload: { id: 1 } };
-    expect(nodes(initialState, action)).toMatchSnapshot();
-  });
-
-  it('GET_CHILDREN_SUCCESS', () => {
-    const state = nodes(initialState, {
-      type: 'OPEN_EXPLORER',
-      payload: { id: 1 },
-    });
-    const action = {
-      type: 'GET_CHILDREN_SUCCESS',
-      payload: {
-        id: 1,
-        items: [{ id: 3 }, { id: 4 }, { id: 5 }],
-        meta: {
-          total_count: 3,
-        },
-      },
-    };
-    expect(nodes(state, action)).toMatchSnapshot();
-  });
-
-  it('GET_CHILDREN_FAILURE', () => {
-    const state = nodes(initialState, {
-      type: 'OPEN_EXPLORER',
-      payload: { id: 1 },
-    });
-    const action = { type: 'GET_CHILDREN_FAILURE', payload: { id: 1 } };
-    expect(nodes(state, action)).toMatchSnapshot();
-  });
-});

+ 0 - 239
client/src/components/Explorer/reducers/nodes.ts

@@ -1,239 +0,0 @@
-import { WagtailPageAPI } from '../../../api/admin';
-import { OPEN_EXPLORER, CLOSE_EXPLORER } from './explorer';
-
-export interface PageState extends WagtailPageAPI {
-  isFetchingChildren: boolean;
-  isFetchingTranslations: boolean;
-  isError: boolean;
-  children: {
-    items: any[];
-    count: number;
-  };
-  translations?: Map<string, number>;
-}
-
-const defaultPageState: PageState = {
-  id: 0,
-  isFetchingChildren: false,
-  isFetchingTranslations: false,
-  isError: false,
-  children: {
-    items: [],
-    count: 0,
-  },
-  meta: {
-    status: {
-      status: '',
-      live: false,
-      has_unpublished_changes: true,
-    },
-    parent: null,
-    children: {},
-  },
-};
-
-interface OpenExplorerAction {
-  type: typeof OPEN_EXPLORER;
-  payload: {
-    id: number;
-  };
-}
-
-interface CloseExplorerAction {
-  type: typeof CLOSE_EXPLORER;
-}
-
-export const GET_PAGE_SUCCESS = 'GET_PAGE_SUCCESS';
-interface GetPageSuccess {
-  type: typeof GET_PAGE_SUCCESS;
-  payload: {
-    id: number;
-    data: WagtailPageAPI;
-  };
-}
-
-export const GET_CHILDREN_START = 'GET_CHILDREN_START';
-interface GetChildrenStart {
-  type: typeof GET_CHILDREN_START;
-  payload: {
-    id: number;
-  };
-}
-
-export const GET_CHILDREN_SUCCESS = 'GET_CHILDREN_SUCCESS';
-interface GetChildrenSuccess {
-  type: typeof GET_CHILDREN_SUCCESS;
-  payload: {
-    id: number;
-    meta: {
-      total_count: number;
-    };
-    items: WagtailPageAPI[];
-  };
-}
-
-export const GET_TRANSLATIONS_START = 'GET_TRANSLATIONS_START';
-interface GetTranslationsStart {
-  type: typeof GET_TRANSLATIONS_START;
-  payload: {
-    id: number;
-  };
-}
-
-export const GET_TRANSLATIONS_SUCCESS = 'GET_TRANSLATIONS_SUCCESS';
-interface GetTranslationsSuccess {
-  type: typeof GET_TRANSLATIONS_SUCCESS;
-  payload: {
-    id: number;
-    meta: {
-      total_count: number;
-    };
-    items: WagtailPageAPI[];
-  };
-}
-
-export const GET_PAGE_FAILURE = 'GET_PAGE_FAILURE';
-interface GetPageFailure {
-  type: typeof GET_PAGE_FAILURE;
-  payload: {
-    id: number;
-  };
-}
-
-export const GET_CHILDREN_FAILURE = 'GET_CHILDREN_FAILURE';
-interface GetChildrenFailure {
-  type: typeof GET_CHILDREN_FAILURE;
-  payload: {
-    id: number;
-  };
-}
-
-export const GET_TRANSLATIONS_FAILURE = 'GET_TRANSLATIONS_FAILURE';
-interface GetTranslationsFailure {
-  type: typeof GET_TRANSLATIONS_FAILURE;
-  payload: {
-    id: number;
-  };
-}
-
-export type Action =
-  | OpenExplorerAction
-  | CloseExplorerAction
-  | GetPageSuccess
-  | GetChildrenStart
-  | GetChildrenSuccess
-  | GetTranslationsStart
-  | GetTranslationsSuccess
-  | GetPageFailure
-  | GetChildrenFailure
-  | GetTranslationsFailure;
-
-/**
- * A single page node in the explorer.
- */
-const node = (state = defaultPageState, action: Action): PageState => {
-  switch (action.type) {
-    case GET_PAGE_SUCCESS:
-      return Object.assign({}, state, action.payload.data, {
-        isError: false,
-      });
-
-    case GET_CHILDREN_START:
-      return Object.assign({}, state, {
-        isFetchingChildren: true,
-      });
-
-    case GET_TRANSLATIONS_START:
-      return Object.assign({}, state, {
-        isFetchingTranslations: true,
-      });
-
-    case GET_CHILDREN_SUCCESS:
-      return Object.assign({}, state, {
-        isFetchingChildren: false,
-        isError: false,
-        children: {
-          items: state.children.items
-            .slice()
-            .concat(action.payload.items.map((item) => item.id)),
-          count: action.payload.meta.total_count,
-        },
-      });
-
-    case GET_TRANSLATIONS_SUCCESS:
-      // eslint-disable-next-line no-case-declarations
-      const translations = new Map();
-
-      action.payload.items.forEach((item) => {
-        translations.set(item.meta.locale, item.id);
-      });
-
-      return Object.assign({}, state, {
-        isFetchingTranslations: false,
-        isError: false,
-        translations,
-      });
-
-    case GET_PAGE_FAILURE:
-    case GET_CHILDREN_FAILURE:
-    case GET_TRANSLATIONS_FAILURE:
-      return Object.assign({}, state, {
-        isFetchingChildren: false,
-        isFetchingTranslations: true,
-        isError: true,
-      });
-
-    default:
-      return state;
-  }
-};
-
-export interface State {
-  [id: number]: PageState;
-}
-
-const defaultState: State = {};
-
-/**
- * Contains all of the page nodes in one object.
- */
-export default function nodes(state = defaultState, action: Action) {
-  switch (action.type) {
-    case OPEN_EXPLORER: {
-      return Object.assign({}, state, {
-        [action.payload.id]: Object.assign({}, defaultPageState),
-      });
-    }
-
-    case GET_PAGE_SUCCESS:
-    case GET_CHILDREN_START:
-    case GET_TRANSLATIONS_START:
-    case GET_PAGE_FAILURE:
-    case GET_CHILDREN_FAILURE:
-    case GET_TRANSLATIONS_FAILURE:
-      return Object.assign({}, state, {
-        // Delegate logic to single-node reducer.
-        [action.payload.id]: node(state[action.payload.id], action),
-      });
-
-    case GET_CHILDREN_SUCCESS:
-    case GET_TRANSLATIONS_SUCCESS:
-      // eslint-disable-next-line no-case-declarations
-      const newState = Object.assign({}, state, {
-        [action.payload.id]: node(state[action.payload.id], action),
-      });
-
-      action.payload.items.forEach((item) => {
-        newState[item.id] = Object.assign({}, defaultPageState, item);
-      });
-
-      return newState;
-
-    case CLOSE_EXPLORER: {
-      return defaultState;
-    }
-
-    default:
-      return state;
-  }
-}

+ 1 - 1
client/src/components/PageExplorer/PageExplorerPanel.test.js

@@ -20,7 +20,7 @@ const mockProps = {
 describe('PageExplorerPanel', () => {
 describe('PageExplorerPanel', () => {
   describe('general rendering', () => {
   describe('general rendering', () => {
     beforeEach(() => {
     beforeEach(() => {
-      document.body.innerHTML = '<div data-explorer-menu-item></div>';
+      document.body.innerHTML = '<div></div>';
     });
     });
 
 
     it('exists', () => {
     it('exists', () => {

+ 8 - 0
client/src/components/Sidebar/Sidebar.scss

@@ -87,6 +87,14 @@
   &--mobile &__collapse-toggle {
   &--mobile &__collapse-toggle {
     display: none;
     display: none;
   }
   }
+
+  .icon--menuitem {
+    width: 1.25em;
+    height: 1.25em;
+    min-width: 1.25em;
+    margin-inline-end: 0.5em;
+    vertical-align: text-top;
+  }
 }
 }
 
 
 // This is a separate component as it needs to display in the header
 // This is a separate component as it needs to display in the header

+ 2 - 9
client/src/components/Sidebar/modules/MainMenu.scss

@@ -42,14 +42,6 @@
     @include show-focus-outline-inside;
     @include show-focus-outline-inside;
   }
   }
 
 
-  .icon--menuitem {
-    width: 1.25em;
-    height: 1.25em;
-    min-width: 1.25em;
-    margin-inline-end: 0.5em;
-    vertical-align: text-top;
-  }
-
   .icon--submenu-header {
   .icon--submenu-header {
     display: block;
     display: block;
     width: 4rem;
     width: 4rem;
@@ -134,7 +126,8 @@
     width: $menu-width !important; // Override collapsed style
     width: $menu-width !important; // Override collapsed style
 
 
     > ul {
     > ul {
-      max-height: $nav-footer-submenu-height;
+      $footer-submenu-height: 85px;
+      max-height: $footer-submenu-height;
       visibility: visible;
       visibility: visible;
     }
     }
   }
   }

+ 0 - 77
client/src/entrypoints/admin/core.js

@@ -224,83 +224,6 @@ $(() => {
   // Add class to the body from which transitions may be hung so they don't appear to transition as the page loads
   // Add class to the body from which transitions may be hung so they don't appear to transition as the page loads
   $('body').addClass('ready');
   $('body').addClass('ready');
 
 
-  // Enable toggle to open/close nav
-  $(document).on('click', '#nav-toggle', () => {
-    $('body').toggleClass('nav-open');
-    if (!$('body').hasClass('nav-open')) {
-      $('body').addClass('nav-closed');
-    } else {
-      $('body').removeClass('nav-closed');
-    }
-  });
-
-  // Enable toggle to open/close user settings
-  // eslint-disable-next-line func-names
-  $(document).on('click', '#account-settings', function () {
-    $('.nav-main').toggleClass('nav-main--open-footer');
-    $(this).find('em').toggleClass('icon-arrow-down-after icon-arrow-up-after');
-  });
-
-  // Resize nav to fit height of window. This is an unimportant bell/whistle to make it look nice
-  // eslint-disable-next-line func-names
-  const fitNav = function () {
-    $('.nav-wrapper').css('min-height', $(window).height());
-  };
-
-  fitNav();
-
-  $(window).on('resize', () => {
-    fitNav();
-  });
-
-  // Logo interactivity
-  function initLogo() {
-    const sensitivity = 8; // the amount of times the user must stroke the wagtail to trigger the animation
-
-    const $logoContainer = $('[data-animated-logo-container]');
-    let lastMouseX = 0;
-    let lastDir = '';
-    let dirChangeCount = 0;
-
-    function enableWag() {
-      $logoContainer.removeClass('logo-serious').addClass('logo-playful');
-    }
-
-    function disableWag() {
-      $logoContainer.removeClass('logo-playful').addClass('logo-serious');
-    }
-
-    $logoContainer.on('mousemove', (event) => {
-      const mouseX = event.pageX;
-      let dir;
-
-      if (mouseX > lastMouseX) {
-        dir = 'r';
-      } else if (mouseX < lastMouseX) {
-        dir = 'l';
-      }
-
-      if (dir !== lastDir && lastDir !== '') {
-        dirChangeCount += 1;
-      }
-
-      if (dirChangeCount > sensitivity) {
-        enableWag();
-      }
-
-      lastMouseX = mouseX;
-      lastDir = dir;
-    });
-
-    $logoContainer.on('mouseleave', () => {
-      dirChangeCount = 0;
-      disableWag();
-    });
-
-    disableWag();
-  }
-  initLogo();
-
   // Enable nice focus effects on all fields. This enables help text on hover.
   // Enable nice focus effects on all fields. This enables help text on hover.
   // eslint-disable-next-line func-names
   // eslint-disable-next-line func-names
   $(document).on('focus mouseover', 'input,textarea,select', function () {
   $(document).on('focus mouseover', 'input,textarea,select', function () {

+ 0 - 15
client/src/entrypoints/admin/sidebar-legacy.js

@@ -1,15 +0,0 @@
-import { initExplorer } from '../../components/Explorer';
-import { initSubmenus } from '../../includes/initSubmenus';
-import { initSkipLink } from '../../includes/initSkipLink';
-
-document.addEventListener('DOMContentLoaded', () => {
-  const explorerNode = document.querySelector('[data-explorer-menu]');
-  const toggleNode = document.querySelector('[data-explorer-start-page]');
-
-  if (explorerNode && toggleNode) {
-    initExplorer(explorerNode, toggleNode);
-  }
-
-  initSubmenus();
-  initSkipLink();
-});

+ 0 - 27
client/src/entrypoints/admin/sidebar-legacy.test.js

@@ -1,27 +0,0 @@
-jest.mock('../../components/Explorer');
-
-const Explorer = require('../../components/Explorer');
-
-document.addEventListener = jest.fn();
-
-require('./sidebar-legacy');
-
-describe('sidebar-legacy', () => {
-  const [event, listener] = document.addEventListener.mock.calls[0];
-
-  it('DOMContentLoaded', () => {
-    expect(event).toBe('DOMContentLoaded');
-  });
-
-  it('init', () => {
-    listener();
-    expect(Explorer.initExplorer).not.toHaveBeenCalled();
-  });
-
-  it('init with DOM', () => {
-    document.body.innerHTML =
-      '<div data-explorer-menu></div><div data-explorer-start-page></div>';
-    listener();
-    expect(Explorer.initExplorer).toHaveBeenCalled();
-  });
-});

+ 2 - 1
client/src/entrypoints/admin/wagtailadmin.js

@@ -1,6 +1,6 @@
 import React from 'react';
 import React from 'react';
 import ReactDOM from 'react-dom';
 import ReactDOM from 'react-dom';
-import { Icon, Portal, initUpgradeNotification } from '../..';
+import { Icon, Portal, initUpgradeNotification, initSkipLink } from '../..';
 
 
 if (process.env.NODE_ENV === 'development') {
 if (process.env.NODE_ENV === 'development') {
   // Run react-axe in development only, so it does not affect performance
   // Run react-axe in development only, so it does not affect performance
@@ -21,4 +21,5 @@ window.wagtail.components = {
  */
  */
 document.addEventListener('DOMContentLoaded', () => {
 document.addEventListener('DOMContentLoaded', () => {
   initUpgradeNotification();
   initUpgradeNotification();
+  initSkipLink();
 });
 });

+ 0 - 49
client/src/includes/initSubmenus.js

@@ -1,49 +0,0 @@
-/**
- * Initialises the Submenus within the primary Wagtail menu (excluding the Explorer menu)
- */
-
-const initSubmenus = () => {
-  const primaryNavContainer = document.querySelector('[data-nav-primary]');
-
-  if (!primaryNavContainer) {
-    return;
-  }
-
-  const subMenuTriggers = document.querySelectorAll(
-    '[data-nav-primary-submenu-trigger]',
-  );
-  const activeClass = 'submenu-active';
-
-  subMenuTriggers.forEach((subMenuTrigger) => {
-    subMenuTrigger.addEventListener('click', (clickEvent) => {
-      const submenuContainer = subMenuTrigger.parentNode;
-
-      primaryNavContainer.classList.remove(activeClass);
-      subMenuTriggers.forEach((sm) => sm.classList.remove(activeClass));
-
-      primaryNavContainer.classList.toggle(activeClass);
-      submenuContainer.classList.toggle(activeClass);
-
-      document.addEventListener('mousedown', (e) => {
-        if (
-          !submenuContainer.contains(e.target) &&
-          subMenuTrigger !== e.target
-        ) {
-          primaryNavContainer.classList.remove(activeClass);
-          submenuContainer.classList.remove(activeClass);
-        }
-      });
-
-      document.addEventListener('keydown', (e) => {
-        if (e.key === 'Escape') {
-          primaryNavContainer.classList.remove(activeClass);
-          submenuContainer.classList.remove(activeClass);
-        }
-      });
-
-      clickEvent.preventDefault();
-    });
-  });
-};
-
-export { initSubmenus };

+ 8 - 17
client/src/index.ts

@@ -3,20 +3,11 @@
  * Re-exports components and other modules via a cleaner API.
  * Re-exports components and other modules via a cleaner API.
  */
  */
 
 
-import Button from './components/Button/Button';
-import Icon from './components/Icon/Icon';
-import LoadingSpinner from './components/LoadingSpinner/LoadingSpinner';
-import Portal from './components/Portal/Portal';
-import PublicationStatus from './components/PublicationStatus/PublicationStatus';
-import Transition from './components/Transition/Transition';
-import { initUpgradeNotification } from './components/UpgradeNotification';
-
-export {
-  Button,
-  Icon,
-  LoadingSpinner,
-  Portal,
-  PublicationStatus,
-  Transition,
-  initUpgradeNotification,
-};
+export { default as Button } from './components/Button/Button';
+export { default as Icon } from './components/Icon/Icon';
+export { default as LoadingSpinner } from './components/LoadingSpinner/LoadingSpinner';
+export { default as Portal } from './components/Portal/Portal';
+export { default as PublicationStatus } from './components/PublicationStatus/PublicationStatus';
+export { default as Transition } from './components/Transition/Transition';
+export { initUpgradeNotification } from './components/UpgradeNotification';
+export { initSkipLink } from './includes/initSkipLink';

+ 0 - 1
client/storybook/preview.js

@@ -1,7 +1,6 @@
 import '../tests/stubs';
 import '../tests/stubs';
 
 
 import '../../wagtail/admin/static_src/wagtailadmin/scss/core.scss';
 import '../../wagtail/admin/static_src/wagtailadmin/scss/core.scss';
-import '../../wagtail/admin/static_src/wagtailadmin/scss/sidebar.scss';
 import './preview.scss';
 import './preview.scss';
 
 
 export const parameters = {
 export const parameters = {

+ 0 - 9
client/webpack.config.js

@@ -45,7 +45,6 @@ module.exports = function exports(env, argv) {
       'page-editor',
       'page-editor',
       'privacy-switch',
       'privacy-switch',
       'sidebar',
       'sidebar',
-      'sidebar-legacy',
       'task-chooser-modal',
       'task-chooser-modal',
       'task-chooser',
       'task-chooser',
       'telepath/blocks',
       'telepath/blocks',
@@ -187,14 +186,6 @@ module.exports = function exports(env, argv) {
     'panels',
     'panels',
     'streamfield.scss',
     'streamfield.scss',
   );
   );
-  sassEntry[getOutputPath('admin', 'css', 'sidebar')] = path.resolve(
-    'wagtail',
-    'admin',
-    'static_src',
-    'wagtailadmin',
-    'scss',
-    'sidebar.scss',
-  );
   sassEntry[getOutputPath('admin', 'css', 'userbar')] = path.resolve(
   sassEntry[getOutputPath('admin', 'css', 'userbar')] = path.resolve(
     'wagtail',
     'wagtail',
     'admin',
     'admin',

+ 0 - 9
docs/reference/settings.rst

@@ -237,15 +237,6 @@ If a user has not uploaded a profile picture, Wagtail will look for an avatar li
 
 
 Changes whether the Submit for Moderation button is displayed in the action menu.
 Changes whether the Submit for Moderation button is displayed in the action menu.
 
 
-``WAGTAIL_SLIM_SIDEBAR``
-------------------------
-
-.. code-block:: python
-
-  WAGTAIL_SLIM_SIDEBAR = False
-
-Disables Wagtail’s slim sidebar to use the legacy sidebar instead. The legacy sidebar and this setting will be removed in Wagtail 2.18.
-
 Comments
 Comments
 ========
 ========
 
 

+ 0 - 10
wagtail/admin/menu.py

@@ -10,8 +10,6 @@ from wagtail.coreutils import cautious_slugify
 
 
 
 
 class MenuItem(metaclass=MediaDefiningClass):
 class MenuItem(metaclass=MediaDefiningClass):
-    template = "wagtailadmin/shared/menu_item.html"
-
     def __init__(
     def __init__(
         self, label, url, name=None, classnames="", icon_name="", attrs=None, order=1000
         self, label, url, name=None, classnames="", icon_name="", attrs=None, order=1000
     ):
     ):
@@ -121,8 +119,6 @@ class Menu:
 
 
 
 
 class SubmenuMenuItem(MenuItem):
 class SubmenuMenuItem(MenuItem):
-    template = "wagtailadmin/shared/menu_submenu_item.html"
-
     """A MenuItem which wraps an inner Menu object"""
     """A MenuItem which wraps an inner Menu object"""
 
 
     def __init__(self, label, menu, **kwargs):
     def __init__(self, label, menu, **kwargs):
@@ -136,12 +132,6 @@ class SubmenuMenuItem(MenuItem):
     def is_active(self, request):
     def is_active(self, request):
         return bool(self.menu.active_menu_items(request))
         return bool(self.menu.active_menu_items(request))
 
 
-    def get_context(self, request):
-        context = super().get_context(request)
-        context["menu_html"] = self.menu.render_html(request)
-        context["request"] = request
-        return context
-
     def render_component(self, request):
     def render_component(self, request):
         return SubMenuItemComponent(
         return SubMenuItemComponent(
             self.name,
             self.name,

+ 6 - 1
wagtail/admin/static_src/wagtailadmin/scss/layouts/404.scss

@@ -28,10 +28,15 @@
   align-items: center;
   align-items: center;
 }
 }
 
 
-.wagtail-logo-container__desktop {
+.page404__logo {
   float: left;
   float: left;
   width: 400px;
   width: 400px;
   height: 500px;
   height: 500px;
+
+  // Media for Windows High Contrast mode
+  @media (forced-colors: $media-forced-colours) {
+    background-color: $system-color-link-text;
+  }
 }
 }
 
 
 .page404__text-container {
 .page404__text-container {

+ 0 - 1
wagtail/admin/static_src/wagtailadmin/scss/sidebar.scss

@@ -1 +0,0 @@
-@import '../../../../../client/scss/sidebar';

+ 3 - 1
wagtail/admin/templates/wagtailadmin/404.html

@@ -14,7 +14,9 @@
     <main class="page404__bg">
     <main class="page404__bg">
         <div class="w-w-full w-h-full w-max-w-6xl w-mx-auto w-flex w-items-center w-justify-center">
         <div class="w-w-full w-h-full w-max-w-6xl w-mx-auto w-flex w-items-center w-justify-center">
             {% block branding_logo %}
             {% block branding_logo %}
-                {% include "wagtailadmin/shared/animated_logo.html" %}
+                <div class="page404__logo w-hidden sm:w-block">
+                    <img src="{% versioned_static 'wagtailadmin/images/wagtail-logo.svg' %}" alt=""/>
+                </div>
             {% endblock %}
             {% endblock %}
 
 
             <div class="page404__text-container">
             <div class="page404__text-container">

+ 2 - 4
wagtail/admin/templates/wagtailadmin/admin_base.html

@@ -6,8 +6,6 @@
     <link rel="stylesheet" href="{% versioned_static 'wagtailadmin/css/vendor/jquery.tagit.css' %}">
     <link rel="stylesheet" href="{% versioned_static 'wagtailadmin/css/vendor/jquery.tagit.css' %}">
     <link rel="stylesheet" href="{% versioned_static 'wagtailadmin/css/core.css' %}" type="text/css" />
     <link rel="stylesheet" href="{% versioned_static 'wagtailadmin/css/core.css' %}" type="text/css" />
 
 
-    {% main_nav_css %}
-
     {% hook_output 'insert_global_admin_css' %}
     {% hook_output 'insert_global_admin_css' %}
 
 
     {% block extra_css %}{% endblock %}
     {% block extra_css %}{% endblock %}
@@ -54,8 +52,8 @@
     <script src="{% versioned_static 'wagtailadmin/js/core.js' %}"></script>
     <script src="{% versioned_static 'wagtailadmin/js/core.js' %}"></script>
     <script src="{% versioned_static 'wagtailadmin/js/vendor.js' %}"></script>
     <script src="{% versioned_static 'wagtailadmin/js/vendor.js' %}"></script>
     <script src="{% versioned_static 'wagtailadmin/js/wagtailadmin.js' %}"></script>
     <script src="{% versioned_static 'wagtailadmin/js/wagtailadmin.js' %}"></script>
-
-    {% main_nav_js %}
+    <script src="{% versioned_static 'wagtailadmin/js/telepath/telepath.js' %}"></script>
+    <script src="{% versioned_static 'wagtailadmin/js/sidebar.js' %}"></script>
 
 
     {% hook_output 'insert_global_admin_js' %}
     {% hook_output 'insert_global_admin_js' %}
 
 

+ 2 - 37
wagtail/admin/templates/wagtailadmin/base.html

@@ -2,41 +2,9 @@
 {% load wagtailadmin_tags wagtailcore_tags i18n %}
 {% load wagtailadmin_tags wagtailcore_tags i18n %}
 
 
 {% block furniture %}
 {% block furniture %}
-    {% slim_sidebar_enabled as slim_sidebar_enabled %}
     <template data-wagtail-sidebar-branding-logo>{% block branding_logo %}{% endblock %}</template>
     <template data-wagtail-sidebar-branding-logo>{% block branding_logo %}{% endblock %}</template>
-    {% if slim_sidebar_enabled %}
-        {% sidebar_props %}
-        <aside id="wagtail-sidebar" data-wagtail-sidebar></aside>
-    {% else %}
-        <aside id="wagtail-sidebar" class="nav-wrapper" data-nav-primary>
-            <div class="inner">
-                <a href="{% url 'wagtailadmin_home' %}" class="logo" aria-label="{% trans 'Dashboard' %}">
-                    {# Mobile-only logo: #}
-                    <div class="wagtail-logo-container__mobile u-hidden@sm">
-                        <img class="wagtail-logo wagtail-logo__full" src="{% versioned_static 'wagtailadmin/images/wagtail-logo.svg' %}" alt="" width="80" />
-                    </div>
-
-                    {# Desktop logo (animated): #}
-                    {% include "wagtailadmin/shared/animated_logo.html" %}
-                    <span class="u-hidden@sm">{% trans "Dashboard" %}</span>
-                </a>
-
-                {% menu_search %}
-                {% main_nav %}
-            </div>
-            <div class="explorer__wrapper" data-explorer-menu></div>
-        </aside>
-        {# Backwards-compatibility for branding_logo customisations in legacy sidebar. #}
-        {# RemovedInWagtail40Warning Remove when removing the legacy sidebar. #}
-        <script>
-            const branding_logo = document.querySelector('[data-wagtail-sidebar-branding-logo]');
-            const legacySidebar = document.querySelector('[data-nav-primary]');
-            if (branding_logo && branding_logo.innerHTML && legacySidebar) {
-                const link = legacySidebar.querySelector('a');
-                link.innerHTML = `${branding_logo.innerHTML}<span class="u-hidden@sm">{% trans "Dashboard" %}</span>`;
-            }
-        </script>
-    {% endif %}
+    {% sidebar_props %}
+    <aside id="wagtail-sidebar" data-wagtail-sidebar></aside>
 
 
     <main class="content-wrapper" id="main">
     <main class="content-wrapper" id="main">
         <div class="content">
         <div class="content">
@@ -64,9 +32,6 @@
                 {% endif %}
                 {% endif %}
             </div>
             </div>
 
 
-            {% if not slim_sidebar_enabled %}
-                <button type="button" id="nav-toggle" class="nav-toggle icon button unbutton" ><span class="visuallyhidden">{% trans "Toggle sidebar" %}</span></button>
-            {% endif %}
             {% block content %}{% endblock %}
             {% block content %}{% endblock %}
         </div>
         </div>
     </main>
     </main>

+ 0 - 5
wagtail/admin/templates/wagtailadmin/shared/animated_logo.html

@@ -1,5 +0,0 @@
-{% load wagtailadmin_tags %}
-
-<div class="wagtail-logo-container__desktop w-hidden sm:w-block">
-    <img src="{% versioned_static 'wagtailadmin/images/wagtail-logo.svg' %}" alt=""/>
-</div>

+ 0 - 14
wagtail/admin/templates/wagtailadmin/shared/animated_logo.stories.tsx

@@ -1,14 +0,0 @@
-import React from 'react';
-import TemplatePattern from '../../../../../client/storybook/TemplatePattern';
-
-import template from './animated_logo.html';
-
-export default {
-  parameters: {
-    docs: {
-      source: { code: template },
-    },
-  },
-};
-
-export const AnimatedLogo = () => <TemplatePattern filename={__filename} />;

+ 0 - 10
wagtail/admin/templates/wagtailadmin/shared/explorer_menu_item.html

@@ -1,10 +0,0 @@
-{% load wagtailadmin_tags %}
-
-{% if start_page_id %}
-    <li class="menu-item{% if active %} menu-active{% endif %}" data-explorer-menu-item>
-        <a href="{{ url }}" data-explorer-start-page="{{ start_page_id }}">
-            {% if icon_name %}{% icon icon_name 'icon--menuitem' %}{% endif %}
-            {{ label }}
-        </a>
-    </li>
-{% endif %}

+ 0 - 22
wagtail/admin/templates/wagtailadmin/shared/main_nav.html

@@ -1,22 +0,0 @@
-{% load wagtailadmin_tags %}
-{% load i18n %}
-<nav class="nav-main">
-    <ul>
-        {{ menu_html }}
-
-        <li class="nav-footer" id="footer">
-            <div class="account" id="account-settings" title="{% trans 'Edit your account' %}">
-                <span class="avatar square avatar-on-dark">
-                    <img src="{% avatar_url request.user size=50 %}" alt="" />
-                </span>
-                <em class="icon icon-arrow-up-after">{{ request.user.first_name|default:request.user.get_username }}</em>
-            </div>
-
-            <ul class="nav-footer-submenu">
-                <li><a href="{% url 'wagtailadmin_account' %}" class="icon icon-user">{% trans "Account settings" %}</a></li>
-                <li><a href="{% url 'wagtailadmin_logout' %}" class="icon icon-logout">{% trans "Log out" %}</a></li>
-            </ul>
-
-        </li>
-    </ul>
-</nav>

+ 0 - 10
wagtail/admin/templates/wagtailadmin/shared/menu_item.html

@@ -1,10 +0,0 @@
-{% load wagtailadmin_tags %}
-
-<li class="menu-item{% if active %} menu-active{% endif %}">
-    <a href="{{ url }}"
-        class="{{ classnames }}"
-        {{ attr_string }}>
-        {% if icon_name %}{% icon icon_name 'icon--menuitem' %}{% endif %}
-        {{ label }}
-    </a>
-</li>

+ 0 - 10
wagtail/admin/templates/wagtailadmin/shared/menu_search.html

@@ -1,10 +0,0 @@
-{% load i18n %}
-
-<form class="nav-search" action="{{ search_url }}" method="get">
-    <div>
-        <label for="menu-search-q">{% trans "Search" %}</label>
-        <input type="text" id="menu-search-q" name="q" placeholder="{% trans 'Search' %}" />
-        <button class="button" type="submit">{% trans "Search" %}</button>
-    </div>
-</form>
-

+ 0 - 6
wagtail/admin/templates/wagtailadmin/shared/menu_settings_menu_item.html

@@ -1,6 +0,0 @@
-{% extends "wagtailadmin/shared/menu_submenu_item.html" %}
-{% load wagtailcore_tags %}
-
-{% block menu_footer %}
-    <p class="nav-submenu__footer">Wagtail v.{% wagtail_version %}</p>
-{% endblock %}

+ 0 - 19
wagtail/admin/templates/wagtailadmin/shared/menu_submenu_item.html

@@ -1,19 +0,0 @@
-{% load wagtailadmin_tags %}
-
-<li class="menu-item{% if active %} menu-active{% endif %}">
-    <a href="#" data-nav-primary-submenu-trigger class="{{ classnames }}"{{ attr_string }}>
-        {% if icon_name %}{% icon icon_name 'icon--menuitem' %}{% endif %}
-        {{ label }}
-        {% icon 'arrow-right' 'icon--submenu-trigger' %}
-    </a>
-    <div class="nav-submenu">
-        <h2 id="nav-submenu-{{ name }}-title" class="{{ classnames }}">
-            {% if icon_name %}{% icon icon_name 'icon--submenu-header' %}{% endif %}
-            {{ label }}
-        </h2>
-        <ul class="nav-submenu__list" aria-labelledby="nav-submenu-{{ name }}-title">
-            {{ menu_html }}
-        </ul>
-        {% block menu_footer %}{% endblock %}
-    </div>
-</li>

+ 1 - 2
wagtail/admin/templates/wagtailadmin/skeleton.html

@@ -16,9 +16,8 @@
 
 
         {% block branding_favicon %}{% endblock %}
         {% block branding_favicon %}{% endblock %}
     </head>
     </head>
-    {% slim_sidebar_enabled as slim_sidebar_enabled %}
     {% sidebar_collapsed as sidebar_collapsed %}
     {% sidebar_collapsed as sidebar_collapsed %}
-    <body id="wagtail" class="{% block bodyclass %}{% endblock %} {% if slim_sidebar_enabled and sidebar_collapsed %}sidebar-collapsed{% endif %} {% if messages %}has-messages{% endif %} focus-outline-on">
+    <body id="wagtail" class="{% block bodyclass %}{% endblock %} {% if sidebar_collapsed %}sidebar-collapsed{% endif %} {% if messages %}has-messages{% endif %}">
         <div data-sprite></div>
         <div data-sprite></div>
         <script>
         <script>
             function loadIconSprite() {
             function loadIconSprite() {

+ 0 - 61
wagtail/admin/templatetags/wagtailadmin_tags.py

@@ -8,10 +8,8 @@ from django.contrib.admin.utils import quote
 from django.contrib.humanize.templatetags.humanize import intcomma
 from django.contrib.humanize.templatetags.humanize import intcomma
 from django.contrib.messages.constants import DEFAULT_TAGS as MESSAGE_TAGS
 from django.contrib.messages.constants import DEFAULT_TAGS as MESSAGE_TAGS
 from django.db.models import Min, QuerySet
 from django.db.models import Min, QuerySet
-from django.forms import Media
 from django.shortcuts import resolve_url as resolve_url_func
 from django.shortcuts import resolve_url as resolve_url_func
 from django.template.defaultfilters import stringfilter
 from django.template.defaultfilters import stringfilter
-from django.template.loader import render_to_string
 from django.templatetags.static import static
 from django.templatetags.static import static
 from django.urls import reverse
 from django.urls import reverse
 from django.urls.exceptions import NoReverseMatch
 from django.urls.exceptions import NoReverseMatch
@@ -57,33 +55,6 @@ register = template.Library()
 register.filter("intcomma", intcomma)
 register.filter("intcomma", intcomma)
 
 
 
 
-@register.simple_tag(takes_context=True)
-def menu_search(context):
-    request = context["request"]
-
-    search_areas = admin_search_areas.search_items_for_request(request)
-    if not search_areas:
-        return ""
-    search_area = search_areas[0]
-
-    return render_to_string(
-        "wagtailadmin/shared/menu_search.html",
-        {
-            "search_url": search_area.url,
-        },
-    )
-
-
-@register.inclusion_tag("wagtailadmin/shared/main_nav.html", takes_context=True)
-def main_nav(context):
-    request = context["request"]
-
-    return {
-        "menu_html": admin_menu.render_html(request),
-        "request": request,
-    }
-
-
 @register.inclusion_tag("wagtailadmin/shared/breadcrumb.html", takes_context=True)
 @register.inclusion_tag("wagtailadmin/shared/breadcrumb.html", takes_context=True)
 def explorer_breadcrumb(
 def explorer_breadcrumb(
     context,
     context,
@@ -137,33 +108,6 @@ def search_other(context, current=None):
     }
     }
 
 
 
 
-@register.simple_tag
-def main_nav_js():
-    if slim_sidebar_enabled():
-        return Media(
-            js=[
-                versioned_static("wagtailadmin/js/telepath/telepath.js"),
-                versioned_static("wagtailadmin/js/sidebar.js"),
-            ]
-        )
-
-    else:
-        return (
-            Media(js=[versioned_static("wagtailadmin/js/sidebar-legacy.js")])
-            + admin_menu.media["js"]
-        )
-
-
-@register.simple_tag
-def main_nav_css():
-    if slim_sidebar_enabled():
-        return Media(css={"all": [versioned_static("wagtailadmin/css/sidebar.css")]})
-
-    else:
-        # Legacy sidebar CSS in core.css
-        return admin_menu.media["css"]
-
-
 @register.filter("ellipsistrim")
 @register.filter("ellipsistrim")
 def ellipsistrim(value, max_length):
 def ellipsistrim(value, max_length):
     if len(value) > max_length:
     if len(value) > max_length:
@@ -850,11 +794,6 @@ def locale_label_from_id(locale_id):
     return get_locales_display_names().get(locale_id)
     return get_locales_display_names().get(locale_id)
 
 
 
 
-@register.simple_tag()
-def slim_sidebar_enabled():
-    return getattr(settings, "WAGTAIL_SLIM_SIDEBAR", True)
-
-
 @register.simple_tag(takes_context=True)
 @register.simple_tag(takes_context=True)
 def sidebar_collapsed(context):
 def sidebar_collapsed(context):
     request = context.get("request")
     request = context.get("request")

+ 0 - 22
wagtail/admin/tests/test_admin_search.py

@@ -19,12 +19,6 @@ class BaseSearchAreaTestCase(WagtailTestUtils, TestCase):
         template = Template("{% load wagtailadmin_tags %}{% search_other %}")
         template = Template("{% load wagtailadmin_tags %}{% search_other %}")
         return template.render(Context({"request": request}))
         return template.render(Context({"request": request}))
 
 
-    def menu_search(self, current_url="/admin/", data=None):
-        request = self.rf.get(current_url, data=data)
-        request.user = self.user
-        template = Template("{% load wagtailadmin_tags %}{% menu_search %}")
-        return template.render(Context({"request": request}))
-
 
 
 class TestSearchAreas(BaseSearchAreaTestCase):
 class TestSearchAreas(BaseSearchAreaTestCase):
     def setUp(self):
     def setUp(self):
@@ -62,10 +56,6 @@ class TestSearchAreas(BaseSearchAreaTestCase):
             html=True,
             html=True,
         )
         )
 
 
-    def test_menu_search(self):
-        rendered = self.menu_search()
-        self.assertIn(reverse("wagtailadmin_pages:search"), rendered)
-
     def test_search_other(self):
     def test_search_other(self):
         rendered = self.search_other()
         rendered = self.search_other()
         self.assertIn(reverse("wagtailadmin_pages:search"), rendered)
         self.assertIn(reverse("wagtailadmin_pages:search"), rendered)
@@ -109,14 +99,6 @@ class TestSearchAreaNoPagePermissions(BaseSearchAreaTestCase):
             '{"_type": "wagtail.sidebar.SearchModule", "_args": ["/customsearch/"]}',
             '{"_type": "wagtail.sidebar.SearchModule", "_args": ["/customsearch/"]}',
         )
         )
 
 
-    def test_menu_search(self):
-        """
-        The search form should go to the custom search, not the page search.
-        """
-        rendered = self.menu_search()
-        self.assertNotIn(reverse("wagtailadmin_pages:search"), rendered)
-        self.assertIn('action="/customsearch/"', rendered)
-
     def test_search_other(self):
     def test_search_other(self):
         """The pages search link should be hidden, custom search should be visible."""
         """The pages search link should be hidden, custom search should be visible."""
         rendered = self.search_other()
         rendered = self.search_other()
@@ -125,7 +107,3 @@ class TestSearchAreaNoPagePermissions(BaseSearchAreaTestCase):
 
 
         self.assertNotIn("Pages", rendered)
         self.assertNotIn("Pages", rendered)
         self.assertIn("My Search", rendered)
         self.assertIn("My Search", rendered)
-
-    def test_no_searches(self):
-        rendered = self.menu_search(data={"hide-option": "true"})
-        self.assertEqual(rendered, "")

+ 1 - 9
wagtail/admin/tests/test_menu.py

@@ -1,4 +1,4 @@
-from django.test import RequestFactory, TestCase, override_settings
+from django.test import RequestFactory, TestCase
 from django.urls import reverse
 from django.urls import reverse
 
 
 from wagtail import hooks
 from wagtail import hooks
@@ -32,14 +32,6 @@ class TestMenuRendering(TestCase, WagtailTestUtils):
         response = self.client.get(reverse("wagtailadmin_home"))
         response = self.client.get(reverse("wagtailadmin_home"))
         self.assertContains(response, "sidebar-collapsed")
         self.assertContains(response, "sidebar-collapsed")
 
 
-    @override_settings(WAGTAIL_SLIM_SIDEBAR=False)
-    def test_not_collapsed_with_legacy(self):
-        """Sidebar should only remember its collapsed state with the slim implementation."""
-        # Sidebar should not be collapsed because the feature flag is not enabled
-        self.client.cookies["wagtail_sidebar_collapsed"] = "1"
-        response = self.client.get(reverse("wagtailadmin_home"))
-        self.assertNotContains(response, "sidebar-collapsed")
-
     def test_simple_menu(self):
     def test_simple_menu(self):
         # Note: initialise the menu before registering hooks as this is what happens in reality.
         # Note: initialise the menu before registering hooks as this is what happens in reality.
         # (the real menus are initialised at the module level in admin/menu.py)
         # (the real menus are initialised at the module level in admin/menu.py)

+ 1 - 9
wagtail/admin/wagtail_hooks.py

@@ -55,8 +55,6 @@ from wagtail.whitelist import allow_without_attributes, attribute_rule, check_ur
 
 
 
 
 class ExplorerMenuItem(MenuItem):
 class ExplorerMenuItem(MenuItem):
-    template = "wagtailadmin/shared/explorer_menu_item.html"
-
     def is_shown(self, request):
     def is_shown(self, request):
         return user_has_any_page_permission(request.user)
         return user_has_any_page_permission(request.user)
 
 
@@ -97,8 +95,6 @@ def register_explorer_menu_item():
 
 
 
 
 class SettingsMenuItem(SubmenuMenuItem):
 class SettingsMenuItem(SubmenuMenuItem):
-    template = "wagtailadmin/shared/menu_settings_menu_item.html"
-
     def render_component(self, request):
     def render_component(self, request):
         return SubMenuItemComponent(
         return SubMenuItemComponent(
             self.name,
             self.name,
@@ -841,10 +837,6 @@ def register_core_features(features):
     )
     )
 
 
 
 
-class ReportsMenuItem(SubmenuMenuItem):
-    template = "wagtailadmin/shared/menu_submenu_item.html"
-
-
 class LockedPagesMenuItem(MenuItem):
 class LockedPagesMenuItem(MenuItem):
     def is_shown(self, request):
     def is_shown(self, request):
         return UserPagePermissionsProxy(request.user).can_remove_locks()
         return UserPagePermissionsProxy(request.user).can_remove_locks()
@@ -917,7 +909,7 @@ def register_aging_pages_report_menu_item():
 
 
 @hooks.register("register_admin_menu_item")
 @hooks.register("register_admin_menu_item")
 def register_reports_menu():
 def register_reports_menu():
-    return ReportsMenuItem(_("Reports"), reports_menu, icon_name="site", order=9000)
+    return SubmenuMenuItem(_("Reports"), reports_menu, icon_name="site", order=9000)
 
 
 
 
 @hooks.register("register_icons")
 @hooks.register("register_icons")

+ 2 - 2
wagtail/images/templates/wagtailimages/images/edit.html

@@ -52,7 +52,7 @@
                         {% endif %}
                         {% endif %}
                     {% endfor %}
                     {% endfor %}
                 </ul>
                 </ul>
-                <div class="u-hidden@xs">
+                <div class="w-hidden sm:w-block">
                     <input type="submit" value="{% trans 'Save' %}" class="button" />
                     <input type="submit" value="{% trans 'Save' %}" class="button" />
                     {% if user_can_delete %}
                     {% if user_can_delete %}
                         <a href="{% url 'wagtailimages:delete' image.id %}{% if next %}?next={{ next|urlencode }}{% endif %}" class="button button-secondary no">{% trans "Delete image" %}</a>
                         <a href="{% url 'wagtailimages:delete' image.id %}{% if next %}?next={{ next|urlencode }}{% endif %}" class="button button-secondary no">{% trans "Delete image" %}</a>
@@ -108,7 +108,7 @@
             </div>
             </div>
         </div>
         </div>
 
 
-        <div class="row row-flush nice-padding u-hidden@sm">
+        <div class="row row-flush nice-padding sm:!w-hidden">
             <div class="col5">
             <div class="col5">
                 <input type="submit" value="{% trans 'Save' %}" class="button" />
                 <input type="submit" value="{% trans 'Save' %}" class="button" />
                 {% if user_can_delete %}
                 {% if user_can_delete %}