From 29da8f6dfc6b5433e19dca59d78eebe985008086 Mon Sep 17 00:00:00 2001
From: Guy Sartorelli <guy.sartorelli@signify.co.nz>
Date: Mon, 17 Jan 2022 11:34:15 +1300
Subject: [PATCH 1/2] ENH Make all GridField components injectable.

Some components were already injectable, but all GridField components shipped in silverstripe should be injectable.
This makes it a LOT easier to make global project-specific changes to a given component.
The new AbstractGridFieldComponent also makes it easy to make similar wide-spread changes in the future.
---
 .../GridField/AbstractGridFieldComponent.php  | 10 +++++++
 src/Forms/GridField/GridField.php             |  8 +++---
 .../GridFieldAddExistingAutocompleter.php     |  2 +-
 src/Forms/GridField/GridFieldAddNewButton.php |  2 +-
 src/Forms/GridField/GridFieldButtonRow.php    |  2 +-
 src/Forms/GridField/GridFieldConfig_Base.php  | 14 +++++-----
 .../GridFieldConfig_RecordEditor.php          | 24 ++++++++---------
 .../GridFieldConfig_RecordViewer.php          |  4 +--
 .../GridFieldConfig_RelationEditor.php        | 26 +++++++++----------
 src/Forms/GridField/GridFieldDataColumns.php  |  2 +-
 src/Forms/GridField/GridFieldDeleteAction.php |  6 ++---
 src/Forms/GridField/GridFieldDetailForm.php   |  5 ++--
 src/Forms/GridField/GridFieldEditButton.php   |  5 ++--
 src/Forms/GridField/GridFieldExportButton.php |  2 +-
 src/Forms/GridField/GridFieldFilterHeader.php |  2 +-
 src/Forms/GridField/GridFieldFooter.php       |  2 +-
 src/Forms/GridField/GridFieldImportButton.php |  5 +---
 src/Forms/GridField/GridFieldLazyLoader.php   |  2 +-
 src/Forms/GridField/GridFieldLevelup.php      |  5 +---
 src/Forms/GridField/GridFieldPageCount.php    |  2 +-
 src/Forms/GridField/GridFieldPaginator.php    |  2 +-
 src/Forms/GridField/GridFieldPrintButton.php  |  2 +-
 .../GridField/GridFieldSortableHeader.php     |  2 +-
 .../GridField/GridFieldToolbarHeader.php      |  2 +-
 src/Forms/GridField/GridFieldViewButton.php   |  2 +-
 src/Forms/GridField/GridField_ActionMenu.php  |  2 +-
 src/Forms/GridField/GridState_Component.php   |  2 +-
 src/Security/Group.php                        | 10 +++----
 28 files changed, 78 insertions(+), 76 deletions(-)
 create mode 100644 src/Forms/GridField/AbstractGridFieldComponent.php

diff --git a/src/Forms/GridField/AbstractGridFieldComponent.php b/src/Forms/GridField/AbstractGridFieldComponent.php
new file mode 100644
index 00000000000..5842e57feb4
--- /dev/null
+++ b/src/Forms/GridField/AbstractGridFieldComponent.php
@@ -0,0 +1,10 @@
+<?php
+
+namespace SilverStripe\Forms\GridField;
+
+use SilverStripe\Core\Injector\Injectable;
+
+abstract class AbstractGridFieldComponent implements GridFieldComponent
+{
+    use Injectable;
+}
diff --git a/src/Forms/GridField/GridField.php b/src/Forms/GridField/GridField.php
index 6c71f152860..79d59741a9a 100644
--- a/src/Forms/GridField/GridField.php
+++ b/src/Forms/GridField/GridField.php
@@ -259,7 +259,7 @@ public function performReadonlyTransformation()
 
         // If the edit button has been removed, replace it with a view button
         if ($hadEditButton && !$copyConfig->getComponentByType(GridFieldViewButton::class)) {
-            $copyConfig->addComponent(new GridFieldViewButton);
+            $copyConfig->addComponent(GridFieldViewButton::create());
         }
 
         return $copy;
@@ -295,7 +295,7 @@ public function setConfig(GridFieldConfig $config)
         $this->config = $config;
 
         if (!$this->config->getComponentByType(GridState_Component::class)) {
-            $this->config->addComponent(new GridState_Component());
+            $this->config->addComponent(GridState_Component::create());
         }
 
         return $this;
@@ -440,7 +440,7 @@ private function initState(): void
     public function FieldHolder($properties = [])
     {
         $this->extend('onBeforeRenderHolder', $this, $properties);
-        
+
         $columns = $this->getColumns();
 
         $list = $this->getManipulatedList();
@@ -655,7 +655,7 @@ public function FieldHolder($properties = [])
             $tableAttributes,
             $header . "\n" . $footer . "\n" . $body
         );
-        
+
         $message = Convert::raw2xml($this->getMessage());
         if (is_array($message)) {
             $message = $message['message'];
diff --git a/src/Forms/GridField/GridFieldAddExistingAutocompleter.php b/src/Forms/GridField/GridFieldAddExistingAutocompleter.php
index afec2e03c59..5637cee6b12 100644
--- a/src/Forms/GridField/GridFieldAddExistingAutocompleter.php
+++ b/src/Forms/GridField/GridFieldAddExistingAutocompleter.php
@@ -32,7 +32,7 @@
  * For easier setup, have a look at a sample configuration in
  * {@link GridFieldConfig_RelationEditor}.
  */
-class GridFieldAddExistingAutocompleter implements GridField_HTMLProvider, GridField_ActionProvider, GridField_DataManipulator, GridField_URLHandler
+class GridFieldAddExistingAutocompleter extends AbstractGridFieldComponent implements GridField_HTMLProvider, GridField_ActionProvider, GridField_DataManipulator, GridField_URLHandler
 {
 
     /**
diff --git a/src/Forms/GridField/GridFieldAddNewButton.php b/src/Forms/GridField/GridFieldAddNewButton.php
index 24f7cdd9c34..dffcabdcd08 100644
--- a/src/Forms/GridField/GridFieldAddNewButton.php
+++ b/src/Forms/GridField/GridFieldAddNewButton.php
@@ -15,7 +15,7 @@
  * Only returns a button if {@link DataObject->canCreate()} for this record
  * returns true.
  */
-class GridFieldAddNewButton implements GridField_HTMLProvider
+class GridFieldAddNewButton extends AbstractGridFieldComponent implements GridField_HTMLProvider
 {
 
     protected $targetFragment;
diff --git a/src/Forms/GridField/GridFieldButtonRow.php b/src/Forms/GridField/GridFieldButtonRow.php
index c62f5f8b2c4..32855478b26 100644
--- a/src/Forms/GridField/GridFieldButtonRow.php
+++ b/src/Forms/GridField/GridFieldButtonRow.php
@@ -14,7 +14,7 @@
  * This row provides two new HTML fragment spaces: 'toolbar-header-left' and
  * 'toolbar-header-right'.
  */
-class GridFieldButtonRow implements GridField_HTMLProvider
+class GridFieldButtonRow extends AbstractGridFieldComponent implements GridField_HTMLProvider
 {
 
     protected $targetFragment;
diff --git a/src/Forms/GridField/GridFieldConfig_Base.php b/src/Forms/GridField/GridFieldConfig_Base.php
index 026bab57a0f..ea080390fc9 100644
--- a/src/Forms/GridField/GridFieldConfig_Base.php
+++ b/src/Forms/GridField/GridFieldConfig_Base.php
@@ -15,13 +15,13 @@ class GridFieldConfig_Base extends GridFieldConfig
     public function __construct($itemsPerPage = null)
     {
         parent::__construct();
-        $this->addComponent(new GridFieldToolbarHeader());
-        $this->addComponent(new GridFieldButtonRow('before'));
-        $this->addComponent($sort = new GridFieldSortableHeader());
-        $this->addComponent($filter = new GridFieldFilterHeader());
-        $this->addComponent(new GridFieldDataColumns());
-        $this->addComponent(new GridFieldPageCount('toolbar-header-right'));
-        $this->addComponent($pagination = new GridFieldPaginator($itemsPerPage));
+        $this->addComponent(GridFieldToolbarHeader::create());
+        $this->addComponent(GridFieldButtonRow::create('before'));
+        $this->addComponent($sort = GridFieldSortableHeader::create());
+        $this->addComponent($filter = GridFieldFilterHeader::create());
+        $this->addComponent(GridFieldDataColumns::create());
+        $this->addComponent(GridFieldPageCount::create('toolbar-header-right'));
+        $this->addComponent($pagination = GridFieldPaginator::create($itemsPerPage));
 
         $sort->setThrowExceptionOnBadDataType(false);
         $filter->setThrowExceptionOnBadDataType(false);
diff --git a/src/Forms/GridField/GridFieldConfig_RecordEditor.php b/src/Forms/GridField/GridFieldConfig_RecordEditor.php
index 28d7b6749fd..bcb3079d95f 100644
--- a/src/Forms/GridField/GridFieldConfig_RecordEditor.php
+++ b/src/Forms/GridField/GridFieldConfig_RecordEditor.php
@@ -17,18 +17,18 @@ public function __construct($itemsPerPage = null, $showPagination = null, $showA
     {
         parent::__construct();
 
-        $this->addComponent(new GridFieldButtonRow('before'));
-        $this->addComponent(new GridFieldAddNewButton('buttons-before-left'));
-        $this->addComponent(new GridFieldToolbarHeader());
-        $this->addComponent($sort = new GridFieldSortableHeader());
-        $this->addComponent($filter = new GridFieldFilterHeader());
-        $this->addComponent(new GridFieldDataColumns());
-        $this->addComponent(new GridFieldEditButton());
-        $this->addComponent(new GridFieldDeleteAction());
-        $this->addComponent(new GridField_ActionMenu());
-        $this->addComponent(new GridFieldPageCount('toolbar-header-right'));
-        $this->addComponent($pagination = new GridFieldPaginator($itemsPerPage));
-        $this->addComponent(new GridFieldDetailForm(null, $showPagination, $showAdd));
+        $this->addComponent(GridFieldButtonRow::create('before'));
+        $this->addComponent(GridFieldAddNewButton::create('buttons-before-left'));
+        $this->addComponent(GridFieldToolbarHeader::create());
+        $this->addComponent($sort = GridFieldSortableHeader::create());
+        $this->addComponent($filter = GridFieldFilterHeader::create());
+        $this->addComponent(GridFieldDataColumns::create());
+        $this->addComponent(GridFieldEditButton::create());
+        $this->addComponent(GridFieldDeleteAction::create());
+        $this->addComponent(GridField_ActionMenu::create());
+        $this->addComponent(GridFieldPageCount::create('toolbar-header-right'));
+        $this->addComponent($pagination = GridFieldPaginator::create($itemsPerPage));
+        $this->addComponent(GridFieldDetailForm::create(null, $showPagination, $showAdd));
 
         $sort->setThrowExceptionOnBadDataType(false);
         $filter->setThrowExceptionOnBadDataType(false);
diff --git a/src/Forms/GridField/GridFieldConfig_RecordViewer.php b/src/Forms/GridField/GridFieldConfig_RecordViewer.php
index fdaa911394e..aae4d471fef 100644
--- a/src/Forms/GridField/GridFieldConfig_RecordViewer.php
+++ b/src/Forms/GridField/GridFieldConfig_RecordViewer.php
@@ -12,8 +12,8 @@ public function __construct($itemsPerPage = null)
     {
         parent::__construct($itemsPerPage);
 
-        $this->addComponent(new GridFieldViewButton());
-        $this->addComponent(new GridFieldDetailForm());
+        $this->addComponent(GridFieldViewButton::create());
+        $this->addComponent(GridFieldDetailForm::create());
         $this->removeComponentsByType(GridFieldFilterHeader::class);
 
         $this->extend('updateConfig');
diff --git a/src/Forms/GridField/GridFieldConfig_RelationEditor.php b/src/Forms/GridField/GridFieldConfig_RelationEditor.php
index 83cc4000474..2cb243f51b5 100644
--- a/src/Forms/GridField/GridFieldConfig_RelationEditor.php
+++ b/src/Forms/GridField/GridFieldConfig_RelationEditor.php
@@ -29,19 +29,19 @@ public function __construct($itemsPerPage = null)
     {
         parent::__construct();
 
-        $this->addComponent(new GridFieldButtonRow('before'));
-        $this->addComponent(new GridFieldAddNewButton('buttons-before-left'));
-        $this->addComponent(new GridFieldAddExistingAutocompleter('buttons-before-right'));
-        $this->addComponent(new GridFieldToolbarHeader());
-        $this->addComponent($sort = new GridFieldSortableHeader());
-        $this->addComponent($filter = new GridFieldFilterHeader());
-        $this->addComponent(new GridFieldDataColumns());
-        $this->addComponent(new GridFieldEditButton());
-        $this->addComponent(new GridFieldDeleteAction(true));
-        $this->addComponent(new GridField_ActionMenu());
-        $this->addComponent(new GridFieldPageCount('toolbar-header-right'));
-        $this->addComponent($pagination = new GridFieldPaginator($itemsPerPage));
-        $this->addComponent(new GridFieldDetailForm());
+        $this->addComponent(GridFieldButtonRow::create('before'));
+        $this->addComponent(GridFieldAddNewButton::create('buttons-before-left'));
+        $this->addComponent(GridFieldAddExistingAutocompleter::create('buttons-before-right'));
+        $this->addComponent(GridFieldToolbarHeader::create());
+        $this->addComponent($sort = GridFieldSortableHeader::create());
+        $this->addComponent($filter = GridFieldFilterHeader::create());
+        $this->addComponent(GridFieldDataColumns::create());
+        $this->addComponent(GridFieldEditButton::create());
+        $this->addComponent(GridFieldDeleteAction::create(true));
+        $this->addComponent(GridField_ActionMenu::create());
+        $this->addComponent(GridFieldPageCount::create('toolbar-header-right'));
+        $this->addComponent($pagination = GridFieldPaginator::create($itemsPerPage));
+        $this->addComponent(GridFieldDetailForm::create());
 
         $sort->setThrowExceptionOnBadDataType(false);
         $filter->setThrowExceptionOnBadDataType(false);
diff --git a/src/Forms/GridField/GridFieldDataColumns.php b/src/Forms/GridField/GridFieldDataColumns.php
index a56a285d3bc..39ee1ecfe43 100644
--- a/src/Forms/GridField/GridFieldDataColumns.php
+++ b/src/Forms/GridField/GridFieldDataColumns.php
@@ -9,7 +9,7 @@
 /**
  * @see GridField
  */
-class GridFieldDataColumns implements GridField_ColumnProvider
+class GridFieldDataColumns extends AbstractGridFieldComponent implements GridField_ColumnProvider
 {
 
     /**
diff --git a/src/Forms/GridField/GridFieldDeleteAction.php b/src/Forms/GridField/GridFieldDeleteAction.php
index 5f714c17703..e5bc0434991 100644
--- a/src/Forms/GridField/GridFieldDeleteAction.php
+++ b/src/Forms/GridField/GridFieldDeleteAction.php
@@ -16,13 +16,13 @@
  * Use the {@link $removeRelation} property set in the constructor.
  *
  * <code>
- * $action = new GridFieldDeleteAction(); // delete objects permanently
+ * $action = GridFieldDeleteAction::create(); // delete objects permanently
  *
  * // removes the relation to object instead of deleting
- * $action = new GridFieldDeleteAction(true);
+ * $action = GridFieldDeleteAction::create(true);
  * </code>
  */
-class GridFieldDeleteAction implements GridField_ColumnProvider, GridField_ActionProvider, GridField_ActionMenuItem
+class GridFieldDeleteAction extends AbstractGridFieldComponent implements GridField_ColumnProvider, GridField_ActionProvider, GridField_ActionMenuItem
 {
 
     /**
diff --git a/src/Forms/GridField/GridFieldDetailForm.php b/src/Forms/GridField/GridFieldDetailForm.php
index a6dae5aba63..0497dc564f6 100644
--- a/src/Forms/GridField/GridFieldDetailForm.php
+++ b/src/Forms/GridField/GridFieldDetailForm.php
@@ -11,7 +11,6 @@
 use SilverStripe\Control\RequestHandler;
 use SilverStripe\Core\ClassInfo;
 use SilverStripe\Core\Extensible;
-use SilverStripe\Core\Injector\Injectable;
 use SilverStripe\Core\Injector\Injector;
 use SilverStripe\Forms\FieldList;
 use SilverStripe\Forms\Validator;
@@ -32,10 +31,10 @@
  *  - <FormURL>/field/<GridFieldName>/item/<RecordID>
  *  - <FormURL>/field/<GridFieldName>/item/<RecordID>/edit
  */
-class GridFieldDetailForm implements GridField_URLHandler
+class GridFieldDetailForm extends AbstractGridFieldComponent implements GridField_URLHandler
 {
 
-    use Extensible, Injectable, GridFieldStateAware;
+    use Extensible, GridFieldStateAware;
 
     /**
      * @var string
diff --git a/src/Forms/GridField/GridFieldEditButton.php b/src/Forms/GridField/GridFieldEditButton.php
index 41a5b606bc8..aac826507af 100644
--- a/src/Forms/GridField/GridFieldEditButton.php
+++ b/src/Forms/GridField/GridFieldEditButton.php
@@ -3,7 +3,6 @@
 namespace SilverStripe\Forms\GridField;
 
 use SilverStripe\Control\Controller;
-use SilverStripe\Core\Injector\Injectable;
 use SilverStripe\ORM\DataObject;
 use SilverStripe\View\ArrayData;
 use SilverStripe\View\SSViewer;
@@ -18,9 +17,9 @@
  * The default routing applies to the {@link GridFieldDetailForm} component,
  * which has to be added separately to the {@link GridField} configuration.
  */
-class GridFieldEditButton implements GridField_ColumnProvider, GridField_ActionProvider, GridField_ActionMenuLink
+class GridFieldEditButton extends AbstractGridFieldComponent implements GridField_ColumnProvider, GridField_ActionProvider, GridField_ActionMenuLink
 {
-    use Injectable, GridFieldStateAware;
+    use GridFieldStateAware;
 
     /**
      * HTML classes to be added to GridField edit buttons
diff --git a/src/Forms/GridField/GridFieldExportButton.php b/src/Forms/GridField/GridFieldExportButton.php
index 35be2269e33..1954eb00628 100644
--- a/src/Forms/GridField/GridFieldExportButton.php
+++ b/src/Forms/GridField/GridFieldExportButton.php
@@ -12,7 +12,7 @@
 /**
  * Adds an "Export list" button to the bottom of a {@link GridField}.
  */
-class GridFieldExportButton implements GridField_HTMLProvider, GridField_ActionProvider, GridField_URLHandler
+class GridFieldExportButton extends AbstractGridFieldComponent implements GridField_HTMLProvider, GridField_ActionProvider, GridField_URLHandler
 {
     /**
      * @var array Map of a property name on the exported objects, with values being the column title in the CSV file.
diff --git a/src/Forms/GridField/GridFieldFilterHeader.php b/src/Forms/GridField/GridFieldFilterHeader.php
index 3d58882124f..9bd3bf37a98 100755
--- a/src/Forms/GridField/GridFieldFilterHeader.php
+++ b/src/Forms/GridField/GridFieldFilterHeader.php
@@ -26,7 +26,7 @@
  *
  * @see GridField
  */
-class GridFieldFilterHeader implements GridField_URLHandler, GridField_HTMLProvider, GridField_DataManipulator, GridField_ActionProvider, GridField_StateProvider
+class GridFieldFilterHeader extends AbstractGridFieldComponent implements GridField_URLHandler, GridField_HTMLProvider, GridField_DataManipulator, GridField_ActionProvider, GridField_StateProvider
 {
     /**
      * See {@link setThrowExceptionOnBadDataType()}
diff --git a/src/Forms/GridField/GridFieldFooter.php b/src/Forms/GridField/GridFieldFooter.php
index bb6ace848fd..0cec8cc97d7 100644
--- a/src/Forms/GridField/GridFieldFooter.php
+++ b/src/Forms/GridField/GridFieldFooter.php
@@ -18,7 +18,7 @@
  * The purpose of this class is to have a footer that can round off
  * {@link GridField} without having to use pagination.
  */
-class GridFieldFooter implements GridField_HTMLProvider
+class GridFieldFooter extends AbstractGridFieldComponent implements GridField_HTMLProvider
 {
 
     /**
diff --git a/src/Forms/GridField/GridFieldImportButton.php b/src/Forms/GridField/GridFieldImportButton.php
index e3ce0322181..61865a2dae0 100644
--- a/src/Forms/GridField/GridFieldImportButton.php
+++ b/src/Forms/GridField/GridFieldImportButton.php
@@ -2,15 +2,12 @@
 
 namespace SilverStripe\Forms\GridField;
 
-use SilverStripe\Core\Injector\Injectable;
 use SilverStripe\Forms\Form;
 use SilverStripe\View\ArrayData;
 use SilverStripe\View\SSViewer;
 
-class GridFieldImportButton implements GridField_HTMLProvider
+class GridFieldImportButton extends AbstractGridFieldComponent implements GridField_HTMLProvider
 {
-    use Injectable;
-
     /**
      * Fragment to write the button to
      */
diff --git a/src/Forms/GridField/GridFieldLazyLoader.php b/src/Forms/GridField/GridFieldLazyLoader.php
index 63a761f6e35..e117116cb76 100755
--- a/src/Forms/GridField/GridFieldLazyLoader.php
+++ b/src/Forms/GridField/GridFieldLazyLoader.php
@@ -14,7 +14,7 @@
  *
  * @see GridField
  */
-class GridFieldLazyLoader implements GridField_DataManipulator, GridField_HTMLProvider
+class GridFieldLazyLoader extends AbstractGridFieldComponent implements GridField_DataManipulator, GridField_HTMLProvider
 {
 
     /**
diff --git a/src/Forms/GridField/GridFieldLevelup.php b/src/Forms/GridField/GridFieldLevelup.php
index 7cdc015ddaa..d43838a02a5 100644
--- a/src/Forms/GridField/GridFieldLevelup.php
+++ b/src/Forms/GridField/GridFieldLevelup.php
@@ -2,7 +2,6 @@
 
 namespace SilverStripe\Forms\GridField;
 
-use SilverStripe\Core\Injector\Injectable;
 use SilverStripe\ORM\DataObject;
 use SilverStripe\ORM\FieldType\DBField;
 use SilverStripe\ORM\Hierarchy\Hierarchy;
@@ -15,10 +14,8 @@
  * hierarchical data. Requires the managed record to have a "getParent()"
  * method or has_one relationship called "Parent".
  */
-class GridFieldLevelup implements GridField_HTMLProvider
+class GridFieldLevelup extends AbstractGridFieldComponent implements GridField_HTMLProvider
 {
-    use Injectable;
-
     /**
      * @var integer - the record id of the level up to
      */
diff --git a/src/Forms/GridField/GridFieldPageCount.php b/src/Forms/GridField/GridFieldPageCount.php
index b82d3046511..bebbadcd0a0 100755
--- a/src/Forms/GridField/GridFieldPageCount.php
+++ b/src/Forms/GridField/GridFieldPageCount.php
@@ -12,7 +12,7 @@
  *
  * Depends on {@link GridFieldPaginator} being added to the {@link GridField}.
  */
-class GridFieldPageCount implements GridField_HTMLProvider
+class GridFieldPageCount extends AbstractGridFieldComponent implements GridField_HTMLProvider
 {
     use Configurable;
 
diff --git a/src/Forms/GridField/GridFieldPaginator.php b/src/Forms/GridField/GridFieldPaginator.php
index 8573c0efd7f..d6eb208ef06 100755
--- a/src/Forms/GridField/GridFieldPaginator.php
+++ b/src/Forms/GridField/GridFieldPaginator.php
@@ -14,7 +14,7 @@
  * GridFieldPaginator paginates the {@link GridField} list and adds controls
  * to the bottom of the {@link GridField}.
  */
-class GridFieldPaginator implements GridField_HTMLProvider, GridField_DataManipulator, GridField_ActionProvider, GridField_StateProvider
+class GridFieldPaginator extends AbstractGridFieldComponent implements GridField_HTMLProvider, GridField_DataManipulator, GridField_ActionProvider, GridField_StateProvider
 {
     use Configurable;
 
diff --git a/src/Forms/GridField/GridFieldPrintButton.php b/src/Forms/GridField/GridFieldPrintButton.php
index cbec6c3aec5..1ec2be669f6 100644
--- a/src/Forms/GridField/GridFieldPrintButton.php
+++ b/src/Forms/GridField/GridFieldPrintButton.php
@@ -16,7 +16,7 @@
 /**
  * Adds an "Print" button to the bottom or top of a GridField.
  */
-class GridFieldPrintButton implements GridField_HTMLProvider, GridField_ActionProvider, GridField_URLHandler
+class GridFieldPrintButton extends AbstractGridFieldComponent implements GridField_HTMLProvider, GridField_ActionProvider, GridField_URLHandler
 {
     use Extensible;
 
diff --git a/src/Forms/GridField/GridFieldSortableHeader.php b/src/Forms/GridField/GridFieldSortableHeader.php
index fa97d8239ba..7255cc8b584 100644
--- a/src/Forms/GridField/GridFieldSortableHeader.php
+++ b/src/Forms/GridField/GridFieldSortableHeader.php
@@ -18,7 +18,7 @@
  *
  * @see GridField
  */
-class GridFieldSortableHeader implements GridField_HTMLProvider, GridField_DataManipulator, GridField_ActionProvider, GridField_StateProvider
+class GridFieldSortableHeader extends AbstractGridFieldComponent implements GridField_HTMLProvider, GridField_DataManipulator, GridField_ActionProvider, GridField_StateProvider
 {
 
     /**
diff --git a/src/Forms/GridField/GridFieldToolbarHeader.php b/src/Forms/GridField/GridFieldToolbarHeader.php
index 513c5677e10..8171139e30f 100644
--- a/src/Forms/GridField/GridFieldToolbarHeader.php
+++ b/src/Forms/GridField/GridFieldToolbarHeader.php
@@ -10,7 +10,7 @@
  *
  * The header serves to display the name of the data the GridField is showing.
  */
-class GridFieldToolbarHeader implements GridField_HTMLProvider
+class GridFieldToolbarHeader extends AbstractGridFieldComponent implements GridField_HTMLProvider
 {
 
     /**
diff --git a/src/Forms/GridField/GridFieldViewButton.php b/src/Forms/GridField/GridFieldViewButton.php
index e99b40033d0..9848f7a25cc 100644
--- a/src/Forms/GridField/GridFieldViewButton.php
+++ b/src/Forms/GridField/GridFieldViewButton.php
@@ -11,7 +11,7 @@
  * disabled by default and intended for use in readonly {@link GridField}
  * instances.
  */
-class GridFieldViewButton implements GridField_ColumnProvider, GridField_ActionMenuLink
+class GridFieldViewButton extends AbstractGridFieldComponent implements GridField_ColumnProvider, GridField_ActionMenuLink
 {
     /**
      * @inheritdoc
diff --git a/src/Forms/GridField/GridField_ActionMenu.php b/src/Forms/GridField/GridField_ActionMenu.php
index 23b180e4c82..caf535b573c 100644
--- a/src/Forms/GridField/GridField_ActionMenu.php
+++ b/src/Forms/GridField/GridField_ActionMenu.php
@@ -8,7 +8,7 @@
 /**
  * Groups exiting actions in the Actions column in to a menu
  */
-class GridField_ActionMenu implements GridField_ColumnProvider, GridField_ActionProvider
+class GridField_ActionMenu extends AbstractGridFieldComponent implements GridField_ColumnProvider, GridField_ActionProvider
 {
     public function augmentColumns($gridField, &$columns)
     {
diff --git a/src/Forms/GridField/GridState_Component.php b/src/Forms/GridField/GridState_Component.php
index 2b749345c72..b1f67272f3b 100644
--- a/src/Forms/GridField/GridState_Component.php
+++ b/src/Forms/GridField/GridState_Component.php
@@ -5,7 +5,7 @@
 /**
  * @see GridState
  */
-class GridState_Component implements GridField_HTMLProvider
+class GridState_Component extends AbstractGridFieldComponent implements GridField_HTMLProvider
 {
 
     public function getHTMLFragments($gridField)
diff --git a/src/Security/Group.php b/src/Security/Group.php
index 4fcb6a5eb07..0ba051006fd 100755
--- a/src/Security/Group.php
+++ b/src/Security/Group.php
@@ -84,7 +84,7 @@ class Group extends DataObject
     ];
 
     private static $table_name = "Group";
-    
+
     private static $indexes = [
         'Title' => true,
         'Code' => true,
@@ -159,11 +159,11 @@ public function getCMSFields()
         if ($this->ID) {
             $group = $this;
             $config = GridFieldConfig_RelationEditor::create();
-            $config->addComponent(new GridFieldButtonRow('after'));
-            $config->addComponents(new GridFieldExportButton('buttons-after-left'));
-            $config->addComponents(new GridFieldPrintButton('buttons-after-left'));
+            $config->addComponent(GridFieldButtonRow::create('after'));
+            $config->addComponents(GridFieldExportButton::create('buttons-after-left'));
+            $config->addComponents(GridFieldPrintButton::create('buttons-after-left'));
             $config->removeComponentsByType(GridFieldDeleteAction::class);
-            $config->addComponent(new GridFieldGroupDeleteAction($this->ID), GridFieldPageCount::class);
+            $config->addComponent(GridFieldGroupDeleteAction::create($this->ID), GridFieldPageCount::class);
 
             /** @var GridFieldAddExistingAutocompleter $autocompleter */
             $autocompleter = $config->getComponentByType(GridFieldAddExistingAutocompleter::class);

From 3edc3e0f3487616f3b3178dd953a4cb4c660d194 Mon Sep 17 00:00:00 2001
From: Guy Sartorelli <guy.sartorelli@signify.co.nz>
Date: Mon, 17 Jan 2022 11:48:36 +1300
Subject: [PATCH 2/2] DOCS Encourage injection for GridField and
 GridFieldComponents.

---
 .../03_Forms/Field_types/04_GridField.md      | 66 ++++++++++---------
 .../How_Tos/03_Create_a_GridFieldComponent.md |  6 +-
 .../04_Create_a_GridField_ActionProvider.md   | 12 ++--
 .../01_ModelAdmin.md                          | 10 +--
 .../How_Tos/Customise_CMS_Menu.md             |  2 +-
 5 files changed, 51 insertions(+), 45 deletions(-)

diff --git a/docs/en/02_Developer_Guides/03_Forms/Field_types/04_GridField.md b/docs/en/02_Developer_Guides/03_Forms/Field_types/04_GridField.md
index 6d7681b1c91..6c4d0ada51c 100644
--- a/docs/en/02_Developer_Guides/03_Forms/Field_types/04_GridField.md
+++ b/docs/en/02_Developer_Guides/03_Forms/Field_types/04_GridField.md
@@ -13,7 +13,7 @@ tabular data in a format that is easy to view and modify. It can be thought of a
 ```php
 use SilverStripe\Forms\GridField\GridField;
 
-$field = new GridField($name, $title, $list);
+$field = GridField::create($name, $title, $list);
 ```
 
 [hint]
@@ -45,7 +45,7 @@ class Page extends SiteTree
         $fields = parent::getCMSFields();
 
         $fields->addFieldToTab('Root.Pages', 
-            new GridField('Pages', 'All pages', SiteTree::get())
+            GridField::create('Pages', 'All pages', SiteTree::get())
         ); 
 
         return $fields;
@@ -81,7 +81,7 @@ class Page extends SiteTree
         $fields = parent::getCMSFields();
 
         $fields->addFieldToTab('Root.Pages', 
-            $grid = new GridField('Pages', 'All pages', SiteTree::get())
+            $grid = GridField::create('Pages', 'All pages', SiteTree::get())
         );
 
         // GridField configuration
@@ -115,7 +115,7 @@ use SilverStripe\Forms\GridField\GridFieldDataColumns;
 $config = GridFieldConfig::create();
 
 // add a component
-$config->addComponent(new GridFieldDataColumns());
+$config->addComponent(GridFieldDataColumns::create());
 
 // Update the GridField with our custom configuration
 $gridField->setConfig($config);
@@ -128,7 +128,7 @@ before another component by passing the second parameter.
 use SilverStripe\Forms\GridField\GridFieldFilterHeader;
 use SilverStripe\Forms\GridField\GridFieldDataColumns;
 
-$config->addComponent(new GridFieldFilterHeader(), GridFieldDataColumns::class);
+$config->addComponent(GridFieldFilterHeader::create(), GridFieldDataColumns::class);
 ```
 
 We can add multiple components in one call.
@@ -136,8 +136,8 @@ We can add multiple components in one call.
 
 ```php
 $config->addComponents(
-    new GridFieldDataColumns(), 
-    new GridFieldToolbarHeader()
+    GridFieldDataColumns::create(), 
+    GridFieldToolbarHeader::create()
 );
 ```
 
@@ -191,12 +191,12 @@ $config = GridFieldConfig_Base::create();
 $gridField->setConfig($config);
 
 // Is the same as adding the following components..
-// .. new GridFieldToolbarHeader()
-// .. new GridFieldSortableHeader()
-// .. new GridFieldFilterHeader()
-// .. new GridFieldDataColumns()
-// .. new GridFieldPageCount('toolbar-header-right')
-// .. new GridFieldPaginator($itemsPerPage)
+// .. GridFieldToolbarHeader::create()
+// .. GridFieldSortableHeader::create()
+// .. GridFieldFilterHeader::create()
+// .. GridFieldDataColumns::create()
+// .. GridFieldPageCount::create('toolbar-header-right')
+// .. GridFieldPaginator::create($itemsPerPage)
 ```
 
 ### GridFieldConfig_RecordViewer
@@ -223,8 +223,8 @@ $config = GridFieldConfig_RecordViewer::create();
 $gridField->setConfig($config);
 
 // Same as GridFieldConfig_Base with the addition of
-// .. new GridFieldViewButton(),
-// .. new GridFieldDetailForm()
+// .. GridFieldViewButton::create(),
+// .. GridFieldDetailForm::create()
 ```
 
 ### GridFieldConfig_RecordEditor
@@ -248,9 +248,9 @@ $config = GridFieldConfig_RecordEditor::create();
 $gridField->setConfig($config);
 
 // Same as GridFieldConfig_RecordViewer with the addition of
-// .. new GridFieldAddNewButton(),
-// .. new GridFieldEditButton(),
-// .. new GridFieldDeleteAction()
+// .. GridFieldAddNewButton::create(),
+// .. GridFieldEditButton::create(),
+// .. GridFieldDeleteAction::create()
 ```
 
 ### GridFieldConfig_RelationEditor
@@ -287,9 +287,9 @@ $config = GridFieldConfig::create();
 $config->addComponent();
 
 $config->addComponents(
-    new GridFieldDataColumns(),
-    new GridFieldEditButton(), 
-    new GridField_ActionMenu()
+    GridFieldDataColumns::create(),
+    GridFieldEditButton::create(), 
+    GridField_ActionMenu::create()
 );
 
 // Update the GridField with our custom configuration
@@ -308,8 +308,8 @@ The `GridFieldDetailForm` component drives the record viewing and editing form.
 use SilverStripe\Forms\GridField\GridFieldDetailForm;
 
 $form = $gridField->getConfig()->getComponentByType(GridFieldDetailForm::class);
-$form->setFields(new FieldList(
-    new TextField('Title')
+$form->setFields(FieldList::create(
+    TextField::create('Title')
 ));
 ```
 
@@ -369,13 +369,13 @@ class Player extends DataObject
             $teamFields->addFieldToTab(
                 'Root.Main',
                 // The "ManyMany[<extradata-name>]" convention
-                new TextField('ManyMany[Position]', 'Current Position')
+                TextField::create('ManyMany[Position]', 'Current Position')
             );
 
             $config = GridFieldConfig_RelationEditor::create();
             $config->getComponentByType(GridFieldDetailForm::class)->setFields($teamFields);
 
-            $gridField = new GridField('Teams', 'Teams', $this->Teams(), $config);
+            $gridField = GridField::create('Teams', 'Teams', $this->Teams(), $config);
             $fields->findOrMakeTab('Root.Teams')->replaceField('Teams', $gridField);
         }
 
@@ -405,8 +405,8 @@ bottom right of the table.
 use SilverStripe\Forms\GridField\GridFieldButtonRow;
 use SilverStripe\Forms\GridField\GridFieldPrintButton;
 
-$config->addComponent(new GridFieldButtonRow('after'));
-$config->addComponent(new GridFieldPrintButton('buttons-after-right'));
+$config->addComponent(GridFieldButtonRow::create('after'));
+$config->addComponent(GridFieldPrintButton::create('buttons-after-right'));
 ```
 
 ### Creating your own Fragments
@@ -417,12 +417,13 @@ create an area rendered before the table wrapped in a simple `<div>`.
 
 
 ```php
+use SilverStripe\Forms\GridField\AbstractGridFieldComponent;
 use SilverStripe\Forms\GridField\GridField_HTMLProvider;
 
-class MyAreaComponent implements GridField_HTMLProvider 
+class MyAreaComponent extends AbstractGridFieldComponent implements GridField_HTMLProvider 
 {
 
-    public function getHTMLFragments( $gridField) 
+    public function getHTMLFragments($gridField) 
     {
         return [
             'before' => '<div class="my-area">$DefineFragment(my-area)</div>'
@@ -442,12 +443,13 @@ Now you can add other components into this area by returning them as an array fr
 
 
 ```php
+use SilverStripe\Forms\GridField\AbstractGridFieldComponent;
 use SilverStripe\Forms\GridField\GridField_HTMLProvider;
 
-class MyShareLinkComponent implements GridField_HTMLProvider 
+class MyShareLinkComponent extends AbstractGridFieldComponent implements GridField_HTMLProvider 
 {
 
-    public function getHTMLFragments( $gridField) 
+    public function getHTMLFragments($gridField) 
     {        
         return [
             'my-area' => '<a href>...</a>'
@@ -461,7 +463,7 @@ Your new area can also be used by existing components, e.g. the [GridFieldPrintB
 
 
 ```php
-new GridFieldPrintButton('my-area');
+GridFieldPrintButton::create('my-area');
 ```
 
 ## Creating a Custom GridFieldComponent
diff --git a/docs/en/02_Developer_Guides/03_Forms/How_Tos/03_Create_a_GridFieldComponent.md b/docs/en/02_Developer_Guides/03_Forms/How_Tos/03_Create_a_GridFieldComponent.md
index 0e4637f3593..892ebb504b1 100644
--- a/docs/en/02_Developer_Guides/03_Forms/How_Tos/03_Create_a_GridFieldComponent.md
+++ b/docs/en/02_Developer_Guides/03_Forms/How_Tos/03_Create_a_GridFieldComponent.md
@@ -3,7 +3,9 @@ title: Create a GridField Component
 summary: Customise your GridField with a variety of add-ons.
 icon: table
 ---
-A single component often uses a number of interfaces.
+A single component often uses a number of interfaces. It is good practice for your custom
+components to subclass the `AbstractGridFieldComponent` class to ensure they behave the same
+way as built-in components (e.g. are `Injectable`).
 
 ### GridField_HTMLProvider
 
@@ -50,4 +52,4 @@ has a list of URL's that it can handle and the GridField passes request on to UR
 Examples:
 
  - A pop-up form for editing a record's details.
- - JSON formatted data used for javascript control of the gridfield.
\ No newline at end of file
+ - JSON formatted data used for javascript control of the gridfield.
diff --git a/docs/en/02_Developer_Guides/03_Forms/How_Tos/04_Create_a_GridField_ActionProvider.md b/docs/en/02_Developer_Guides/03_Forms/How_Tos/04_Create_a_GridField_ActionProvider.md
index 096d3444ef1..34f752f7e3a 100644
--- a/docs/en/02_Developer_Guides/03_Forms/How_Tos/04_Create_a_GridField_ActionProvider.md
+++ b/docs/en/02_Developer_Guides/03_Forms/How_Tos/04_Create_a_GridField_ActionProvider.md
@@ -32,13 +32,14 @@ below:
 
 
 ```php
+use SilverStripe\Forms\GridField\AbstractGridFieldComponent;
 use SilverStripe\Forms\GridField\GridField_ColumnProvider;
 use SilverStripe\Forms\GridField\GridField_ActionProvider;
 use SilverStripe\Forms\GridField\GridField_FormAction;
 use SilverStripe\Forms\GridField\GridField;
 use SilverStripe\Control\Controller;
 
-class GridFieldCustomAction implements GridField_ColumnProvider, GridField_ActionProvider 
+class GridFieldCustomAction extends AbstractGridFieldComponent implements GridField_ColumnProvider, GridField_ActionProvider 
 {
 
     public function augmentColumns($gridField, &$columns) 
@@ -114,12 +115,12 @@ manipulating the `GridFieldConfig` instance if required.
 ```php
 // option 1: creating a new GridField with the CustomAction
 $config = GridFieldConfig::create();
-$config->addComponent(new GridFieldCustomAction());
+$config->addComponent(GridFieldCustomAction::create());
 
-$gridField = new GridField('Teams', 'Teams', $this->Teams(), $config);
+$gridField = GridField::create('Teams', 'Teams', $this->Teams(), $config);
 
 // option 2: adding the CustomAction to an existing GridField
-$gridField->getConfig()->addComponent(new GridFieldCustomAction());
+$gridField->getConfig()->addComponent(GridFieldCustomAction::create());
 ```
 
 For documentation on adding a Component to a `GridField` created by `ModelAdmin`
@@ -177,13 +178,14 @@ For an action to be included in the action menu dropdown, which appears on each
 ## Basic GridFieldCustomAction boilerplate implementing GridField_ActionMenuItem
 
 ```php
+use SilverStripe\Forms\GridField\AbstractGridFieldComponent;
 use SilverStripe\Forms\GridField\GridField_ColumnProvider;
 use SilverStripe\Forms\GridField\GridField_ActionProvider;
 use SilverStripe\Forms\GridField\GridField_ActionMenuItem;
 use SilverStripe\Forms\GridField\GridField_FormAction;
 use SilverStripe\Control\Controller;
 
-class GridFieldCustomAction implements GridField_ColumnProvider, GridField_ActionProvider, GridField_ActionMenuItem
+class GridFieldCustomAction extends AbstractGridFieldComponent implements GridField_ColumnProvider, GridField_ActionProvider, GridField_ActionMenuItem
 {
 
     public function getTitle($gridField, $record, $columnName)
diff --git a/docs/en/02_Developer_Guides/15_Customising_the_Admin_Interface/01_ModelAdmin.md b/docs/en/02_Developer_Guides/15_Customising_the_Admin_Interface/01_ModelAdmin.md
index 8e39eae62b1..bf2e1907f6c 100644
--- a/docs/en/02_Developer_Guides/15_Customising_the_Admin_Interface/01_ModelAdmin.md
+++ b/docs/en/02_Developer_Guides/15_Customising_the_Admin_Interface/01_ModelAdmin.md
@@ -299,7 +299,7 @@ class MyAdmin extends ModelAdmin
         $context = parent::getSearchContext();
 
         if($this->modelClass == 'Product') {
-            $context->getFields()->push(new CheckboxField('q[ExpensiveOnly]', 'Only expensive stuff'));
+            $context->getFields()->push(CheckboxField::create('q[ExpensiveOnly]', 'Only expensive stuff'));
         }
 
         return $context;
@@ -362,7 +362,7 @@ class MyAdmin extends ModelAdmin
     {
         $config = parent::getGridFieldConfig();
 
-        $config->addComponent(new GridFieldFilterHeader());
+        $config->addComponent(GridFieldFilterHeader::create());
 
         return $config;
     }
@@ -397,7 +397,7 @@ class MyAdmin extends ModelAdmin
 
         // modify the list view.
         if ($this->modelClass === Product::class) {
-            $config->addComponent(new GridFieldFilterHeader());
+            $config->addComponent(GridFieldFilterHeader::create());
         }
 
         return $config;
@@ -425,7 +425,7 @@ class ModelAdminExtension extends Extension
 {
     public function updateGridFieldConfig(GridFieldConfig &$config)
     {
-        $config->addComponent(new GridFieldFilterHeader());
+        $config->addComponent(GridFieldFilterHeader::create());
     }
 }
 ```
@@ -470,7 +470,7 @@ class MyAdmin extends ModelAdmin
         $gridField = $form->Fields()->fieldByName($gridFieldName);
 
         // modify the list view.
-        $gridField->getConfig()->addComponent(new GridFieldFilterHeader());
+        $gridField->getConfig()->addComponent(GridFieldFilterHeader::create());
 
         return $form;
     }
diff --git a/docs/en/02_Developer_Guides/15_Customising_the_Admin_Interface/How_Tos/Customise_CMS_Menu.md b/docs/en/02_Developer_Guides/15_Customising_the_Admin_Interface/How_Tos/Customise_CMS_Menu.md
index 921c89a5c4d..51077720126 100644
--- a/docs/en/02_Developer_Guides/15_Customising_the_Admin_Interface/How_Tos/Customise_CMS_Menu.md
+++ b/docs/en/02_Developer_Guides/15_Customising_the_Admin_Interface/How_Tos/Customise_CMS_Menu.md
@@ -142,7 +142,7 @@ SilverStripe\Forms\GridField\GridFieldDetailForm_ItemRequest:
 You can also override this for a specific `GridField` instance when using the `GridFieldConfig_RecordEditor` constructor:
 
 ```php
-$grid = new GridField(
+$grid = GridField::create(
     "pages", 
     "All Pages", 
     SiteTree::get(),