Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add a basic Options modal #10215

Merged
merged 14 commits into from
Oct 17, 2018
Merged
Show file tree
Hide file tree
Changes from 13 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions docs/reference/deprecated.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,10 @@
Gutenberg's deprecation policy is intended to support backwards-compatibility for two minor releases, when possible. The current deprecations are listed below and are grouped by _the version at which they will be removed completely_. If your plugin depends on these behaviors, you must update to the recommended alternative before the noted version.

# 4.3.0

- `isEditorSidebarPanelOpened` selector (`core/edit-post`) has been removed. Please use `isEditorPanelEnabled` instead.
- `toggleGeneralSidebarEditorPanel` action (`core/edit-post`) has been removed. Please use `toggleEditorPanelOpened` instead.

## 4.2.0

- Writing resolvers as async generators has been removed. Use the controls plugin instead.
Expand Down
6 changes: 5 additions & 1 deletion packages/edit-post/src/components/header/more-menu/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
* WordPress dependencies
*/
import { _x } from '@wordpress/i18n';
import { IconButton, Dropdown } from '@wordpress/components';
import { IconButton, Dropdown, MenuGroup } from '@wordpress/components';
import { Fragment } from '@wordpress/element';

/**
Expand All @@ -11,6 +11,7 @@ import { Fragment } from '@wordpress/element';
import ModeSwitcher from '../mode-switcher';
import PluginMoreMenuGroup from '../plugins-more-menu-group';
import ToolsMoreMenuGroup from '../tools-more-menu-group';
import OptionsMenuItem from '../options-menu-item';
import WritingMenu from '../writing-menu';

const MoreMenu = () => (
Expand All @@ -32,6 +33,9 @@ const MoreMenu = () => (
<ModeSwitcher onSelect={ onClose } />
<PluginMoreMenuGroup.Slot fillProps={ { onClose } } />
<ToolsMoreMenuGroup.Slot fillProps={ { onClose } } />
<MenuGroup>
<OptionsMenuItem onSelect={ onClose } />
</MenuGroup>
</Fragment>
) }
/>
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
/**
* WordPress Dependencies
*/
import { withDispatch } from '@wordpress/data';
import { __ } from '@wordpress/i18n';
import { MenuItem } from '@wordpress/components';

export function OptionsMenuItem( { openModal, onSelect } ) {
return (
<MenuItem
onClick={ () => {
onSelect();
openModal( 'edit-post/options' );
} }
>
{ __( 'Options' ) }
</MenuItem>
);
}

export default withDispatch( ( dispatch ) => {
const { openModal } = dispatch( 'core/edit-post' );

return {
openModal,
};
} )( OptionsMenuItem );
2 changes: 2 additions & 0 deletions packages/edit-post/src/components/layout/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@ import TextEditor from '../text-editor';
import VisualEditor from '../visual-editor';
import EditorModeKeyboardShortcuts from '../keyboard-shortcuts';
import KeyboardShortcutHelpModal from '../keyboard-shortcut-help-modal';
import OptionsModal from '../options-modal';
import MetaBoxes from '../meta-boxes';
import Sidebar from '../sidebar';
import PluginPostPublishPanel from '../sidebar/plugin-post-publish-panel';
Expand Down Expand Up @@ -81,6 +82,7 @@ function Layout( {
<PreserveScrollInReorder />
<EditorModeKeyboardShortcuts />
<KeyboardShortcutHelpModal />
<OptionsModal />
{ mode === 'text' && <TextEditor /> }
{ mode === 'visual' && <VisualEditor /> }
<div className="edit-post-layout__metaboxes">
Expand Down
70 changes: 70 additions & 0 deletions packages/edit-post/src/components/options-modal/index.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
/**
* External dependencies
*/
import { get } from 'lodash';

/**
* WordPress dependencies
*/
import { Modal } from '@wordpress/components';
import { __ } from '@wordpress/i18n';
import { withSelect, withDispatch } from '@wordpress/data';
import { compose } from '@wordpress/compose';
import { PostTaxonomies, PostExcerptCheck, PageAttributesCheck } from '@wordpress/editor';

/**
* Internal dependencies
*/
import Section from './section';
import { EnablePublishSidebarOption, EnableTipsOption, EnablePanelOption } from './options';
gziolo marked this conversation as resolved.
Show resolved Hide resolved

const MODAL_NAME = 'edit-post/options';

export function OptionsModal( { isModalActive, closeModal } ) {
if ( ! isModalActive ) {
return null;
}

return (
<Modal
gziolo marked this conversation as resolved.
Show resolved Hide resolved
className="edit-post-options-modal"
title={ <span className="edit-post-options-modal__title">{ __( 'Options' ) }</span> }
closeLabel={ __( 'Close' ) }
onRequestClose={ closeModal }
>
<Section title={ __( 'General' ) }>
<EnablePublishSidebarOption label={ __( 'Enable Pre-publish Checks' ) } />
<EnableTipsOption label={ __( 'Enable Tips' ) } />
</Section>
<Section title={ __( 'Document Panels' ) }>
<PostTaxonomies
noisysocks marked this conversation as resolved.
Show resolved Hide resolved
taxonomyWrapper={ ( content, taxonomy ) => (
<EnablePanelOption
label={ get( taxonomy, [ 'labels', 'menu_name' ] ) }
panelName={ `taxonomy-panel-${ taxonomy.slug }` }
/>
) }
/>
<EnablePanelOption label={ __( 'Featured Image' ) } panelName="featured-image" />
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Those panel names seem to be very inconsistent. Should we do something about it? This is what we have:

  • post-status
  • taxonomy-panel-*
  • featured-image
  • post-excerpt
  • discussion-panel

We have related fills and filters named as follows:

  • PluginPostStatusInfo
  • editor.PostTaxonomyType
  • editor.PostFeaturedImage

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🤔🤔🤔

OK, so currently we have:

Component Identifier
PostStatus post-status
PostTaxonomies taxonomy-panel-*
FeaturedImage featured-image
PostExcerpt post-excerpt
DiscussionPanel discussion-panel
PageAttributes page-attributes

Here's my proposal:

Component Identifier
StatusPanel status
PostTaxonomiesPanel post-taxonomy-*
FeaturedImagePanel featured-image
PostExcerptPanel post-excerpt
DiscussionPanel discussion
PageAttributesPanel page-attributes

These names ensure:

  • Identifiers consistently don't contain the word panel.
  • Component names end with Panel which properly describes what the UI component actually is.
  • Panels that only apply to posts begin with post-.
  • Panels that only apply to pages begin with page-.

What do you think? Is it worth changing the names? It's tricky to do this and ensure backwards compatibility with the stored preferences.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

When you check closely code of the panels, it seems like all of them but PageAttributesPanel should be prefixed with Post. I also agree that all of components should end with Panel. My vote would be for:

Component Identifier
PostStatusPanel post-status
PostTaxonomiesPanel post-taxonomy-*
PostFeaturedImagePanel post-featured-image
PostExcerptPanel post-excerpt
PostDiscussionPanel post-discussion
PageAttributesPanel page-attributes

What do you think?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

By the way, this can be done in a seperate PR, this isn't a blocker at all as it existed before. It would be nice to have it standardized as we will be referencing them in the modal dialog. By they way, do you think we should put those values in constants or would it be overkill?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

By they way, do you think we should put those values in constants or would it be overkill?

It's probably overkill. We don't use constants for other identifiers e.g. block names and modal names.

When you check closely code of the panels, it seems like all of them but PageAttributesPanel should be prefixed with Post.

I disagree. I think:

  • Panels that appear on both posts and pages (e.g. Discussion) should have no prefix
  • Panels that appear on posts but not pages (e.g. PostExcept) should have a Post prefix
  • Panels that appear on pages but not posts (e.g. PageAttributes) should have a Page prefix

Copy link
Member

@gziolo gziolo Oct 17, 2018

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Panels that appear on both posts and pages (e.g. Discussion) should have no prefix

I like your reasoning. I think it's more complex in the WordPress world because everything is a post both Post and Page which makes it all even more confusing.

I'm wondering how Classic Editor handles it. Well, it depends:

screen shot 2018-10-17 at 08 45 05

screen shot 2018-10-17 at 08 45 49

<PostExcerptCheck>
<EnablePanelOption label={ __( 'Excerpt' ) } panelName="post-excerpt" />
</PostExcerptCheck>
<EnablePanelOption label={ __( 'Discussion' ) } panelName="discussion-panel" />
<PageAttributesCheck>
<EnablePanelOption label={ __( 'Page Attributes' ) } panelName="page-attributes" />
</PageAttributesCheck>
</Section>
</Modal>
);
}

export default compose(
withSelect( ( select ) => ( {
isModalActive: select( 'core/edit-post' ).isModalActive( MODAL_NAME ),
} ) ),
withDispatch( ( dispatch ) => {
return {
closeModal: () => dispatch( 'core/edit-post' ).closeModal(),
};
} )
)( OptionsModal );
81 changes: 81 additions & 0 deletions packages/edit-post/src/components/options-modal/options.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,81 @@
/**
* WordPress dependencies
*/
import { CheckboxControl } from '@wordpress/components';
import { Component } from '@wordpress/element';
import { compose } from '@wordpress/compose';
import { withSelect, withDispatch } from '@wordpress/data';

function Option( { label, isChecked, onChange } ) {
return (
<CheckboxControl
className="edit-post-options-modal__option"
label={ label }
checked={ isChecked }
onChange={ onChange }
/>
);
}

class DeferredOption extends Component {
constructor( { isChecked } ) {
super( ...arguments );
this.state = {
isChecked,
};
}

componentWillUnmount() {
if ( this.state.isChecked !== this.props.isChecked ) {
this.props.onChange( this.state.isChecked );
}
}

render() {
return (
<Option
label={ this.props.label }
isChecked={ this.state.isChecked }
onChange={ ( isChecked ) => this.setState( { isChecked } ) }
/>
);
}
}

export const EnablePublishSidebarOption = compose(
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This file is very compositiony! Really nice use of composition. 😄

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah I'm pretty pleased with this!

withSelect( ( select ) => ( {
isChecked: select( 'core/editor' ).isPublishSidebarEnabled(),
} ) ),
withDispatch( ( dispatch ) => {
const { enablePublishSidebar, disablePublishSidebar } = dispatch( 'core/editor' );
return {
onChange: ( isEnabled ) => ( isEnabled ? enablePublishSidebar() : disablePublishSidebar() ),
};
} )
)( Option );

export const EnableTipsOption = compose(
withSelect( ( select ) => ( {
isChecked: select( 'core/nux' ).areTipsEnabled(),
} ) ),
withDispatch( ( dispatch ) => {
const { enableTips, disableTips } = dispatch( 'core/nux' );
return {
onChange: ( isEnabled ) => ( isEnabled ? enableTips() : disableTips() ),
};
} )
)(
// Using DeferredOption here means enableTips() is called when the Options
// modal is dismissed. This stops the NUX guide from appearing above the
// Options modal, which looks totally weird.
DeferredOption
);

export const EnablePanelOption = compose(
withSelect( ( select, { panelName } ) => ( {
isChecked: select( 'core/edit-post' ).isEditorPanelEnabled( panelName ),
} ) ),
withDispatch( ( dispatch, { panelName } ) => ( {
onChange: () => dispatch( 'core/edit-post' ).toggleEditorPanelEnabled( panelName ),
} ) )
)( Option );
8 changes: 8 additions & 0 deletions packages/edit-post/src/components/options-modal/section.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
const Section = ( { title, children } ) => (
<section className="edit-post-options-modal__section">
<h2 className="edit-post-options-modal__section-title">{ title }</h2>
{ children }
</section>
);

export default Section;
36 changes: 36 additions & 0 deletions packages/edit-post/src/components/options-modal/style.scss
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
.edit-post-options-modal {
min-width: 450px;

&__title {
font-size: 1rem;
font-weight: bold;
}

&__section {
margin: 0 0 2rem 0;
}

&__section-title {
font-size: 0.9rem;
font-weight: bold;
}

&__option {
border-top: 1px solid $light-gray-500;

&:last-child {
border-bottom: 1px solid $light-gray-500;
}

.components-base-control__field {
align-items: center;
display: flex;
margin: 0;
}

.components-checkbox-control__label {
flex-grow: 1;
padding: 0.6rem 0 0.6rem 10px;
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP

exports[`OptionsModal should match snapshot when the modal is active 1`] = `
<WithInstanceId(Modal)
className="edit-post-options-modal"
closeLabel="Close"
title={
<span
className="edit-post-options-modal__title"
>
Options
</span>
}
>
<Section
title="General"
>
<WithSelect(WithDispatch(Option))
label="Enable Pre-publish Checks"
/>
<WithSelect(WithDispatch(DeferredOption))
label="Enable Tips"
/>
</Section>
<Section
title="Document Panels"
>
<WithSelect(PostTaxonomies)
taxonomyWrapper={[Function]}
/>
<WithSelect(WithDispatch(Option))
label="Featured Image"
panelName="featured-image"
/>
<PostExcerptCheck>
<WithSelect(WithDispatch(Option))
label="Excerpt"
panelName="post-excerpt"
/>
</PostExcerptCheck>
<WithSelect(WithDispatch(Option))
label="Discussion"
panelName="discussion-panel"
/>
<WithSelect(PageAttributesCheck)>
<WithSelect(WithDispatch(Option))
label="Page Attributes"
panelName="page-attributes"
/>
</WithSelect(PageAttributesCheck)>
</Section>
</WithInstanceId(Modal)>
`;
21 changes: 21 additions & 0 deletions packages/edit-post/src/components/options-modal/test/index.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
/**
* External dependencies
*/
import { shallow } from 'enzyme';

/**
* Internal dependencies
*/
import { OptionsModal } from '../';

describe( 'OptionsModal', () => {
it( 'should match snapshot when the modal is active', () => {
const wrapper = shallow( <OptionsModal isModalActive={ true } /> );
expect( wrapper ).toMatchSnapshot();
} );

it( 'should not render when the modal is not active', () => {
const wrapper = shallow( <OptionsModal isModalActive={ false } /> );
expect( wrapper.isEmptyRender() ).toBe( true );
} );
} );
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,11 @@ import { withSelect, withDispatch } from '@wordpress/data';
*/
const PANEL_NAME = 'discussion-panel';

function DiscussionPanel( { isOpened, onTogglePanel } ) {
function DiscussionPanel( { isEnabled, isOpened, onTogglePanel } ) {
if ( ! isEnabled ) {
return null;
}

return (
<PostTypeSupportCheck supportKeys={ [ 'comments', 'trackbacks' ] }>
<PanelBody title={ __( 'Discussion' ) } opened={ isOpened } onToggle={ onTogglePanel }>
Expand All @@ -35,12 +39,13 @@ function DiscussionPanel( { isOpened, onTogglePanel } ) {
export default compose( [
withSelect( ( select ) => {
return {
isOpened: select( 'core/edit-post' ).isEditorSidebarPanelOpened( PANEL_NAME ),
isEnabled: select( 'core/edit-post' ).isEditorPanelEnabled( PANEL_NAME ),
isOpened: select( 'core/edit-post' ).isEditorPanelOpened( PANEL_NAME ),
};
} ),
withDispatch( ( dispatch ) => ( {
onTogglePanel() {
return dispatch( 'core/edit-post' ).toggleGeneralSidebarEditorPanel( PANEL_NAME );
return dispatch( 'core/edit-post' ).toggleEditorPanelOpened( PANEL_NAME );
},
} ) ),
] )( DiscussionPanel );
Expand Down
Loading