Skip to content

Commit

Permalink
API Add a trait to bootstrap ViewableData into react component
Browse files Browse the repository at this point in the history
  • Loading branch information
Maxime Rainville committed Mar 4, 2021
1 parent 036bc9e commit 1628428
Show file tree
Hide file tree
Showing 10 changed files with 261 additions and 8 deletions.
2 changes: 2 additions & 0 deletions client/src/boot/registerComponents.js
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import { Field as ReduxFormField } from 'redux-form';
import Injector from 'lib/Injector';
import ActionMenu from 'components/ActionMenu/ActionMenu';
import CmsSiteName from 'components/CmsSiteName/CmsSiteName';
import Badge from 'components/Badge/Badge';
import Button from 'components/Button/Button';
import BackButton from 'components/Button/BackButton';
Expand Down Expand Up @@ -100,5 +101,6 @@ export default () => {
NumberField,
PopoverOptionSet,
ToastsContainer,
CmsSiteName
});
};
1 change: 1 addition & 0 deletions client/src/bundles/bundle.js
Original file line number Diff line number Diff line change
Expand Up @@ -100,6 +100,7 @@ require('../legacy/AddToCampaignForm');
require('../legacy/SecurityAdmin');
require('../legacy/ModelAdmin');
require('../legacy/ToastsContainer');
require('../legacy/BootstrapComponent');

// Legacy form fields
// Fields used by core legacy UIs, or available to users
Expand Down
21 changes: 21 additions & 0 deletions client/src/components/CmsSiteName/CmsSiteName.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
import React from 'react';
import PropTypes from 'prop-types';
import classnames from 'classnames';

const CmsSiteName = ({ title, baseHref }) => (
<div className="cms-sitename">
<a href="#" className="cms-sitename__link font-icon-silverstripe font-icon-large" target="_blank">
</a>
<a className="cms-sitename__title" href={baseHref} target="_blank">{title}</a>
</div>
);

CmsSiteName.propTypes = {
title: PropTypes.string,
baseHref: PropTypes.string,
};

CmsSiteName.defaultProps = {
};

export default CmsSiteName;
48 changes: 48 additions & 0 deletions client/src/legacy/BootstrapComponent.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
/* global ss */
import jQuery from 'jquery';
import React from 'react';
import ReactDOM from 'react-dom';
import { loadComponent } from 'lib/Injector';

jQuery.entwine('ss', ($) => {
$('.js-injector-boot .bootstrap-component').entwine({

Component: null,

onmatch() {
const cmsContent = this.closest('.cms-content').attr('id');
const context = (cmsContent)
? { context: cmsContent }
: {};

const componentName = this.data('component');
const Component = loadComponent(componentName, context);

this.setComponent(Component);
this._super();
this.refresh();
},

refresh() {
const props = this.getProps();
const Component = this.getComponent();
ReactDOM.render(<Component {...props} />, this[0]);
},

/**
* Find the selected node and get attributes associated to attach the data to the form
*
* @returns {Object}
*/
getProps() {
return $(this).data('props') || {};
},

/**
* Remove the component when unmatching
*/
onunmatch() {
ReactDOM.unmountComponentAtNode(this[0]);
},
});
});
10 changes: 8 additions & 2 deletions code/LeftAndMain.php
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
use InvalidArgumentException;
use LogicException;
use ReflectionClass;
use SilverStripe\Admin\React\SiteName;
use SilverStripe\CMS\Controllers\SilverStripeNavigator;
use SilverStripe\Control\ContentNegotiator;
use SilverStripe\Control\Controller;
Expand Down Expand Up @@ -1110,6 +1111,11 @@ public function Menu()
return $this->renderWith($this->getTemplatesWithSuffix('_Menu'));
}

public function SiteName()
{
return new SiteName();
}

/**
* @todo Wrap in CMSMenu instance accessor
* @return ArrayData A single menu entry (see {@link MainMenu})
Expand Down Expand Up @@ -1658,11 +1664,11 @@ public function currentPageID()
if (isset($this->urlParams['ID']) && is_numeric($this->urlParams['ID'])) {
return $this->urlParams['ID'];
}

if (is_numeric($this->getRequest()->param('ID'))) {
return $this->getRequest()->param('ID');
}

/** @deprecated */
$session = $this->getRequest()->getSession();
return $session->get($this->sessionNamespace() . ".currentPage") ?: null;
Expand Down
152 changes: 152 additions & 0 deletions code/React/BootstrapComponent.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,152 @@
<?php

namespace SilverStripe\Admin\React;

use SilverStripe\Core\Convert;

trait BootstrapComponent
{

private static $casting = [
'AttributesHTML' => 'HTMLFragment'
];

private $attributes = [];

protected $extraClasses = [];

public function forTemplate()
{
$return = $this->renderWith($this->getTemplates());
return $return;
}

public function getTemplates(): array
{
return [self::class, 'SilverStripe\\Admin\\React\\BootstrapComponent'];
}

public function getAttributesHTML($attrs = null)
{
$exclude = (is_string($attrs)) ? func_get_args() : null;

$attrs = $this->getAttributes();

// Remove empty
$attrs = array_filter((array)$attrs, function ($value) {
return ($value || $value === 0);
});

// Remove excluded
if ($exclude) {
$attrs = array_diff_key($attrs, array_flip($exclude));
}

// Prepare HTML-friendly 'method' attribute (lower-case)
if (isset($attrs['method'])) {
$attrs['method'] = strtolower($attrs['method']);
}

// Create markup
$parts = [];
foreach ($attrs as $name => $value) {
if ($value === true) {
$value = $name;
}

$parts[] = sprintf('%s="%s"', Convert::raw2att($name), Convert::raw2att($value));
}

return implode(' ', $parts);
}

/**
* @param string $name
* @param string $value
* @return $this
*/
public function setAttribute($name, $value)
{
$this->attributes[$name] = $value;
return $this;
}

/**
* @param string $name
* @return string
*/
public function getAttribute($name)
{
if (isset($this->attributes[$name])) {
return $this->attributes[$name];
}
return null;
}

/**
* @return array
*/
public function getAttributes()
{
$attrs = [
'class' => $this->extraClass(),
'data-component' => $this->getComponent(),
'data-props' => json_encode($this->getProps()),
];

$attrs = array_merge($attrs, $this->attributes);

return $attrs;
}

/**
* Compiles all CSS-classes.
*
* @return string
*/
public function extraClass()
{
return 'bootstrap-component ' . implode(' ', array_unique($this->extraClasses));
}

/**
* Add a CSS-class to the form-container. If needed, multiple classes can
* be added by delimiting a string with spaces.
*
* @param string $class A string containing a classname or several class
* names delimited by a single space.
* @return $this
*/
public function addExtraClass($class)
{
//split at white space
$classes = preg_split('/\s+/', $class);
foreach ($classes as $class) {
//add classes one by one
$this->extraClasses[$class] = $class;
}
return $this;
}

/**
* Remove a CSS-class from the form-container. Multiple class names can
* be passed through as a space delimited string
*
* @param string $class
* @return $this
*/
public function removeExtraClass($class)
{
//split at white space
$classes = preg_split('/\s+/', $class);
foreach ($classes as $class) {
//unset one by one
unset($this->extraClasses[$class]);
}
return $this;
}

abstract public function getProps(): array;

abstract public function getComponent(): string;
}
27 changes: 27 additions & 0 deletions code/React/SiteName.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
<?php
namespace SilverStripe\Admin\React;

use SilverStripe\Admin\LeftAndMain;
use SilverStripe\Control\Director;
use SilverStripe\SiteConfig\SiteConfig;
use SilverStripe\View\ViewableData;


class SiteName extends ViewableData
{
use BootstrapComponent;

public function getProps(): array
{
$config = SiteConfig::current_site_config();
return [
'title' => $config->Title ?? LeftAndMain::config()->get('application_name'),
'baseHref' => Director::absoluteBaseURL()
];
}

public function getComponent(): string
{
return 'CmsSiteName';
}
}
2 changes: 1 addition & 1 deletion templates/SilverStripe/Admin/Includes/LeftAndMain_Menu.ss
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@

<div class="fill-height cms-menu cms-panel cms-panel-layout" id="cms-menu" data-layout-type="border" aria-expanded="false">
<div class="cms-menu__header">
<% include SilverStripe\\Admin\\LeftAndMain_MenuLogo %>
$SiteName
<% include SilverStripe\\Admin\\LeftAndMain_MenuStatus %>
</div>

Expand Down
5 changes: 0 additions & 5 deletions templates/SilverStripe/Admin/Includes/LeftAndMain_MenuLogo.ss

This file was deleted.

1 change: 1 addition & 0 deletions templates/SilverStripe/Admin/React/BootstrapComponent.ss
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
<div $AttributesHTML></div>

0 comments on commit 1628428

Please sign in to comment.