From a7ee16347fc4d4e1b5f2f843ef8cc63e00000575 Mon Sep 17 00:00:00 2001 From: tellthemachines Date: Wed, 2 Mar 2022 16:33:32 +1100 Subject: [PATCH 1/8] Add reusable preferences modal to interface package. --- .../preferences-modal/base-option.js | 20 +++ .../src/components/preferences-modal/index.js | 160 ++++++++++++++++++ .../components/preferences-modal/section.js | 17 ++ .../components/preferences-modal/style.scss | 101 +++++++++++ 4 files changed, 298 insertions(+) create mode 100644 packages/interface/src/components/preferences-modal/base-option.js create mode 100644 packages/interface/src/components/preferences-modal/index.js create mode 100644 packages/interface/src/components/preferences-modal/section.js create mode 100644 packages/interface/src/components/preferences-modal/style.scss diff --git a/packages/interface/src/components/preferences-modal/base-option.js b/packages/interface/src/components/preferences-modal/base-option.js new file mode 100644 index 0000000000000..bf027b17354a2 --- /dev/null +++ b/packages/interface/src/components/preferences-modal/base-option.js @@ -0,0 +1,20 @@ +/** + * WordPress dependencies + */ +import { ToggleControl } from '@wordpress/components'; + +function BaseOption( { help, label, isChecked, onChange, children } ) { + return ( +
+ + { children } +
+ ); +} + +export default BaseOption; diff --git a/packages/interface/src/components/preferences-modal/index.js b/packages/interface/src/components/preferences-modal/index.js new file mode 100644 index 0000000000000..4b8c6d222180e --- /dev/null +++ b/packages/interface/src/components/preferences-modal/index.js @@ -0,0 +1,160 @@ +/** + * WordPress dependencies + */ +import { useViewportMatch } from '@wordpress/compose'; +import { + __experimentalNavigatorProvider as NavigatorProvider, + __experimentalNavigatorScreen as NavigatorScreen, + __experimentalNavigatorButton as NavigatorButton, + __experimentalNavigatorBackButton as NavigatorBackButton, + __experimentalItemGroup as ItemGroup, + __experimentalItem as Item, + __experimentalHStack as HStack, + __experimentalText as Text, + __experimentalTruncate as Truncate, + FlexItem, + Modal, + TabPanel, + Card, + CardHeader, + CardBody, +} from '@wordpress/components'; +import { useMemo, useCallback, useState } from '@wordpress/element'; +import { chevronLeft, chevronRight, Icon } from '@wordpress/icons'; +import { isRTL, __ } from '@wordpress/i18n'; + +const PREFERENCES_MENU = 'preferences-menu'; + +export default function PreferencesModal( + closeModal, + isModalActive, + sections +) { + const isLargeViewport = useViewportMatch( 'medium' ); + + // This is also used to sync the two different rendered components + // between small and large viewports. + const [ activeMenu, setActiveMenu ] = useState( PREFERENCES_MENU ); + + /** + * Create helper objects from `sections` for easier data handling. + * `tabs` is used for creating the `TabPanel` and `sectionsContentMap` + * is used for easier access to active tab's content. + */ + const { tabs, sectionsContentMap } = useMemo( + () => + sections.reduce( + ( accumulator, { name, tabLabel: title, content } ) => { + accumulator.tabs.push( { name, title } ); + accumulator.sectionsContentMap[ name ] = content; + return accumulator; + }, + { tabs: [], sectionsContentMap: {} } + ), + [ sections ] + ); + const getCurrentTab = useCallback( + ( tab ) => sectionsContentMap[ tab.name ] || null, + [ sectionsContentMap ] + ); + if ( ! isModalActive ) { + return null; + } + + let modalContent; + // We render different components based on the viewport size. + if ( isLargeViewport ) { + modalContent = ( + + { getCurrentTab } + + ); + } else { + modalContent = ( + + + + + + { tabs.map( ( tab ) => { + return ( + + + + + { tab.title } + + + + + + + + ); + } ) } + + + + + { sections.map( ( section ) => { + return ( + + + + + { section.tabLabel } + + { section.content } + + + ); + } ) } + + ); + } + + return ( + + { modalContent } + + ); +} diff --git a/packages/interface/src/components/preferences-modal/section.js b/packages/interface/src/components/preferences-modal/section.js new file mode 100644 index 0000000000000..3ad08b557ff85 --- /dev/null +++ b/packages/interface/src/components/preferences-modal/section.js @@ -0,0 +1,17 @@ +const Section = ( { description, title, children } ) => ( +
+ +

+ { title } +

+ { description && ( +

+ { description } +

+ ) } +
+ { children } +
+); + +export default Section; diff --git a/packages/interface/src/components/preferences-modal/style.scss b/packages/interface/src/components/preferences-modal/style.scss new file mode 100644 index 0000000000000..d0708d117996b --- /dev/null +++ b/packages/interface/src/components/preferences-modal/style.scss @@ -0,0 +1,101 @@ +$vertical-tabs-width: 160px; + +.interface-preferences-modal { + // To keep modal dimensions consistent as subsections are navigated, width + // and height are used instead of max-(width/height). + @include break-small() { + width: calc(100% - #{ $grid-unit-20 * 2 }); + height: calc(100% - #{ $header-height * 2 }); + } + @include break-medium() { + width: $break-medium - $grid-unit-20 * 2; + } + @include break-large() { + height: 70%; + } + + // Clears spacing to flush fit the navigator component to the modal edges. + @media (max-width: #{ ($break-medium - 1) }) { + .components-modal__content { + padding: 0; + + &::before { + content: none; + } + } + // Keep the navigator component from overflowing the modal content area + // to ensure that sticky position elements stick where intended. + .components-navigator-provider { + height: 100%; + } + } + + .interface-preferences__tabs { + .components-tab-panel__tabs { + position: absolute; + top: $header-height + $grid-unit-30; + // Aligns button text instead of button box. + left: $grid-unit-20; + width: $vertical-tabs-width; + .components-tab-panel__tabs-item { + border-radius: $radius-block-ui; + font-weight: 400; + &.is-active { + background: $gray-100; + box-shadow: none; + font-weight: 500; + } + &:focus:not(:disabled) { + box-shadow: inset 0 0 0 var(--wp-admin-border-width-focus) var(--wp-admin-theme-color); + } + } + } + .components-tab-panel__tab-content { + padding-left: $grid-unit-30; + margin-left: $vertical-tabs-width; + } + } + + .components-base-control__help { + margin: -$grid-unit-10 0 $grid-unit-10 58px; + font-size: $helptext-font-size; + font-style: normal; + color: $gray-700; + } +} + +.interface-preferences-modal__section { + margin: 0 0 2.5rem 0; + + &:last-child { + margin: 0; + } +} + +.interface-preferences-modal__section-title { + font-size: 0.9rem; + font-weight: 600; + margin-top: 0; +} + +.interface-preferences-modal__section-description { + margin: -$grid-unit-10 0 $grid-unit-10 0; + font-size: $helptext-font-size; + font-style: normal; + color: $gray-700; +} + +.interface-preferences-modal__option { + .components-base-control { + .components-base-control__field { + align-items: center; + display: flex; + margin-bottom: 0; + + & > label { + flex-grow: 1; + padding: 0.6rem 0 0.6rem 10px; + } + } + } +} From bf9b839dd64624b809ea3d87c9ffd62a088c8256 Mon Sep 17 00:00:00 2001 From: tellthemachines Date: Wed, 2 Mar 2022 17:09:43 +1100 Subject: [PATCH 2/8] Rearrange and export files --- packages/interface/src/components/index.js | 3 ++ .../index.js} | 0 .../preferences-modal-base-option/style.scss | 14 ++++++++ .../index.js} | 0 .../preferences-modal-section/style.scss | 20 +++++++++++ .../components/preferences-modal/style.scss | 36 ------------------- packages/interface/src/style.scss | 3 ++ 7 files changed, 40 insertions(+), 36 deletions(-) rename packages/interface/src/components/{preferences-modal/base-option.js => preferences-modal-base-option/index.js} (100%) create mode 100644 packages/interface/src/components/preferences-modal-base-option/style.scss rename packages/interface/src/components/{preferences-modal/section.js => preferences-modal-section/index.js} (100%) create mode 100644 packages/interface/src/components/preferences-modal-section/style.scss diff --git a/packages/interface/src/components/index.js b/packages/interface/src/components/index.js index 27b41f4ecb999..41677b69d1439 100644 --- a/packages/interface/src/components/index.js +++ b/packages/interface/src/components/index.js @@ -6,3 +6,6 @@ export { default as PinnedItems } from './pinned-items'; export { default as MoreMenuDropdown } from './more-menu-dropdown'; export { default as MoreMenuFeatureToggle } from './more-menu-feature-toggle'; export { default as ActionItem } from './action-item'; +export { default as PreferencesModal } from './preferences-modal'; +export { default as PreferencesModalSection } from './preferences-modal-section'; +export { default as PreferencesModalBaseOption } from './preferences-modal-base-option'; diff --git a/packages/interface/src/components/preferences-modal/base-option.js b/packages/interface/src/components/preferences-modal-base-option/index.js similarity index 100% rename from packages/interface/src/components/preferences-modal/base-option.js rename to packages/interface/src/components/preferences-modal-base-option/index.js diff --git a/packages/interface/src/components/preferences-modal-base-option/style.scss b/packages/interface/src/components/preferences-modal-base-option/style.scss new file mode 100644 index 0000000000000..3c75a96569a0f --- /dev/null +++ b/packages/interface/src/components/preferences-modal-base-option/style.scss @@ -0,0 +1,14 @@ +.interface-preferences-modal__option { + .components-base-control { + .components-base-control__field { + align-items: center; + display: flex; + margin-bottom: 0; + + & > label { + flex-grow: 1; + padding: 0.6rem 0 0.6rem 10px; + } + } + } +} diff --git a/packages/interface/src/components/preferences-modal/section.js b/packages/interface/src/components/preferences-modal-section/index.js similarity index 100% rename from packages/interface/src/components/preferences-modal/section.js rename to packages/interface/src/components/preferences-modal-section/index.js diff --git a/packages/interface/src/components/preferences-modal-section/style.scss b/packages/interface/src/components/preferences-modal-section/style.scss new file mode 100644 index 0000000000000..135545986836a --- /dev/null +++ b/packages/interface/src/components/preferences-modal-section/style.scss @@ -0,0 +1,20 @@ +.interface-preferences-modal__section { + margin: 0 0 2.5rem 0; + + &:last-child { + margin: 0; + } +} + +.interface-preferences-modal__section-title { + font-size: 0.9rem; + font-weight: 600; + margin-top: 0; +} + +.interface-preferences-modal__section-description { + margin: -$grid-unit-10 0 $grid-unit-10 0; + font-size: $helptext-font-size; + font-style: normal; + color: $gray-700; +} diff --git a/packages/interface/src/components/preferences-modal/style.scss b/packages/interface/src/components/preferences-modal/style.scss index d0708d117996b..1f11dfbc34acf 100644 --- a/packages/interface/src/components/preferences-modal/style.scss +++ b/packages/interface/src/components/preferences-modal/style.scss @@ -63,39 +63,3 @@ $vertical-tabs-width: 160px; color: $gray-700; } } - -.interface-preferences-modal__section { - margin: 0 0 2.5rem 0; - - &:last-child { - margin: 0; - } -} - -.interface-preferences-modal__section-title { - font-size: 0.9rem; - font-weight: 600; - margin-top: 0; -} - -.interface-preferences-modal__section-description { - margin: -$grid-unit-10 0 $grid-unit-10 0; - font-size: $helptext-font-size; - font-style: normal; - color: $gray-700; -} - -.interface-preferences-modal__option { - .components-base-control { - .components-base-control__field { - align-items: center; - display: flex; - margin-bottom: 0; - - & > label { - flex-grow: 1; - padding: 0.6rem 0 0.6rem 10px; - } - } - } -} diff --git a/packages/interface/src/style.scss b/packages/interface/src/style.scss index e6950de411156..ecabe0c827f2b 100644 --- a/packages/interface/src/style.scss +++ b/packages/interface/src/style.scss @@ -4,3 +4,6 @@ @import "./components/interface-skeleton/style.scss"; @import "./components/more-menu-dropdown/style.scss"; @import "./components/pinned-items/style.scss"; +@import "./components/preferences-modal/style.scss"; +@import "./components/preferences-modal-section/style.scss"; +@import "./components/preferences-modal-base-option/style.scss"; From 3c08ed5490549bb8a074f3a9a7663fcfe29ffb55 Mon Sep 17 00:00:00 2001 From: tellthemachines Date: Thu, 3 Mar 2022 12:07:54 +1100 Subject: [PATCH 3/8] Make sure sections exist. --- .../src/components/preferences-modal/index.js | 89 ++++++++++--------- 1 file changed, 46 insertions(+), 43 deletions(-) diff --git a/packages/interface/src/components/preferences-modal/index.js b/packages/interface/src/components/preferences-modal/index.js index 4b8c6d222180e..4604b1acf92ae 100644 --- a/packages/interface/src/components/preferences-modal/index.js +++ b/packages/interface/src/components/preferences-modal/index.js @@ -25,41 +25,39 @@ import { isRTL, __ } from '@wordpress/i18n'; const PREFERENCES_MENU = 'preferences-menu'; -export default function PreferencesModal( - closeModal, - isModalActive, - sections -) { +export default function PreferencesModal( { closeModal, sections } ) { const isLargeViewport = useViewportMatch( 'medium' ); // This is also used to sync the two different rendered components // between small and large viewports. const [ activeMenu, setActiveMenu ] = useState( PREFERENCES_MENU ); - /** * Create helper objects from `sections` for easier data handling. * `tabs` is used for creating the `TabPanel` and `sectionsContentMap` * is used for easier access to active tab's content. */ - const { tabs, sectionsContentMap } = useMemo( - () => - sections.reduce( + const { tabs, sectionsContentMap } = useMemo( () => { + let mappedTabs = { + tabs: [], + sectionsContentMap: {}, + }; + if ( sections.length ) { + mappedTabs = sections.reduce( ( accumulator, { name, tabLabel: title, content } ) => { accumulator.tabs.push( { name, title } ); accumulator.sectionsContentMap[ name ] = content; return accumulator; }, { tabs: [], sectionsContentMap: {} } - ), - [ sections ] - ); + ); + } + return mappedTabs; + }, [ sections ] ); + const getCurrentTab = useCallback( ( tab ) => sectionsContentMap[ tab.name ] || null, [ sectionsContentMap ] ); - if ( ! isModalActive ) { - return null; - } let modalContent; // We render different components based on the viewport size. @@ -115,34 +113,39 @@ export default function PreferencesModal( - { sections.map( ( section ) => { - return ( - - - - - { section.tabLabel } - - { section.content } - - - ); - } ) } + { sections.length && + sections.map( ( section ) => { + return ( + + + + + + { section.tabLabel } + + + { section.content } + + + ); + } ) } ); } From 4d8563a1d27a208b8df678af697591ed0bded549 Mon Sep 17 00:00:00 2001 From: tellthemachines Date: Thu, 3 Mar 2022 15:02:40 +1100 Subject: [PATCH 4/8] Add readmes for all components. --- .../preferences-modal-base-option/README.md | 27 +++++++ .../preferences-modal-section/README.md | 30 +++++++ .../components/preferences-modal/README.md | 81 +++++++++++++++++++ 3 files changed, 138 insertions(+) create mode 100644 packages/interface/src/components/preferences-modal-base-option/README.md create mode 100644 packages/interface/src/components/preferences-modal-section/README.md create mode 100644 packages/interface/src/components/preferences-modal/README.md diff --git a/packages/interface/src/components/preferences-modal-base-option/README.md b/packages/interface/src/components/preferences-modal-base-option/README.md new file mode 100644 index 0000000000000..c76e8c8cd8539 --- /dev/null +++ b/packages/interface/src/components/preferences-modal-base-option/README.md @@ -0,0 +1,27 @@ +#PreferencesModalBaseOption + +`PreferencesModalBaseOption` renders a toggle meant to be used with `PreferencesModal`. + +This component implements a `ToggleControl` component from the `@wordpress/components` package. + +## Example + +See the `PreferencesModal` readme for usage info. + + +## Props + +### help +### label +### isChecked +### onChange + +These props are passed directly to ToggleControl, so see [ToggleControl readme](https://github.com/WordPress/gutenberg/blob/trunk/packages/components/src/toggle-control/README.md) for more info. + +### children + +Components to be rendered as content. + +- Type: `Element` +- Required: No. + diff --git a/packages/interface/src/components/preferences-modal-section/README.md b/packages/interface/src/components/preferences-modal-section/README.md new file mode 100644 index 0000000000000..6e78ca05c0aa8 --- /dev/null +++ b/packages/interface/src/components/preferences-modal-section/README.md @@ -0,0 +1,30 @@ + +`PreferencesModalSection` renders a section (as a fieldset) meant to be used with `PreferencesModal`. + +## Example + +See the `PreferencesModal` readme for usage info. + + +## Props + +### title + +The title of the section + +- Type: `String` +- Required: Yes. + +### description + +The description for the section. + +- Type: `String` +- Required: No. + +### children + +Components to be rendered as content. + +- Type: `Element` +- Required: Yes. diff --git a/packages/interface/src/components/preferences-modal/README.md b/packages/interface/src/components/preferences-modal/README.md new file mode 100644 index 0000000000000..b56e7e036af04 --- /dev/null +++ b/packages/interface/src/components/preferences-modal/README.md @@ -0,0 +1,81 @@ +# PreferencesModal + +`PreferencesModal` renders a modal with editor preferences. It can take multiple sections, which are split into tabs. On small viewports, the modal is fullscreen and the tabs are closed by default. Markup differs between small and large viewports. + +This component implements a `Modal` component from the `@wordpress/components` package. + +Sections passed to this component should use `PreferencesModalSection` component from the `@wordpress/interface` package, and can also use `PreferencesModalBaseOption`, from the same package, to implement toggle buttons. + + +## Example + +```jsx +function MyEditorPreferencesModal() { + const sections = [ + { + name: 'section 1', + tabLabel: __( 'Section 1' ), + content: ( + + + + ) + + } + { + name: 'section 2', + tabLabel: __( 'Section 2' ), + content: ( + + + { isChecked !== areCustomFieldsEnabled && ( + + ) } + + + ) + + } + ] + return ( + + ); +} +``` + +## Props + +### closeModal + +A function to call when closing the modal. + +- Type: `Function` +- Required: Yes. + +### sections + +Sections to populate the modal with. Takes an array of objects, where each should include `name`, `tablabel` and `content`. + +- Type: `Array` +- Required: Yes. From f58d859036afcd4b98b252979228e13a877abf55 Mon Sep 17 00:00:00 2001 From: tellthemachines Date: Thu, 3 Mar 2022 15:46:34 +1100 Subject: [PATCH 5/8] Update readme code example. Co-authored-by: Daniel Richards --- packages/interface/src/components/preferences-modal/README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/interface/src/components/preferences-modal/README.md b/packages/interface/src/components/preferences-modal/README.md index b56e7e036af04..dfd1b9f739114 100644 --- a/packages/interface/src/components/preferences-modal/README.md +++ b/packages/interface/src/components/preferences-modal/README.md @@ -59,7 +59,7 @@ function MyEditorPreferencesModal() { } ] return ( - + ); } ``` From 68918a0065773e12cb7b0213ee6bd12c2957d991 Mon Sep 17 00:00:00 2001 From: tellthemachines Date: Thu, 3 Mar 2022 17:06:44 +1100 Subject: [PATCH 6/8] Split out tabs from modal. --- packages/interface/src/components/index.js | 1 + .../preferences-modal-tabs/README.md | 14 ++ .../preferences-modal-tabs/index.js | 153 ++++++++++++++++++ .../preferences-modal-tabs/style.scss | 27 ++++ .../components/preferences-modal/README.md | 14 +- .../src/components/preferences-modal/index.js | 153 +----------------- .../components/preferences-modal/style.scss | 28 ---- packages/interface/src/style.scss | 1 + 8 files changed, 205 insertions(+), 186 deletions(-) create mode 100644 packages/interface/src/components/preferences-modal-tabs/README.md create mode 100644 packages/interface/src/components/preferences-modal-tabs/index.js create mode 100644 packages/interface/src/components/preferences-modal-tabs/style.scss diff --git a/packages/interface/src/components/index.js b/packages/interface/src/components/index.js index 41677b69d1439..0683cf7f8ea5c 100644 --- a/packages/interface/src/components/index.js +++ b/packages/interface/src/components/index.js @@ -7,5 +7,6 @@ export { default as MoreMenuDropdown } from './more-menu-dropdown'; export { default as MoreMenuFeatureToggle } from './more-menu-feature-toggle'; export { default as ActionItem } from './action-item'; export { default as PreferencesModal } from './preferences-modal'; +export { default as PreferencesModalTabs } from './preferences-modal-tabs'; export { default as PreferencesModalSection } from './preferences-modal-section'; export { default as PreferencesModalBaseOption } from './preferences-modal-base-option'; diff --git a/packages/interface/src/components/preferences-modal-tabs/README.md b/packages/interface/src/components/preferences-modal-tabs/README.md new file mode 100644 index 0000000000000..e1ffbd86a41b6 --- /dev/null +++ b/packages/interface/src/components/preferences-modal-tabs/README.md @@ -0,0 +1,14 @@ +# PreferencesModalTabs + +`PreferencesModalTabs` creates a tabbed interface meant to be used inside a `PreferencesModal`. Markup differs between small and large viewports; on small the tabs are closed by default. + +## Example + +See the `PreferencesModal` readme for usage info. +## Props +### sections + +Sections to populate the modal with. Takes an array of objects, where each should include `name`, `tablabel` and `content`. + +- Type: `Array` +- Required: Yes. \ No newline at end of file diff --git a/packages/interface/src/components/preferences-modal-tabs/index.js b/packages/interface/src/components/preferences-modal-tabs/index.js new file mode 100644 index 0000000000000..46875f7a8057c --- /dev/null +++ b/packages/interface/src/components/preferences-modal-tabs/index.js @@ -0,0 +1,153 @@ +/** + * WordPress dependencies + */ +import { useViewportMatch } from '@wordpress/compose'; +import { + __experimentalNavigatorProvider as NavigatorProvider, + __experimentalNavigatorScreen as NavigatorScreen, + __experimentalNavigatorButton as NavigatorButton, + __experimentalNavigatorBackButton as NavigatorBackButton, + __experimentalItemGroup as ItemGroup, + __experimentalItem as Item, + __experimentalHStack as HStack, + __experimentalText as Text, + __experimentalTruncate as Truncate, + FlexItem, + TabPanel, + Card, + CardHeader, + CardBody, +} from '@wordpress/components'; +import { useMemo, useCallback, useState } from '@wordpress/element'; +import { chevronLeft, chevronRight, Icon } from '@wordpress/icons'; +import { isRTL, __ } from '@wordpress/i18n'; + +const PREFERENCES_MENU = 'preferences-menu'; + +export default function PreferencesModalTabs( { sections } ) { + const isLargeViewport = useViewportMatch( 'medium' ); + + // This is also used to sync the two different rendered components + // between small and large viewports. + const [ activeMenu, setActiveMenu ] = useState( PREFERENCES_MENU ); + /** + * Create helper objects from `sections` for easier data handling. + * `tabs` is used for creating the `TabPanel` and `sectionsContentMap` + * is used for easier access to active tab's content. + */ + const { tabs, sectionsContentMap } = useMemo( () => { + let mappedTabs = { + tabs: [], + sectionsContentMap: {}, + }; + if ( sections.length ) { + mappedTabs = sections.reduce( + ( accumulator, { name, tabLabel: title, content } ) => { + accumulator.tabs.push( { name, title } ); + accumulator.sectionsContentMap[ name ] = content; + return accumulator; + }, + { tabs: [], sectionsContentMap: {} } + ); + } + return mappedTabs; + }, [ sections ] ); + + const getCurrentTab = useCallback( + ( tab ) => sectionsContentMap[ tab.name ] || null, + [ sectionsContentMap ] + ); + + let modalContent; + // We render different components based on the viewport size. + if ( isLargeViewport ) { + modalContent = ( + + { getCurrentTab } + + ); + } else { + modalContent = ( + + + + + + { tabs.map( ( tab ) => { + return ( + + + + + { tab.title } + + + + + + + + ); + } ) } + + + + + { sections.length && + sections.map( ( section ) => { + return ( + + + + + + { section.tabLabel } + + + { section.content } + + + ); + } ) } + + ); + } + + return modalContent; +} diff --git a/packages/interface/src/components/preferences-modal-tabs/style.scss b/packages/interface/src/components/preferences-modal-tabs/style.scss new file mode 100644 index 0000000000000..b69165e12c577 --- /dev/null +++ b/packages/interface/src/components/preferences-modal-tabs/style.scss @@ -0,0 +1,27 @@ +$vertical-tabs-width: 160px; + +.interface-preferences__tabs { + .components-tab-panel__tabs { + position: absolute; + top: $header-height + $grid-unit-30; + // Aligns button text instead of button box. + left: $grid-unit-20; + width: $vertical-tabs-width; + .components-tab-panel__tabs-item { + border-radius: $radius-block-ui; + font-weight: 400; + &.is-active { + background: $gray-100; + box-shadow: none; + font-weight: 500; + } + &:focus:not(:disabled) { + box-shadow: inset 0 0 0 var(--wp-admin-border-width-focus) var(--wp-admin-theme-color); + } + } + } + .components-tab-panel__tab-content { + padding-left: $grid-unit-30; + margin-left: $vertical-tabs-width; + } +} diff --git a/packages/interface/src/components/preferences-modal/README.md b/packages/interface/src/components/preferences-modal/README.md index dfd1b9f739114..0e28bec2fad41 100644 --- a/packages/interface/src/components/preferences-modal/README.md +++ b/packages/interface/src/components/preferences-modal/README.md @@ -1,6 +1,6 @@ # PreferencesModal -`PreferencesModal` renders a modal with editor preferences. It can take multiple sections, which are split into tabs. On small viewports, the modal is fullscreen and the tabs are closed by default. Markup differs between small and large viewports. +`PreferencesModal` renders a modal with editor preferences. It can take a `PreferencesModalTabs` component, which accepts multiple tabs, and/or other child components. On small viewports, the modal is fullscreen. This component implements a `Modal` component from the `@wordpress/components` package. @@ -11,6 +11,7 @@ Sections passed to this component should use `PreferencesModalSection` component ```jsx function MyEditorPreferencesModal() { + const { closeModal } = useDispatch( editPostStore ); const sections = [ { name: 'section 1', @@ -59,7 +60,9 @@ function MyEditorPreferencesModal() { } ] return ( - + + + ); } ``` @@ -72,10 +75,3 @@ A function to call when closing the modal. - Type: `Function` - Required: Yes. - -### sections - -Sections to populate the modal with. Takes an array of objects, where each should include `name`, `tablabel` and `content`. - -- Type: `Array` -- Required: Yes. diff --git a/packages/interface/src/components/preferences-modal/index.js b/packages/interface/src/components/preferences-modal/index.js index 4604b1acf92ae..b91be8b21204a 100644 --- a/packages/interface/src/components/preferences-modal/index.js +++ b/packages/interface/src/components/preferences-modal/index.js @@ -1,155 +1,10 @@ /** * WordPress dependencies */ -import { useViewportMatch } from '@wordpress/compose'; -import { - __experimentalNavigatorProvider as NavigatorProvider, - __experimentalNavigatorScreen as NavigatorScreen, - __experimentalNavigatorButton as NavigatorButton, - __experimentalNavigatorBackButton as NavigatorBackButton, - __experimentalItemGroup as ItemGroup, - __experimentalItem as Item, - __experimentalHStack as HStack, - __experimentalText as Text, - __experimentalTruncate as Truncate, - FlexItem, - Modal, - TabPanel, - Card, - CardHeader, - CardBody, -} from '@wordpress/components'; -import { useMemo, useCallback, useState } from '@wordpress/element'; -import { chevronLeft, chevronRight, Icon } from '@wordpress/icons'; -import { isRTL, __ } from '@wordpress/i18n'; - -const PREFERENCES_MENU = 'preferences-menu'; - -export default function PreferencesModal( { closeModal, sections } ) { - const isLargeViewport = useViewportMatch( 'medium' ); - - // This is also used to sync the two different rendered components - // between small and large viewports. - const [ activeMenu, setActiveMenu ] = useState( PREFERENCES_MENU ); - /** - * Create helper objects from `sections` for easier data handling. - * `tabs` is used for creating the `TabPanel` and `sectionsContentMap` - * is used for easier access to active tab's content. - */ - const { tabs, sectionsContentMap } = useMemo( () => { - let mappedTabs = { - tabs: [], - sectionsContentMap: {}, - }; - if ( sections.length ) { - mappedTabs = sections.reduce( - ( accumulator, { name, tabLabel: title, content } ) => { - accumulator.tabs.push( { name, title } ); - accumulator.sectionsContentMap[ name ] = content; - return accumulator; - }, - { tabs: [], sectionsContentMap: {} } - ); - } - return mappedTabs; - }, [ sections ] ); - - const getCurrentTab = useCallback( - ( tab ) => sectionsContentMap[ tab.name ] || null, - [ sectionsContentMap ] - ); - - let modalContent; - // We render different components based on the viewport size. - if ( isLargeViewport ) { - modalContent = ( - - { getCurrentTab } - - ); - } else { - modalContent = ( - - - - - - { tabs.map( ( tab ) => { - return ( - - - - - { tab.title } - - - - - - - - ); - } ) } - - - - - { sections.length && - sections.map( ( section ) => { - return ( - - - - - - { section.tabLabel } - - - { section.content } - - - ); - } ) } - - ); - } +import { Modal } from '@wordpress/components'; +import { __ } from '@wordpress/i18n'; +export default function PreferencesModal( { closeModal, children } ) { return ( - { modalContent } + { children } ); } diff --git a/packages/interface/src/components/preferences-modal/style.scss b/packages/interface/src/components/preferences-modal/style.scss index 1f11dfbc34acf..6b0fa2ddcc391 100644 --- a/packages/interface/src/components/preferences-modal/style.scss +++ b/packages/interface/src/components/preferences-modal/style.scss @@ -1,5 +1,3 @@ -$vertical-tabs-width: 160px; - .interface-preferences-modal { // To keep modal dimensions consistent as subsections are navigated, width // and height are used instead of max-(width/height). @@ -30,32 +28,6 @@ $vertical-tabs-width: 160px; } } - .interface-preferences__tabs { - .components-tab-panel__tabs { - position: absolute; - top: $header-height + $grid-unit-30; - // Aligns button text instead of button box. - left: $grid-unit-20; - width: $vertical-tabs-width; - .components-tab-panel__tabs-item { - border-radius: $radius-block-ui; - font-weight: 400; - &.is-active { - background: $gray-100; - box-shadow: none; - font-weight: 500; - } - &:focus:not(:disabled) { - box-shadow: inset 0 0 0 var(--wp-admin-border-width-focus) var(--wp-admin-theme-color); - } - } - } - .components-tab-panel__tab-content { - padding-left: $grid-unit-30; - margin-left: $vertical-tabs-width; - } - } - .components-base-control__help { margin: -$grid-unit-10 0 $grid-unit-10 58px; font-size: $helptext-font-size; diff --git a/packages/interface/src/style.scss b/packages/interface/src/style.scss index ecabe0c827f2b..be111640a0a79 100644 --- a/packages/interface/src/style.scss +++ b/packages/interface/src/style.scss @@ -5,5 +5,6 @@ @import "./components/more-menu-dropdown/style.scss"; @import "./components/pinned-items/style.scss"; @import "./components/preferences-modal/style.scss"; +@import "./components/preferences-modal-tabs/style.scss"; @import "./components/preferences-modal-section/style.scss"; @import "./components/preferences-modal-base-option/style.scss"; From 6d11d8ffb25dc8548da34ae0c11950a44a9ace03 Mon Sep 17 00:00:00 2001 From: tellthemachines Date: Fri, 4 Mar 2022 11:45:09 +1100 Subject: [PATCH 7/8] Mark base option component unstable. --- packages/interface/src/components/index.js | 2 +- .../preferences-modal-base-option/README.md | 23 +++++++++++++++---- .../components/preferences-modal/README.md | 12 ++-------- 3 files changed, 22 insertions(+), 15 deletions(-) diff --git a/packages/interface/src/components/index.js b/packages/interface/src/components/index.js index 0683cf7f8ea5c..aa03fa34ae8c6 100644 --- a/packages/interface/src/components/index.js +++ b/packages/interface/src/components/index.js @@ -9,4 +9,4 @@ export { default as ActionItem } from './action-item'; export { default as PreferencesModal } from './preferences-modal'; export { default as PreferencesModalTabs } from './preferences-modal-tabs'; export { default as PreferencesModalSection } from './preferences-modal-section'; -export { default as PreferencesModalBaseOption } from './preferences-modal-base-option'; +export { default as ___unstablePreferencesModalBaseOption } from './preferences-modal-base-option'; diff --git a/packages/interface/src/components/preferences-modal-base-option/README.md b/packages/interface/src/components/preferences-modal-base-option/README.md index c76e8c8cd8539..03c89960b6850 100644 --- a/packages/interface/src/components/preferences-modal-base-option/README.md +++ b/packages/interface/src/components/preferences-modal-base-option/README.md @@ -1,13 +1,28 @@ -#PreferencesModalBaseOption +#__unstablePreferencesModalBaseOption -`PreferencesModalBaseOption` renders a toggle meant to be used with `PreferencesModal`. +`__unstablePreferencesModalBaseOption` renders a toggle meant to be used with `PreferencesModal`. This component implements a `ToggleControl` component from the `@wordpress/components` package. -## Example +**It is an unstable component so is subject to breaking changes at any moment. Use at own risk.** -See the `PreferencesModal` readme for usage info. +## Example +```jsx +function MyEditorPreferencesOption() { + return ( + <__unstablePreferencesModalBaseOption + label={ label } + isChecked={ isChecked } + onChange={ setIsChecked } + > + { isChecked !== areCustomFieldsEnabled && ( + + ) } + + ) +} +``` ## Props diff --git a/packages/interface/src/components/preferences-modal/README.md b/packages/interface/src/components/preferences-modal/README.md index 0e28bec2fad41..96ecdf03dcc13 100644 --- a/packages/interface/src/components/preferences-modal/README.md +++ b/packages/interface/src/components/preferences-modal/README.md @@ -4,7 +4,7 @@ This component implements a `Modal` component from the `@wordpress/components` package. -Sections passed to this component should use `PreferencesModalSection` component from the `@wordpress/interface` package, and can also use `PreferencesModalBaseOption`, from the same package, to implement toggle buttons. +Sections passed to this component should use `PreferencesModalSection` component from the `@wordpress/interface` package. ## Example @@ -45,15 +45,7 @@ function MyEditorPreferencesModal() { 'Add extra areas to the editor.' ) } > - - { isChecked !== areCustomFieldsEnabled && ( - - ) } - + // Section content here ) From a7daf1e78daa9fe30653ef6820e5a0758cd9da0a Mon Sep 17 00:00:00 2001 From: tellthemachines Date: Fri, 4 Mar 2022 13:30:58 +1100 Subject: [PATCH 8/8] Add classname and move some styles around. --- .../preferences-modal-base-option/style.scss | 7 +++++++ .../src/components/preferences-modal-tabs/index.js | 5 ++++- .../src/components/preferences-modal-tabs/style.scss | 8 ++++++++ .../src/components/preferences-modal/style.scss | 12 ------------ 4 files changed, 19 insertions(+), 13 deletions(-) diff --git a/packages/interface/src/components/preferences-modal-base-option/style.scss b/packages/interface/src/components/preferences-modal-base-option/style.scss index 3c75a96569a0f..88143354a497b 100644 --- a/packages/interface/src/components/preferences-modal-base-option/style.scss +++ b/packages/interface/src/components/preferences-modal-base-option/style.scss @@ -11,4 +11,11 @@ } } } + + .components-base-control__help { + margin: -$grid-unit-10 0 $grid-unit-10 58px; + font-size: $helptext-font-size; + font-style: normal; + color: $gray-700; + } } diff --git a/packages/interface/src/components/preferences-modal-tabs/index.js b/packages/interface/src/components/preferences-modal-tabs/index.js index 46875f7a8057c..bc8f7358b834d 100644 --- a/packages/interface/src/components/preferences-modal-tabs/index.js +++ b/packages/interface/src/components/preferences-modal-tabs/index.js @@ -76,7 +76,10 @@ export default function PreferencesModalTabs( { sections } ) { ); } else { modalContent = ( - + diff --git a/packages/interface/src/components/preferences-modal-tabs/style.scss b/packages/interface/src/components/preferences-modal-tabs/style.scss index b69165e12c577..d09c8b3572bd2 100644 --- a/packages/interface/src/components/preferences-modal-tabs/style.scss +++ b/packages/interface/src/components/preferences-modal-tabs/style.scss @@ -25,3 +25,11 @@ $vertical-tabs-width: 160px; margin-left: $vertical-tabs-width; } } + +@media (max-width: #{ ($break-medium - 1) }) { + // Keep the navigator component from overflowing the modal content area + // to ensure that sticky position elements stick where intended. + .interface-preferences__provider { + height: 100%; + } +} diff --git a/packages/interface/src/components/preferences-modal/style.scss b/packages/interface/src/components/preferences-modal/style.scss index 6b0fa2ddcc391..007b4b7c4e5e5 100644 --- a/packages/interface/src/components/preferences-modal/style.scss +++ b/packages/interface/src/components/preferences-modal/style.scss @@ -21,17 +21,5 @@ content: none; } } - // Keep the navigator component from overflowing the modal content area - // to ensure that sticky position elements stick where intended. - .components-navigator-provider { - height: 100%; - } - } - - .components-base-control__help { - margin: -$grid-unit-10 0 $grid-unit-10 58px; - font-size: $helptext-font-size; - font-style: normal; - color: $gray-700; } }