Skip to content

Commit

Permalink
ENH Add disabled field state
Browse files Browse the repository at this point in the history
  • Loading branch information
emteknetnz committed Jan 26, 2024
1 parent 7e077dc commit 4ba8267
Show file tree
Hide file tree
Showing 13 changed files with 76 additions and 24 deletions.
2 changes: 1 addition & 1 deletion client/dist/js/bundle.js

Large diffs are not rendered by default.

2 changes: 1 addition & 1 deletion client/dist/styles/bundle.css

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

7 changes: 6 additions & 1 deletion client/src/components/LinkField/LinkField.js
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@ const section = 'SilverStripe\\LinkField\\Controllers\\LinkFieldController';
* isMulti - whether this field handles multiple links or not
* canCreate - whether this field can create new links or not
* readonly - whether this field is readonly or not
* disabled - whether this field is disabled or not
* ownerID - ID of the owner DataObject
* ownerClass - class name of the owner DataObject
* ownerRelation - name of the relation on the owner DataObject
Expand All @@ -46,6 +47,7 @@ const LinkField = ({
isMulti = false,
canCreate,
readonly,
disabled,
ownerID,
ownerClass,
ownerRelation,
Expand Down Expand Up @@ -201,13 +203,14 @@ const LinkField = ({
isSorting={isSorting}
canCreate={canCreate}
readonly={readonly}
disabled={disabled}
/>);
}
return links;
};

const sortableLinks = () => {
if (isMulti && !readonly) {
if (isMulti && !readonly && !disabled) {
return <div className={linksClassName}>
<DndContext modifiers={[restrictToVerticalAxis, restrictToParentElement]}
sensors={sensors}
Expand Down Expand Up @@ -286,6 +289,7 @@ const LinkField = ({
types={types}
canCreate={canCreate}
readonly={readonly}
disabled={disabled}
/> }
{sortableLinks()}
{ renderModal && <LinkModalContainer
Expand All @@ -309,6 +313,7 @@ LinkField.propTypes = {
isMulti: PropTypes.bool,
canCreate: PropTypes.bool.isRequired,
readonly: PropTypes.bool.isRequired,
disabled: PropTypes.bool.isRequired,
ownerID: PropTypes.number.isRequired,
ownerClass: PropTypes.string.isRequired,
ownerRelation: PropTypes.string.isRequired,
Expand Down
1 change: 1 addition & 0 deletions client/src/components/LinkField/tests/LinkField-test.js
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,7 @@ function makeProps(obj = {}) {
isMulti: false,
canCreate: true,
readonly: false,
disabled: false,
ownerID: 123,
ownerClass: 'Page',
ownerRelation: 'MyRelation',
Expand Down
5 changes: 3 additions & 2 deletions client/src/components/LinkPicker/LinkPicker.js
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ import LinkModalContainer from 'containers/LinkModalContainer';
/**
* Component which allows users to choose a type of link to create, and opens a modal form for it.
*/
const LinkPicker = ({ types, onModalSuccess, onModalClosed, canCreate, readonly }) => {
const LinkPicker = ({ types, onModalSuccess, onModalClosed, canCreate, readonly, disabled }) => {
const [typeKey, setTypeKey] = useState('');

/**
Expand Down Expand Up @@ -43,7 +43,7 @@ const LinkPicker = ({ types, onModalSuccess, onModalClosed, canCreate, readonly
const allowedTypes = typeArray.filter(type => type.allowed);
const message = i18n._t('LinkField.CANNOT_CREATE_LINK', 'Cannot create link');

if (!canCreate || allowedTypes.length === 0 || readonly) {
if (!canCreate || allowedTypes.length === 0 || readonly || disabled) {
return (
<div className={className}>
<div className="link-picker__cannot-create">
Expand Down Expand Up @@ -74,6 +74,7 @@ LinkPicker.propTypes = {
onModalClosed: PropTypes.func,
canCreate: PropTypes.bool.isRequired,
readonly: PropTypes.bool.isRequired,
disabled: PropTypes.bool.isRequired,
};

export {LinkPicker as Component};
Expand Down
3 changes: 2 additions & 1 deletion client/src/components/LinkPicker/LinkPicker.scss
Original file line number Diff line number Diff line change
Expand Up @@ -99,7 +99,8 @@
display: none;
}

&--readonly {
&--readonly,
&--disabled {
background-color: $gray-100;
}
}
Expand Down
7 changes: 5 additions & 2 deletions client/src/components/LinkPicker/LinkPickerTitle.js
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,7 @@ const LinkPickerTitle = ({
isSorting,
canCreate,
readonly,
disabled,
}) => {
const { loading } = useContext(LinkFieldContext);
const {
Expand All @@ -68,6 +69,7 @@ const LinkPickerTitle = ({
'link-picker__link--is-sorting': isSorting,
'form-control': true,
'link-picker__link--readonly': readonly || !canCreate,
'link-picker__link--disabled': disabled,
};
if (versionState) {
classes[`link-picker__link--${versionState}`] = true;
Expand All @@ -83,7 +85,7 @@ const LinkPickerTitle = ({
{...attributes}
{...listeners}
>
{ (isMulti && !readonly) && <div className="link-picker__drag-handle"><i className="font-icon-drag-handle"></i></div> }
{ (isMulti && !readonly && !disabled) && <div className="link-picker__drag-handle"><i className="font-icon-drag-handle"></i></div> }
<Button disabled={loading} className={`link-picker__button ${typeIcon}`} color="secondary" onClick={stopPropagation(onClick)}>
<div className="link-picker__link-detail">
<div className="link-picker__title">
Expand All @@ -96,7 +98,7 @@ const LinkPickerTitle = ({
</small>
</div>
</Button>
{(canDelete && !readonly) &&
{(canDelete && !readonly && !disabled) &&
<Button disabled={loading} className="link-picker__delete" color="link" onClick={stopPropagation(() => onDelete(id))}>{deleteText}</Button>
}
</div>
Expand All @@ -118,6 +120,7 @@ LinkPickerTitle.propTypes = {
isSorting: PropTypes.bool.isRequired,
canCreate: PropTypes.bool.isRequired,
readonly: PropTypes.bool.isRequired,
disabled: PropTypes.bool.isRequired,
};

export default LinkPickerTitle;
1 change: 1 addition & 0 deletions client/src/components/LinkPicker/tests/LinkPicker-test.js
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ function makeProps(obj = {}) {
types: { phone: { key: 'phone', title: 'Phone', icon: 'font-icon-phone', allowed: true } },
canCreate: true,
readonly: false,
disabled: false,
onModalSuccess: () => {},
onModalClosed: () => {},
...obj
Expand Down
15 changes: 15 additions & 0 deletions client/src/components/LinkPicker/tests/LinkPickerTitle-test.js
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ function makeProps(obj = {}) {
canDelete: true,
canCreate: true,
readonly: false,
disabled: false,
onDelete: () => {},
onClick: () => {},
isMulti: false,
Expand Down Expand Up @@ -105,3 +106,17 @@ test('LinkPickerTitle render() should not have readonly class if set to readonly
</LinkFieldContext.Provider>);
expect(container.querySelectorAll('.link-picker__link--readonly')).toHaveLength(0);
});

test('LinkPickerTitle render() should have disabled class if set to disabled', () => {
const { container } = render(<LinkFieldContext.Provider value={{ loading: false }}>
<LinkPickerTitle {...makeProps({ disabled: true })} />
</LinkFieldContext.Provider>);
expect(container.querySelectorAll('.link-picker__link--disabled')).toHaveLength(1);
});

test('LinkPickerTitle render() should not have disabled class if set to disabled', () => {
const { container } = render(<LinkFieldContext.Provider value={{ loading: false }}>
<LinkPickerTitle {...makeProps({ disabled: false })} />
</LinkFieldContext.Provider>);
expect(container.querySelectorAll('.link-picker__link--disabled')).toHaveLength(0);
});
1 change: 1 addition & 0 deletions client/src/entwine/LinkField.js
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,7 @@ jQuery.entwine('ss', ($) => {
types: this.data('types') ?? {},
canCreate: inputField.data('can-create') ? true : false,
readonly: inputField.data('readonly') ? true : false,
disabled: inputField.data('disabled') ? true : false,
};
},

Expand Down
38 changes: 22 additions & 16 deletions src/Controllers/LinkFieldController.php
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,8 @@
use SilverStripe\LinkField\Services\LinkTypeService;
use SilverStripe\ORM\DataList;
use SilverStripe\ORM\DataObject;
use SilverStripe\Security\Security;
use SilverStripe\LinkField\Form\LinkField;
use SilverStripe\LinkField\Form\MultiLinkField;

class LinkFieldController extends LeftAndMain
{
Expand Down Expand Up @@ -142,8 +143,8 @@ public function linkDelete(): HTTPResponse
// delete() will also delete any published version immediately
$link->delete();
// Update owner object if this Link is on a has_one relation on the owner
$owner = $this->ownerFromRequest();
$ownerRelation = $this->ownerRelationFromRequest();
$owner = $this->getOwnerFromRequest();
$ownerRelation = $this->getOwnerRelationFromRequest();
$hasOne = Injector::inst()->get($owner->ClassName)->hasOne();
if (array_key_exists($ownerRelation, $hasOne) && $owner->canEdit()) {
$owner->$ownerRelation = null;
Expand Down Expand Up @@ -237,10 +238,10 @@ public function save(array $data, Form $form): HTTPResponse

// Update owner object if this Link is on a has_one relation on the owner
// Only do this for has_one, not has_many, because that's stored directly on the Link record
// Get owner using ownerFromRequest() rather than $link->Owner() so that validation is run
// Get owner using getOwnerFromRequest() rather than $link->Owner() so that validation is run
// on the owner params before updating the database
$owner = $this->ownerFromRequest();
$ownerRelation = $this->ownerRelationFromRequest();
$owner = $this->getOwnerFromRequest();
$ownerRelation = $this->getOwnerRelationFromRequest();
$ownerRelationID = "{$ownerRelation}ID";
$hasOne = Injector::inst()->get($owner->ClassName)->hasOne();
if ($operation === 'create'
Expand Down Expand Up @@ -338,10 +339,10 @@ private function createLinkForm(Link $link, string $operation): Form
$name = sprintf(self::FORM_NAME_TEMPLATE, $id);
/** @var Form $form */
$form = $formFactory->getForm($this, $name, ['Record' => $link]);
$owner = $this->ownerFromRequest();
$owner = $this->getOwnerFromRequest();
$ownerID = $owner->ID;
$ownerClassName = $owner->ClassName;
$ownerRelation = $this->ownerRelationFromRequest();
$ownerRelation = $this->getOwnerRelationFromRequest();

// Add hidden form fields for OwnerID, OwnerClass and OwnerRelation
if ($operation === 'create') {
Expand Down Expand Up @@ -379,7 +380,7 @@ private function createLinkForm(Link $link, string $operation): Form
// Make readonly if fail can check
if ($operation === 'create' && !$link->canCreate()
|| $operation === 'edit' && !$link->canEdit()
|| $this->isReadOnlyField()) {
|| $this->getFieldIsReadonlyOrDisabled()) {
$form->makeReadonly();
}

Expand All @@ -390,14 +391,19 @@ private function createLinkForm(Link $link, string $operation): Form
}

/**
* Get is the owner LinkField is readonly
* Get if the relevant LinkField is readonly or disabled
*/
private function isReadOnlyField(): bool
private function getFieldIsReadonlyOrDisabled(): bool
{
$ownerClass = $this->getOwnerClassFromRequest();
$ownerRelation = $this->ownerRelationFromRequest();
$ownerRelation = $this->getOwnerRelationFromRequest();

return (bool) Injector::inst()->get($ownerClass)->getCMSFields()->dataFieldByName($ownerRelation)?->isReadonly();
/** @var LinkField|MultiLinkField $field */
$field = Injector::inst()->get($ownerClass)->getCMSFields()->dataFieldByName($ownerRelation);
if (!$field) {
return false;
}
return $field->isReadonly() || $field->isDisabled();
}

/**
Expand Down Expand Up @@ -513,11 +519,11 @@ private function getOwnerIDFromRequest(): int
* Get the owner based on the query string params ownerID, ownerClass, ownerRelation
* OR the POST vars OwnerID, OwnerClass, OwnerRelation
*/
private function ownerFromRequest(): DataObject
private function getOwnerFromRequest(): DataObject
{
$ownerID = $this->getOwnerIDFromRequest();
$ownerClass = $this->getOwnerClassFromRequest();
$ownerRelation = $this->ownerRelationFromRequest();
$ownerRelation = $this->getOwnerRelationFromRequest();
/** @var DataObject $obj */
$obj = Injector::inst()->get($ownerClass);
$hasOne = $obj->hasOne();
Expand Down Expand Up @@ -547,7 +553,7 @@ private function ownerFromRequest(): DataObject
* Get the owner relation based on the query string param ownerRelation
* OR the POST var OwnerRelation
*/
private function ownerRelationFromRequest(): string
private function getOwnerRelationFromRequest(): string
{
$request = $this->getRequest();
$ownerRelation = $request->getVar('ownerRelation') ?: $request->postVar('OwnerRelation');
Expand Down
9 changes: 9 additions & 0 deletions src/Form/LinkField.php
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@ public function getSchemaStateDefaults()
$data = parent::getSchemaStateDefaults();
$data['canCreate'] = $this->getOwner()->canEdit();
$data['readonly'] = $this->isReadonly();
$data['disabled'] = $this->isDisabled();
return $data;
}

Expand All @@ -44,6 +45,7 @@ protected function getDefaultAttributes(): array
$attributes['data-value'] = $this->Value();
$attributes['data-can-create'] = $this->getOwner()->canEdit();
$attributes['data-readonly'] = $this->isReadonly();
$attributes['data-disabled'] = $this->isDisabled();
$ownerFields = $this->getOwnerFields();
$attributes['data-owner-id'] = $ownerFields['ID'];
$attributes['data-owner-class'] = $ownerFields['Class'];
Expand Down Expand Up @@ -72,4 +74,11 @@ public function performReadonlyTransformation()

return $clone;
}

public function performDisabledTransformation()
{
$clone = clone $this;
$clone->setDisabled(true);
return $clone;
}
}
9 changes: 9 additions & 0 deletions src/Form/MultiLinkField.php
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,7 @@ public function getSchemaStateDefaults()
$data['value'] = $this->getValueArray();
$data['canCreate'] = $this->getOwner()->canEdit();
$data['readonly'] = $this->isReadonly();
$data['disabled'] = $this->isDisabled();
return $data;
}

Expand All @@ -64,6 +65,7 @@ protected function getDefaultAttributes(): array
$attributes['data-value'] = $this->getValueArray();
$attributes['data-can-create'] = $this->getOwner()->canEdit();
$attributes['data-readonly'] = $this->isReadonly();
$attributes['data-disabled'] = $this->isDisabled();
$ownerFields = $this->getOwnerFields();
$attributes['data-owner-id'] = $ownerFields['ID'];
$attributes['data-owner-class'] = $ownerFields['Class'];
Expand Down Expand Up @@ -168,4 +170,11 @@ public function performReadonlyTransformation()

return $clone;
}

public function performDisabledTransformation()
{
$clone = clone $this;
$clone->setDisabled(true);
return $clone;
}
}

0 comments on commit 4ba8267

Please sign in to comment.