From caec27c3001e46fed87633db889abf6cb2f083f2 Mon Sep 17 00:00:00 2001 From: Suzanne Aitchison Date: Wed, 27 Jul 2022 17:40:48 +0100 Subject: [PATCH] Dynamically change number of formatter buttons in md toolbar overflow menu (#18224) * dynamically change number of formatter buttons in the overflow menu * add specs --- .../stylesheets/views/article-form.scss | 20 +- app/assets/stylesheets/views/comments.scss | 7 +- .../MarkdownToolbar/MarkdownToolbar.jsx | 104 ++++--- .../__tests__/MarkdownToolbar.test.jsx | 158 +++++++++-- .../markdownSyntaxFormatters.test.js | 255 +++++++++--------- .../markdownSyntaxFormatters.jsx | 5 +- 6 files changed, 336 insertions(+), 213 deletions(-) diff --git a/app/assets/stylesheets/views/article-form.scss b/app/assets/stylesheets/views/article-form.scss index 9cd6e0b07..846aa9e99 100644 --- a/app/assets/stylesheets/views/article-form.scss +++ b/app/assets/stylesheets/views/article-form.scss @@ -158,15 +158,19 @@ top: 0; background: var(--base-0); padding: var(--su-2) var(--content-padding-x); - padding-right: var(--toolbar-padding-right, 0); - overflow-x: auto; + padding-right: var(--content-padding-x, 0); flex-shrink: 0; margin: calc(var(--content-padding-y) * -1) calc(var(--content-padding-x) * -1) var(--su-6) calc(var(--content-padding-x) * -1); .editor-toolbar { - position: relative; + overflow-x: auto; + + @media (min-width: $breakpoint-xs) { + overflow-x: unset; + position: relative; + } } > :first-child { @@ -174,16 +178,6 @@ margin-left: calc(var(--su-2) * -1); } } - - &::-webkit-scrollbar { - background: transparent; - height: 0; - } - - @media (min-width: $breakpoint-m) { - --toolbar-padding-right: var(--content-padding-x); - overflow: visible; - } } &__cover { diff --git a/app/assets/stylesheets/views/comments.scss b/app/assets/stylesheets/views/comments.scss index bbe82c880..f1dd10268 100644 --- a/app/assets/stylesheets/views/comments.scss +++ b/app/assets/stylesheets/views/comments.scss @@ -28,14 +28,9 @@ .editor-toolbar { border-top: 1px solid var(--form-border); overflow-x: auto; - - &::-webkit-scrollbar { - background: transparent; - height: 0; - } } - @media (min-width: $breakpoint-m) { + @media (min-width: $breakpoint-xs) { .editor-toolbar { overflow-x: unset; } diff --git a/app/javascript/crayons/MarkdownToolbar/MarkdownToolbar.jsx b/app/javascript/crayons/MarkdownToolbar/MarkdownToolbar.jsx index 33e215a01..583ccc37f 100644 --- a/app/javascript/crayons/MarkdownToolbar/MarkdownToolbar.jsx +++ b/app/javascript/crayons/MarkdownToolbar/MarkdownToolbar.jsx @@ -2,8 +2,7 @@ import { h, cloneElement } from 'preact'; import { useState, useLayoutEffect, useRef } from 'preact/hooks'; import { ImageUploader } from '../../article-form/components/ImageUploader'; import { - coreSyntaxFormatters, - secondarySyntaxFormatters, + markdownSyntaxFormatters, getNewTextAreaValueWithEdits, } from './markdownSyntaxFormatters'; import OverflowIcon from '@images/overflow-vertical.svg'; @@ -15,6 +14,25 @@ import { getSelectionData } from '@utilities/textAreaUtils'; // Placeholder text displayed while an image is uploading const UPLOADING_IMAGE_PLACEHOLDER = '![Uploading image](...)'; +const MAX_CORE_FORMATTERS_BY_SCREEN_SIZE = { + small: 5, + large: 7, + extraLarge: 10, +}; + +const getNumberOfIconsToDisplayInToolbar = ({ + isSmallScreen, + isLargeScreen, +}) => { + if (isSmallScreen) { + return MAX_CORE_FORMATTERS_BY_SCREEN_SIZE.small; + } + if (isLargeScreen) { + return MAX_CORE_FORMATTERS_BY_SCREEN_SIZE.large; + } + return MAX_CORE_FORMATTERS_BY_SCREEN_SIZE.extraLarge; +}; + /** * Returns the next sibling in the DOM which matches the given CSS selector. * This makes sure that only toolbar buttons are cycled through on Arrow key press, @@ -66,6 +84,13 @@ export const MarkdownToolbar = ({ const [overflowMenuOpen, setOverflowMenuOpen] = useState(false); const [storedCursorPosition, setStoredCursorPosition] = useState({}); const smallScreen = useMediaQuery(`(max-width: ${BREAKPOINTS.Medium - 1}px)`); + const largeScreen = useMediaQuery( + `(min-width: ${BREAKPOINTS.Large}px) and (max-width: ${ + BREAKPOINTS.ExtraLarge - 1 + }px)`, + ); + + const overflowMenuRows = smallScreen ? 2 : 1; // Enhance any additional toolbar elements with the appropriate roles & listeners const additionalSecondaryItems = additionalSecondaryToolbarElements.map( @@ -78,11 +103,6 @@ export const MarkdownToolbar = ({ }), ); - const markdownSyntaxFormatters = { - ...coreSyntaxFormatters, - ...secondarySyntaxFormatters, - }; - const keyboardShortcuts = Object.fromEntries( Object.keys(markdownSyntaxFormatters) .filter( @@ -116,7 +136,7 @@ export const MarkdownToolbar = ({ if (!focusableToolbarButton) { document.querySelector('.toolbar-btn').setAttribute('tabindex', '0'); } - }, [smallScreen]); + }, [smallScreen, largeScreen]); useLayoutEffect(() => { const clickOutsideHandler = ({ target }) => { @@ -298,29 +318,32 @@ export const MarkdownToolbar = ({ ); }; - const getSecondaryFormatterButtons = (isOverflow) => - Object.keys(secondarySyntaxFormatters).map((controlName, index) => { + const numberOfCoreFormatters = getNumberOfIconsToDisplayInToolbar({ + isSmallScreen: smallScreen, + isLargeScreen: largeScreen, + }); + + const coreSyntaxFormatters = Object.fromEntries( + Object.entries(markdownSyntaxFormatters).slice(0, numberOfCoreFormatters), + ); + const secondarySyntaxFormatters = Object.fromEntries( + Object.entries(markdownSyntaxFormatters).slice(numberOfCoreFormatters), + ); + + const secondaryFormatterButtons = Object.keys(secondarySyntaxFormatters).map( + (controlName, index) => { const { icon, label, getKeyboardShortcut } = secondarySyntaxFormatters[controlName]; return (