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

NEW Add ViewModeToggle component #560

Conversation

raissanorth
Copy link
Contributor

@raissanorth raissanorth commented Jul 4, 2018

This React component allows users to toggle between split, edit, and preview mode.

It utilises Reactstrap's PopoverField Dropdown, connects to a Redux store.

ViewModeToggle component:

image

ViewModeToggle component on hover:

image

Edit view mode chosen and split view mode enabled:

image

Edit view mode chosen and split view mode disabled:

image

Preview view mode chosen and hover on disabled split mode:

image

Sample of ViewModeToggle rendered within Preview component (split mode view chosen)

image

Sample of ViewModeToggle rendered within CampaignAdminList component

image


Important: The test cases introduce a dependency on the JS testing utility Enzyme (recommended by React for shallow rendering, see https://reactjs.org/docs/test-utils.html#overview)

A couple of other things to note:

  • The value for the minimum screen width to enable split view mode is derived from the CSS variable media-breakpoint-down(md) and currently hard coded in viewModeReducer.js The value for the minimum screen width is based on the prior CSS variable media-breakpoint-down(md) used in the media query, minus the width of the expended cms-panel. It is stored in the newly added constants.js file and is used in the viewModeReducer.js

  • Rather than using Reactstrap's ToolTip component, this implementation using the same div and styling as the current PHP implementation of the toggle. As a consequence the height for of 37px for this div is hard coded to 'disable' the split view option when the screen width is too narrow. While the first approach is preferable, i.e. using the button's disable property in combination with Reactstrap's ToolTip, no events are fired on the disabled button which is why the latter approach was chosen.

  • Within ViewModeToggle-test.js the creation of the shallow rendered wrapper is repeated for each test group. To avoid unnecessary repetition, , the wrapper could be defined outside of the test groups. In this case the props need to be defined before the wrapper is created. As the tests use rely on different prop values, this would need to be changed within the inidividual tests.

@robbieaverill
Copy link
Contributor

Added feedback-required/core-team (@silverstripe/core-team) since we're introducing a new React testing pattern with enzyme. This has been discussed already with @lukereative @chillu and @raissanorth internally at SilverStripe.

Copy link
Contributor

@robbieaverill robbieaverill left a comment

Choose a reason for hiding this comment

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

Awesome work! I've left a couple of minor comments.

It also looks like your IDE isn't respecting the editorconfig rules (2 spaces for indentation) - perhaps you could tweak this so it's consistently using it?

let classNames = 'btn btn-secondary icon-view last font-icon-edit-write';
if (activeState === 'edit') {
classNames += ' viewMode-selected';
}
Copy link
Contributor

Choose a reason for hiding this comment

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

We can use the classnames library for this, it's already part of admin

@@ -123,6 +125,7 @@ class Preview extends Component {
<div className="btn-toolbar">
{this.renderBackButton()}
{this.buildToolbarButtons()}
<ViewModeToggle id="view-mode-toggle-in-preview-nb" area={'preview'} />
Copy link
Contributor

Choose a reason for hiding this comment

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

Ideally we'd use the injector to inject a ViewModeToggle prop into Preview which would allow devs to customise this component in the Preview implementation if they wanted to. For example:

import { inject } from 'lib/Injector';

// ...

render() {
  const { ViewModeComponent } = this.props;
  return (
    // ..
    <ViewModeComponent { ...props } />

// ...

Preview.propTypes = {
  ViewModeComponent: PropTypes.oneOfType([PropTypes.node, PropTypes.func]).isRequired,
};

// ...

export default inject(
  ['ViewModeToggle'],
  (ViewModeToggle) => ({
    ViewModeComponent: ViewModeToggle
  }),
  'Admin.Preview'
);

Copy link
Contributor

Choose a reason for hiding this comment

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

You'll also need to register the component with Injector in src/boot/registerComponents.js

@@ -59,6 +59,8 @@
@import "../components/Toolbar/Toolbar";
@import "../components/TreeDropdownField/TreeDropdownField";
@import "../components/UsedOnTable/UsedOnTable";
@import "../components/IframeDialog/IframeDialog";
Copy link
Contributor

Choose a reason for hiding this comment

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

Is this referenced somewhere else?

background: rgba(255, 255, 255, 0);

&::before {
content: "Screen size too small";
Copy link
Contributor

Choose a reason for hiding this comment

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

Is there any way of adding this with javascript or PHP? We can't make this string translatable if it's in CSS

Copy link
Contributor

Choose a reason for hiding this comment

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

Rather than having a :before pseudo element, you could have a span styled the same and injected to the .disabled-tooltip div element, this will enable translatable strings and not require any hacks.

If you want the hacked version, use the attr() css function and pick an arbitrary attribute to get the translation string from.

Copy link
Contributor

@flamerohr flamerohr left a comment

Choose a reason for hiding this comment

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

I glazed over the test file, I'll review it when I get more time
Overall it's a decent implementation - the main blocker for me is that the "tooltip" for disabled options could be improved.

import { selectEditMode, selectPreviewMode, selectSplitMode } from 'state/viewMode/viewModeActions';
import classNames from 'classnames';

/* global window */
Copy link
Contributor

Choose a reason for hiding this comment

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

is there a need to declare the window global anymore?

case 'preview':
return 'font-icon-eye';
default:
return 'font-icon-columns';
Copy link
Contributor

Choose a reason for hiding this comment

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

the indent for these lines seem off, they're 4 spaces instead of 2.
Could you have a map of the classes somewhere? possibly provided through props, or another extensible means. It would make customising the icons by an external author possible.

case 'preview':
return i18n._t('Admin.PREVIEW_MODE', 'Preview mode');
default:
return i18n._t('Admin.SPLIT_MODE', 'Split mode');
Copy link
Contributor

Choose a reason for hiding this comment

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

Similar with the above comment

background: rgba(255, 255, 255, 0);

&::before {
content: "Screen size too small";
Copy link
Contributor

Choose a reason for hiding this comment

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

Rather than having a :before pseudo element, you could have a span styled the same and injected to the .disabled-tooltip div element, this will enable translatable strings and not require any hacks.

If you want the hacked version, use the attr() css function and pick an arbitrary attribute to get the translation string from.

display: none;
background: #555;
left: 47%;
top: -6px;
Copy link
Contributor

Choose a reason for hiding this comment

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

if you implement the span suggestion above, you can then use borders to implement the "speech bubble" effect which will reduce a lot of this juggling around with css3 rotate and other specific pixel placements.

export function selectEditMode() {
return {
type: ACTION_TYPES.SELECT_EDIT,
payload: null,
Copy link
Contributor

Choose a reason for hiding this comment

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

can just omit the payload for all these actions...
Also indent is all over the place for this file

export function enableOrDisableSplitMode(panelWidth) {
return {
type: ACTION_TYPES.SPLIT_AVAILABLE,
panelWidth
Copy link
Contributor

Choose a reason for hiding this comment

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

put panelWidth in a payload object:

{
  type: ACTION_SOMETHING,
  payload: { panelWidth },
};

@flamerohr
Copy link
Contributor

I remember silverstripe-admin had a click simulator somewhere... maybe it was removed because it didn't prove useful.
If Enzyme helps, then got for it. However, I think it's important that you keep it in devDependencies instead of dependencies

@raissanorth raissanorth changed the title NEW Add ViewModeToggle component [WIP] NEW Add ViewModeToggle component Jul 9, 2018
@raissanorth raissanorth force-pushed the pulls/1/build-view-mode-toggle-component branch from 4ab0615 to a255c32 Compare July 11, 2018 05:09
@raissanorth
Copy link
Contributor Author

Hi @flamerohr, thanks a lot for your feedback. I have addressed most things you mentioned - as well as changed the component to use Dropdown instead of PopoverField @lukereative. Unfortunately, yarn throws an error when I try to add enzyme to the dev-dependencies:

image

@raissanorth
Copy link
Contributor Author

If approved I suggest we implement this in https://github.com/silverstripe/silverstripe-versioned-admin. A PR was made to silverstripe/silverstripe-campaign-admin#109 already.

@raissanorth raissanorth changed the title [WIP] NEW Add ViewModeToggle component NEW Add ViewModeToggle component Jul 11, 2018
@flamerohr
Copy link
Contributor

If approved I suggest we implement this in https://github.com/silverstripe/silverstripe-versioned-admin.

That would become a weird circular dependency with the Preview component becoming dependent on ViewModeToggle which belongs to a module which depends on this module.

Does injector return null if a component isn't registered? I remember seeing an error thrown.

Unfortunately, yarn throws an error when I try to add enzyme to the dev-dependencies.

That's a shame, you can add // eslint-disable import/no-extraneous-dependencies to the top of your test file so you can leave it in dev-dependencies...
I think there was a way to config eslint to ignore import dependencies for test files, but the above comment works as a temporary measure to get this through

@robbieaverill
Copy link
Contributor

I don't think that means move the component to versioned-admin, but add the view mode toggle to history viewer in versioned-admin when merged here

Copy link
Contributor

@flamerohr flamerohr left a comment

Choose a reason for hiding this comment

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

Approved with the condition of moving enzyme to dev-dependencies

@flamerohr
Copy link
Contributor

I don't think that means move the component to versioned-admin, but add the view mode toggle to history viewer in versioned-admin when merged here

Sweet, that makes more sense

Copy link
Contributor

@robbieaverill robbieaverill left a comment

Choose a reason for hiding this comment

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

Looking great! Super minor comments

dropdownMenuProps
} = this.props;
let { className } = this.props;
className = classNames(className);
Copy link
Contributor

Choose a reason for hiding this comment

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

I think this is a little confusing - firstly we dereference a prop then we reassign it, unclear as to whether this reassigns the prop which we shouldn't really be doing, I assume it doesn't but only the local variable. Could you instead create a const dropdownClassName = classNames(className); maybe?

left: 0;
right: 0;
height: 37px;
background: rgba(255, 255, 255, 0);
Copy link
Contributor

Choose a reason for hiding this comment

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

I think you could probably use some variables for this, and for the #555 lower. See $gray-darker etc in variables.scss and maybe use darken($gray-dark, 10%) if you need to

@@ -0,0 +1 @@
export const SPLITMODE_BREAKPOINT = 800;
Copy link
Contributor

Choose a reason for hiding this comment

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

It'd be good to add a comment describing what this is for too :)

Copy link
Contributor

Choose a reason for hiding this comment

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

I'd love to see this configurable somehow (maybe stored in the redux store instead), but out of scope for this pull request I think

SELECT_EDIT: 'SELECT_EDIT',
SELECT_PREVIEW: 'SELECT_PREVIEW',
SELECT_SPLIT: 'SELECT_SPLIT',
SPLIT_AVAILABLE: 'SPLIT_AVAILABLE',
Copy link
Contributor

Choose a reason for hiding this comment

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

Indentation is out here

@raissanorth raissanorth force-pushed the pulls/1/build-view-mode-toggle-component branch from a255c32 to 6b14802 Compare July 12, 2018 04:33
@raissanorth raissanorth changed the title NEW Add ViewModeToggle component WIP NEW Add ViewModeToggle component Jul 12, 2018
@raissanorth
Copy link
Contributor Author

raissanorth commented Jul 12, 2018

Changed the status to WIP since there are some minor design changes, discussed with @clarkepaul yesterday, still to be made.

Added all requested styling improvements.

@raissanorth raissanorth force-pushed the pulls/1/build-view-mode-toggle-component branch 2 times, most recently from a31c6d5 to f5d203f Compare July 16, 2018 00:16
@raissanorth raissanorth changed the title WIP NEW Add ViewModeToggle component NEW Add ViewModeToggle component Jul 16, 2018
@raissanorth raissanorth force-pushed the pulls/1/build-view-mode-toggle-component branch from f5d203f to 9394667 Compare July 16, 2018 23:02
This React component allows users to toggle between split, edit, and preview mode. It utilises Reactstrap's Dropdown, connects to a Redux store, and its test cases introduce a dependency on the JS testing utility Enzyme.
@raissanorth raissanorth force-pushed the pulls/1/build-view-mode-toggle-component branch from 9394667 to 5108734 Compare July 17, 2018 03:11
Copy link
Contributor

@robbieaverill robbieaverill left a comment

Choose a reason for hiding this comment

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

Some last requests for SCSS reusability =)

We could write tests for the reducer too, but it's not totally necessary and would only really be for the ACTION_TYPES.SPLIT_AVAILABLE case

@@ -0,0 +1,122 @@
.viewmode-dropdown-toggle {
margin-left: auto;
font-size: 13px;
Copy link
Contributor

Choose a reason for hiding this comment

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

You could use $font-size-base here

}

.viewModeToggle-chosenView-title {
font-size: 10px;
Copy link
Contributor

Choose a reason for hiding this comment

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

$font-size-xxs


.viewModeToggle-chosenView-title {
font-size: 10px;
line-height: 10px;
Copy link
Contributor

Choose a reason for hiding this comment

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

If there's an appropriate line-height variable in _variables.scss or _bootstrap-variables.scss in admin that'd be ideal to use


.viewmode-dropdown-toggle {
&:before {
font-size: 18px;
Copy link
Contributor

Choose a reason for hiding this comment

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

$h3-font-size: 17px or $h2-font-size: 19px - if you could use one of those we'd be reducing customisation away from core

box-shadow: inset 0 3px 5px rgba(0, 0, 0, 0.125), 0 0 0 0.2rem rgba(86, 107, 141, 0.5);
border-color: #e7ebf0;
background-color: transparent;
color: #566b8d;
Copy link
Contributor

Choose a reason for hiding this comment

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

Is there any way we can swap these out for variables, or even darken() or lighten() calls on variables that are close to these values already?

}



Copy link
Contributor

Choose a reason for hiding this comment

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

Just one empty line at the end would be cool

Copy link
Contributor

@robbieaverill robbieaverill left a comment

Choose a reason for hiding this comment

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

Looks awesome!

@robbieaverill robbieaverill merged commit 792002f into silverstripe:1 Jul 17, 2018
@robbieaverill robbieaverill deleted the pulls/1/build-view-mode-toggle-component branch July 17, 2018 04:43
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants