Commit 161a3532 authored by webchick's avatar webchick

Issue #1734642 by attiks, Jelle_S, Gábor Hojtsy: Added Move breakpoint module into core.

parent 920fd45a
......@@ -173,6 +173,9 @@ Block module
Book module
- Peter Wolanin 'pwolanin' http://drupal.org/user/49851
Breakpoint module
- Peter Droogmans 'attiks' http://drupal.org/user/105002
Color module
- ?
......
name = Breakpoint
description = Manage breakpoints and breakpoint groups for responsive designs.
package = Core
version = VERSION
core = 8.x
dependencies[] = config
This diff is collapsed.
This diff is collapsed.
<?php
/**
* @file
* Definition of Drupal\breakpoint\BreakpointGroup.
*/
namespace Drupal\breakpoint;
use Drupal\Core\Config\Entity\ConfigEntityBase;
use Drupal\breakpoint\InvalidBreakpointSourceException;
use Drupal\breakpoint\InvalidBreakpointSourceTypeException;
/**
* Defines the BreakpointGroup entity.
*/
class BreakpointGroup extends ConfigEntityBase {
/**
* The breakpoint group ID.
*
* @var string
*/
public $id;
/**
* The breakpoint group UUID.
*
* @var string
*/
public $uuid;
/**
* The breakpoint group machine name.
*
* @var string
*/
public $name;
/**
* The breakpoint group label.
*
* @var string
*/
public $label;
/**
* The breakpoint group breakpoints.
*
* @var array
* Array containing all breakpoints of this group.
*
* @see Drupal\breakpoints\Breakpoint
*/
public $breakpoints = array();
/**
* The breakpoint group source: theme or module name. Use 'user' for
* user-created groups.
*
* @var string
*/
public $source = 'user';
/**
* The breakpoint group source type.
*
* @var string
* Allowed values:
* Breakpoint::SOURCE_TYPE_THEME
* Breakpoint::SOURCE_TYPE_MODULE
* Breakpoint::SOURCE_TYPE_USER_DEFINED
*
* @see Drupal\breakpoint\Breakpoint
*/
public $sourceType = Breakpoint::SOURCE_TYPE_USER_DEFINED;
/**
* Overrides Drupal\config\ConfigEntityBase::__construct().
*/
public function __construct(array $values, $entity_type) {
parent::__construct($values, $entity_type);
$this->loadAllBreakpoints();
}
/**
* Overrides Drupal\Core\Entity\Entity::save().
*/
public function save() {
// Check if everything is valid.
if (!$this->isValid()) {
throw new InvalidBreakpointException('Invalid data detected.');
}
if (empty($this->id)) {
$this->id = $this->sourceType . '.' . $this->source . '.' . $this->name;
}
// Only save the keys, but return the full objects.
$this->breakpoints = array_keys($this->breakpoints);
parent::save();
$this->loadAllBreakpoints();
}
/**
* Checks if the breakpoint group is valid.
*
* @throws Drupal\breakpoint\InvalidBreakpointSourceTypeException
* @throws Drupal\breakpoint\InvalidBreakpointSourceException
*
* @return true
* Returns true if the breakpoint group is valid.
*/
public function isValid() {
// Check for illegal values in breakpoint group source type.
if (!in_array($this->sourceType, array(
Breakpoint::SOURCE_TYPE_USER_DEFINED,
Breakpoint::SOURCE_TYPE_MODULE,
Breakpoint::SOURCE_TYPE_THEME)
)) {
throw new InvalidBreakpointSourceTypeException(format_string('Invalid source type @source_type', array(
'@source_type' => $this->sourceType,
)));
}
// Check for illegal characters in breakpoint group source.
if (preg_match('/[^a-z_]+/', $this->source) || empty($this->source)) {
throw new InvalidBreakpointSourceException(format_string("Invalid value '@source' for breakpoint group source property. Breakpoint group source property can only contain lowercase letters and underscores.", array('@source' => $this->source)));
}
// Check for illegal characters in breakpoint group name.
if (preg_match('/[^a-z0-9_]+/', $this->name || empty($this->name))) {
throw new InvalidBreakpointNameException(format_string("Invalid value '@name' for breakpoint group name property. Breakpoint group name property can only contain lowercase letters, numbers and underscores.", array('@name' => $this->name)));
}
return TRUE;
}
/**
* Adds a breakpoint using a name and a media query.
*
* @param string $name
* The name of the breakpoint.
* @param string $media_query
* Media query.
*/
public function addBreakpointFromMediaQuery($name, $media_query) {
// Use the existing breakpoint if it exists.
$breakpoint = entity_load('breakpoint', $this->sourceType . '.' . $this->name . '.' . $name);
if (!$breakpoint) {
// Build a new breakpoint.
$breakpoint = entity_create('breakpoint', array(
'name' => $name,
'label' => $name,
'mediaQuery' => $media_query,
'source' => $this->name,
'sourceType' => $this->sourceType,
'weight' => count($this->breakpoints),
));
$breakpoint->save();
}
$this->breakpoints[$breakpoint->id()] = $breakpoint;
}
/**
* Adds one or more breakpoints to this group.
*
* The breakpoint name is either the machine_name or the id of a breakpoint.
*
* @param array $breakpoints
* Array containing breakpoints keyed by their id.
*/
public function addBreakpoints($breakpoints) {
foreach ($breakpoints as $breakpoint_name) {
// Check if breakpoint exists, assume $breakpoint_name is a machine name.
$breakpoint = entity_load('breakpoint', $this->sourceType . '.' . $this->source . '.' . $breakpoint_name);
// If the breakpoint doesn't exist, assume $breakpoint_name is an id.
if (!$breakpoint) {
$breakpoint = entity_load('breakpoint', $breakpoint_name);
}
// If the breakpoint doesn't exists, do not add it.
if ($breakpoint) {
// Add breakpoint to group.
$this->breakpoints[$breakpoint->id()] = $breakpoint;
}
}
}
/**
* Loads all breakpoints, remove non-existing ones.
*
* @return array
* Array containing breakpoints keyed by their id.
*/
protected function loadAllBreakpoints() {
$breakpoints = $this->breakpoints;
$this->breakpoints = array();
foreach ($breakpoints as $breakpoint_id) {
$breakpoint = breakpoint_load($breakpoint_id);
if ($breakpoint) {
$this->breakpoints[$breakpoint_id] = $breakpoint;
}
}
}
}
<?php
/**
* @file
* Definition of Drupal\breakpoint\InvalidBreakpointException.
*/
namespace Drupal\breakpoint;
/**
* Base exception for breakpoint exception.
*/
class InvalidBreakpointException extends \RuntimeException {}
<?php
/**
* @file
* Definition of Drupal\breakpoint\InvalidBreakpointMediaQueryException.
*/
namespace Drupal\breakpoint;
use Drupal\breakpoint\InvalidBreakpointException;
/**
* Exception thrown if an illegal media query is detected.
*/
class InvalidBreakpointMediaQueryException extends InvalidBreakpointException {}
<?php
/**
* @file
* Definition of Drupal\breakpoint\InvalidBreakpointNameException.
*/
namespace Drupal\breakpoint;
use Drupal\breakpoint\InvalidBreakpointException;
/**
* Exception thrown if an invalid name is detected.
*/
class InvalidBreakpointNameException extends InvalidBreakpointException {}
<?php
/**
* @file
* Definition of Drupal\breakpoint\InvalidBreakpointSourceException.
*/
namespace Drupal\breakpoint;
use Drupal\breakpoint\InvalidBreakpointException;
/**
* Exception thrown if an invalid source is detected.
*/
class InvalidBreakpointSourceException extends InvalidBreakpointException {}
<?php
/**
* @file
* Definition of Drupal\breakpoint\InvalidBreakpointSourceTypeException.
*/
namespace Drupal\breakpoint;
use Drupal\breakpoint\InvalidBreakpointException;
/**
* Exception thrown if an invalid source_type is detected.
*/
class InvalidBreakpointSourceTypeException extends InvalidBreakpointException {}
<?php
/**
* @file
* Definition of Drupal\breakpoint\Tests\BreakpointAPITest.
*/
namespace Drupal\breakpoint\Tests;
use Drupal\breakpoint\Tests\BreakpointsTestBase;
use Drupal\breakpoint\Breakpoint;
use Drupal\breakpoint\InvalidBreakpointNameException;
use Drupal\breakpoint\InvalidBreakpointSourceException;
use Drupal\breakpoint\InvalidBreakpointSourceTypeException;
/**
* Tests for general breakpoint API functions.
*/
class BreakpointAPITest extends BreakpointTestBase {
public static function getInfo() {
return array(
'name' => 'Breakpoint general API functions',
'description' => 'Test general API functions of the breakpoint module.',
'group' => 'Breakpoint',
);
}
/**
* Test Breakpoint::buildConfigName().
*/
public function testConfigName() {
// Try an invalid sourceType.
$breakpoint = entity_create('breakpoint', array(
'label' => drupal_strtolower($this->randomName()),
'source' => 'custom_module',
'sourceType' => 'oops',
));
$exception = FALSE;
try {
$breakpoint->save();
}
catch (InvalidBreakpointSourceTypeException $e) {
$exception = TRUE;
}
$this->assertTrue($exception, 'breakpoint_config_name: An exception is thrown when an invalid sourceType is entered.');
// Try an invalid source.
$breakpoint->id = '';
$breakpoint->sourceType = Breakpoint::SOURCE_TYPE_USER_DEFINED;
$breakpoint->source = 'custom*_module source';
$exception = FALSE;
try {
$breakpoint->save();
}
catch (InvalidBreakpointSourceException $e) {
$exception = TRUE;
}
$this->assertTrue($exception, 'breakpoint_config_name: An exception is thrown when an invalid source is entered.');
// Try an invalid name (make sure there is at least once capital letter).
$breakpoint->id = '';
$breakpoint->source = 'custom_module';
$breakpoint->name = drupal_ucfirst($this->randomName());
$exception = FALSE;
try {
$breakpoint->save();
}
catch (InvalidBreakpointNameException $e) {
$exception = TRUE;
}
$this->assertTrue($exception, 'breakpoint_config_name: An exception is thrown when an invalid name is entered.');
// Try a valid breakpoint.
$breakpoint->id = '';
$breakpoint->name = drupal_strtolower($this->randomName());
$breakpoint->mediaQuery = 'all';
$exception = FALSE;
try {
$breakpoint->save();
}
catch (\Exception $e) {
$exception = TRUE;
}
$this->assertFalse($exception, 'breakpoint_config_name: No exception is thrown when a valid breakpoint is passed.');
$this->assertEqual($breakpoint->id(), Breakpoint::SOURCE_TYPE_USER_DEFINED . '.custom_module.' . $breakpoint->name, 'breakpoint_config_name: A id is set when a valid breakpoint is passed.');
}
}
<?php
/**
* @file
* Definition of Drupal\breakpoint\Tests\BreakpointCRUDTest.
*/
namespace Drupal\breakpoint\Tests;
use Drupal\breakpoint\Tests\BreakpointTestBase;
use Drupal\breakpoint\Breakpoint;
/**
* Tests for breakpoint CRUD operations.
*/
class BreakpointCRUDTest extends BreakpointTestBase {
public static function getInfo() {
return array(
'name' => 'Breakpoint CRUD operations',
'description' => 'Test creation, loading, updating, deleting of breakpoints.',
'group' => 'Breakpoint',
);
}
/**
* Test CRUD operations for breakpoints.
*/
public function testBreakpointCRUD() {
// Add a breakpoint with minimum data only.
$breakpoint = entity_create('breakpoint', array(
'label' => drupal_strtolower($this->randomName()),
'mediaQuery' => '(min-width: 600px)',
));
$breakpoint->save();
$this->verifyBreakpoint($breakpoint);
// Test breakpoint_load_all
$all_breakpoints = entity_load_multiple('breakpoint');
$config_name = $breakpoint->id();
$this->assertTrue(isset($all_breakpoints[$config_name]), 'breakpoint_load_all: New breakpoint is present when loading all breakpoints.');
$this->verifyBreakpoint($breakpoint, $all_breakpoints[$config_name]);
// Update the breakpoint.
$breakpoint->weight = 1;
$breakpoint->multipliers['2x'] = '2x';
$breakpoint->save();
$this->verifyBreakpoint($breakpoint);
// Delete the breakpoint.
$breakpoint->delete();
$this->assertFalse(breakpoint_load($config_name), 'breakpoint_load: Loading a deleted breakpoint returns false.', 'Breakpoints API');
}
}
<?php
/**
* @file
* Definition of Drupal\breakpoint\Tests\BreakpointGroupAPITest.
*/
namespace Drupal\breakpoint\Tests;
use Drupal\breakpoint\Tests\BreakpointsTestBase;
use Drupal\breakpoint\BreakpointGroup;
use Drupal\breakpoint\Breakpoint;
use Drupal\breakpoint\InvalidBreakpointNameException;
use Drupal\breakpoint\InvalidBreakpointSourceException;
use Drupal\breakpoint\InvalidBreakpointSourceTypeException;
/**
* Tests for general breakpoint group API functions.
*/
class BreakpointGroupAPITest extends BreakpointGroupTestBase {
public static function getInfo() {
return array(
'name' => 'Breakpoint group general API functions',
'description' => 'Test general API functions of the breakpoint module.',
'group' => 'Breakpoint',
);
}
/**
* Test Breakpoint::buildConfigName().
*/
public function testConfigName() {
// Try an invalid sourceType.
$label = $this->randomName();
$breakpoint_group = entity_create('breakpoint_group', array(
'label' => $label,
'name' => drupal_strtolower($label),
'source' => 'custom_module',
'sourceType' => 'oops',
));
$exception = FALSE;
try {
$breakpoint_group->save();
}
catch (InvalidBreakpointSourceTypeException $e) {
$exception = TRUE;
}
$this->assertTrue($exception, 'An exception is thrown when an invalid sourceType is entered.');
// Try an invalid source.
$breakpoint_group->name = '';
$breakpoint_group->sourceType = Breakpoint::SOURCE_TYPE_USER_DEFINED;
$breakpoint_group->source = 'custom*_module source';
$exception = FALSE;
try {
$breakpoint_group->save();
}
catch (InvalidBreakpointSourceException $e) {
$exception = TRUE;
}
$this->assertTrue($exception, 'An exception is thrown when an invalid source is entered.');
// Try a valid breakpoint_group.
$breakpoint_group->name = 'test';
$breakpoint_group->source = 'custom_module_source';
$exception = FALSE;
try {
$breakpoint_group->save();
}
catch (\Exception $e) {
$exception = TRUE;
}
$this->assertFalse($exception, 'No exception is thrown when a valid data is passed.');
}
}
<?php
/**
* @file
* Definition of Drupal\breakpoint\Tests\BreakpointGroupCRUDTest.
*/
namespace Drupal\breakpoint\Tests;
use Drupal\breakpoint\Tests\BreakpointGroupTestBase;
use Drupal\breakpoint\BreakpointGroup;
use Drupal\breakpoint\Breakpoint;
/**
* Tests for breakpoint group CRUD operations.
*/
class BreakpointGroupCRUDTest extends BreakpointGroupTestBase {
public static function getInfo() {
return array(
'name' => 'Breakpoint group CRUD operations',
'description' => 'Test creation, loading, updating, deleting of breakpoint groups.',
'group' => 'Breakpoint',
);
}
/**
* Test CRUD operations for breakpoint groups.
*/
public function testBreakpointGroupCRUD() {
// Add breakpoints.
$breakpoints = array();
for ($i = 0; $i <= 3; $i++) {
$width = ($i + 1) * 200;
$breakpoint = entity_create('breakpoint', array(
'name' => drupal_strtolower($this->randomName()),
'weight' => $i,
'mediaQuery' => "(min-width: {$width}px)",
));
$breakpoint->save();
$breakpoints[$breakpoint->id()] = $breakpoint;
}
// Add a breakpoint group with minimum data only.
$label = $this->randomName();
$group = entity_create('breakpoint_group', array(
'label' => $label,
'name' => drupal_strtolower($label),
));
$group->save();
$this->verifyBreakpointGroup($group);
// Update the breakpoint group.
$group->breakpoints = array_keys($breakpoints);
$group->save();
$this->verifyBreakpointGroup($group);
// Delete the breakpoint group.
$group->delete();
$this->assertFalse(entity_load('breakpoint_group', $group->id()), 'breakpoint_group_load: Loading a deleted breakpoint group returns false.', 'Breakpoints API');
}
}
<?php
/**
* @file
* Definition of Drupal\breakpoint\Tests\BreakpointGroupTestBase.
*/
namespace Drupal\breakpoint\Tests;
use Drupal\simpletest\WebTestBase;
use Drupal\breakpoint\BreakpointGroup;
/**
* Base class for Breakpoint group tests.
*/
abstract class BreakpointGroupTestBase extends WebTestBase {
/**
* Modules to enable.
*
* @var array
*/
public static $modules = array('breakpoint');
public function setUp() {
parent::setUp();
}
/**
* Verify that a breakpoint is properly stored.
*/
public function verifyBreakpointGroup(BreakpointGroup $group, BreakpointGroup $compare_set = NULL) {
$properties = array(
'label',
'id',
'name',
'breakpoints',
'sourceType',
);
// Verify breakpoint_group_load().
$compare_set = is_null($compare_set) ? entity_load('breakpoint_group', $group->id()) : $compare_set;
foreach ($properties as $property) {
$t_args = array(
'%group' => $group->label(),
'%property' => $property,
);
if (is_array($compare_set->{$property})) {
$this->assertEqual(array_keys($compare_set->{$property}), array_keys($group->{$property}), format_string('breakpoint_group_load: Proper %property for breakpoint group %group.', $t_args), 'Breakpoint API');
}
else {
$t_args = array(
'%group' => $group->label(),
'%property' => $property,
'%property1' => $compare_set->{$property},
'%property2' => $group->{$property},
);
$this->assertEqual($compare_set->{$property}, $group->{$property}, format_string('breakpoint_group_load: Proper %property: %property1 == %property2 for breakpoint group %group.', $t_args), 'Breakpoint API');
}
}
}
}
<?php
/**
* @file
* Definition of Drupal\breakpoint\Tests\BreakpointMediaQueryTest.
*/
namespace Drupal\breakpoint\Tests;
use Drupal\simpletest\UnitTestBase;
use Drupal\breakpoint\Breakpoint;
use Drupal\breakpoint\InvalidBreakpointMediaQueryException;
/**
* Tests for media queries in a breakpoint.
*/
class BreakpointMediaQueryTest extends UnitTestBase {
public static function getInfo() {
return array(
'name' => 'Breakpoint media query tests',
'description' => 'Test validation of media queries.',
'group' => 'Breakpoint',
);
}
/**
* Test valid media queries.
*/
public function testValidMediaQueries() {
$media_queries = array(
// Bartik breakpoints.
'(min-width: 0px)',
'all and (min-width: 560px) and (max-width:850px)',
'all and (min-width: 851px)',
// Seven breakpoints.
'(min-width: 0em)',
'screen and (min-width: 40em)',