diff --git a/src/Forms/TextField.php b/src/Forms/TextField.php index c3a920c9e..2f87da55f 100644 --- a/src/Forms/TextField.php +++ b/src/Forms/TextField.php @@ -17,24 +17,9 @@ class TextField extends FormField protected $schemaDataType = FormField::SCHEMA_DATA_TYPE_TEXT; /** - * @var bool Whether the Tip UI should be rendered + * @var Tip|null A tip to render beside the input */ - protected $tipEnabled = false; - - /** - * @var string The contents of the Tip UI - */ - protected $tipMessage = ''; - - /** - * @var string How important the tip is (normal or high). Informs the color and description. - */ - protected $tipImportance = 'normal'; - - /** - * @var string The icon that should be used on the Tip button - */ - protected $tipIcon = 'lamp'; + protected $tip; /** * Returns an input field. @@ -80,39 +65,24 @@ class TextField extends FormField } /** - * Enables the Tip UI, which shows a popover on the right side of the field - * to place additional context or explanation of the field's purpose in. - * Currently only supported in React-based TextFields. - * - * @param string $message - * @param null|string $importance How important the tip is (normal or high); Informs the color and description - * @param string $icon An icon from the SilverStripe icon font - * @return $this + * @return Tip|null */ - public function enableTip($message, $importance = null, $icon = null) + public function getTip() { - $this->tipEnabled = true; - $this->tipMessage = $message; - - if ($importance) { - $this->tipImportance = $importance; - } - - if ($icon) { - $this->tipIcon = $icon; - } - - return $this; + return $this->tip; } /** - * Disables the Tip UI. The previous configuration is retained. + * Applies a Tip to the field, which shows a popover on the right side of + * the input to place additional context or explanation of the field's + * purpose in. Currently only supported in React-based forms. * + * @param Tip|null $tip The Tip to apply, or null to remove an existing one * @return $this */ - public function disableTip() + public function setTip(Tip $tip = null) { - $this->tipEnabled = false; + $this->tip = $tip; return $this; } @@ -142,12 +112,8 @@ class TextField extends FormField $data = parent::getSchemaDataDefaults(); $data['data']['maxlength'] = $this->getMaxLength(); - if ($this->tipEnabled) { - $data['tip'] = [ - 'content' => $this->tipMessage, - 'importance' => $this->tipImportance, - 'icon' => $this->tipIcon, - ]; + if ($this->tip instanceof Tip) { + $data['tip'] = $this->tip->getTipSchema(); } return $data; diff --git a/src/Forms/Tip.php b/src/Forms/Tip.php new file mode 100644 index 000000000..9ad86220e --- /dev/null +++ b/src/Forms/Tip.php @@ -0,0 +1,126 @@ + 'normal', + 'HIGH' => 'high', + ]; + + const DEFAULT_ICON = 'lamp'; + + const DEFAULT_IMPORTANCE_LEVEL = self::IMPORTANCE_LEVELS['NORMAL']; + + /** + * @var string The icon that should be used on the Tip button + */ + private $icon; + + /** + * @var string How important the tip is (normal or high). Informs the color and description. + */ + private $importance_level; + + /** + * @var string The contents of the Tip UI + */ + private $message; + + public function __construct( + $message, + $importance_level = self::DEFAULT_IMPORTANCE_LEVEL, + $icon = self::DEFAULT_ICON + ) + { + if (!in_array($importance_level, self::IMPORTANCE_LEVELS)) { + throw new InvalidArgumentException( + 'Provided $importance_level must be defined in Tip::IMPORTANCE_LEVELS' + ); + } + + $this->message = $message; + $this->icon = $icon; + $this->importance_level = $importance_level; + } + + /** + * Outputs props to be passed to the front-end Tip component. + * + * @return array + */ + public function getTipSchema() + { + return [ + 'content' => $this->message, + 'icon' => $this->icon, + 'importance' => $this->importance_level, + ]; + } + + /** + * @return string + */ + public function getImportanceLevel() + { + return $this->importance_level; + } + + /** + * @param string $importance_level + */ + public function setImportanceLevel($importance_level) + { + if (!in_array($importance_level, self::IMPORTANCE_LEVELS)) { + throw new InvalidArgumentException( + 'Provided $importance_level must be defined in Tip::IMPORTANCE_LEVELS' + ); + } + + $this->importance_level = $importance_level; + } + + /** + * @return string + */ + public function getIcon(): string + { + return $this->icon; + } + + /** + * @param string $icon + */ + public function setIcon($icon) + { + $this->icon = $icon; + } + + /** + * @return string + */ + public function getMessage() + { + return $this->message; + } + + /** + * @param string $message + */ + public function setMessage($message) + { + $this->message = $message; + } +} diff --git a/tests/php/Forms/TextFieldTest.php b/tests/php/Forms/TextFieldTest.php index 380dff5bf..77acc0461 100644 --- a/tests/php/Forms/TextFieldTest.php +++ b/tests/php/Forms/TextFieldTest.php @@ -5,6 +5,7 @@ namespace SilverStripe\Forms\Tests; use SilverStripe\Dev\SapphireTest; use SilverStripe\Forms\TextField; use SilverStripe\Forms\RequiredFields; +use SilverStripe\Forms\Tip; class TextFieldTest extends SapphireTest { @@ -32,4 +33,16 @@ class TextFieldTest extends SapphireTest $result = $textField->validate(new RequiredFields()); $this->assertTrue($result); } + + /** + * Ensures that when a Tip is applied to the field, it outputs it in the schema + */ + public function testTipIsIncludedInSchema() + { + $textField = new TextField('TestField'); + $this->assertArrayNotHasKey('tip', $textField->getSchemaDataDefaults()); + + $textField->setTip(new Tip('TestTip')); + $this->assertArrayHasKey('tip', $textField->getSchemaDataDefaults()); + } } diff --git a/tests/php/Forms/TipTest.php b/tests/php/Forms/TipTest.php new file mode 100644 index 000000000..d579934e8 --- /dev/null +++ b/tests/php/Forms/TipTest.php @@ -0,0 +1,76 @@ +getTipSchema(); + + $this->assertEquals( + [ + 'content' => 'message', + 'icon' => 'lamp', + 'importance' => 'normal', + ], + $schema + ); + } + + /** + * Ensure custom settings are output in the schema + */ + public function testGeneratesAccurateCustomSchema() + { + $tip = new Tip( + 'message', + Tip::IMPORTANCE_LEVELS['HIGH'], + 'page' + ); + + $schema = $tip->getTipSchema(); + + $this->assertEquals( + [ + 'content' => 'message', + 'icon' => 'page', + 'importance' => 'high', + ], + $schema + ); + } + + /** + * Ensure passing an invalid importance level to the constructor fails + * + * @expectedException InvalidArgumentException + * @expectedExceptionMessage Provided $importance_level must be defined in Tip::IMPORTANCE_LEVELS + */ + public function testInvalidImportanceLevelInConstructorCausesException() + { + $tip = new Tip('message', 'arbitrary-importance'); + } + + /** + * Ensure setting an invalid importance level fails + * + * @expectedException InvalidArgumentException + * @expectedExceptionMessage Provided $importance_level must be defined in Tip::IMPORTANCE_LEVELS + */ + public function testInvalidImportanceLevelInSetterCausesException() + { + $tip = new Tip('message'); + + $tip->setImportanceLevel('arbitrary-importance'); + } +}