-
Notifications
You must be signed in to change notification settings - Fork 15
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
NEW Add MultiLinkField #120
NEW Add MultiLinkField #120
Conversation
Root.unmount(); | ||
if (Root) { | ||
Root.unmount(); | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I was sometimes getting console errors about unmount not being a function on undefined.
See https://docs.silverstripe.org/en/5/changelogs/5.0.0/#reactdom-render-replaced-with-reactdom-createroot-render where this pattern of checking for the root before unmounting is documented as best practice anyway.
14c1d4e
to
f9925f3
Compare
babel.config.json
Outdated
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
These presets are already defined in the global webpack config.
See https://github.com/silverstripe/webpack-config/blob/5832637ef465be20afd50407cdae0185880166b5/js/modules.js#L15-L25
// Allows null coalescing and optional chaining operators. | ||
parserOptions: { | ||
ecmaVersion: 2020 | ||
}, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Null coallescing (x ?? y
) and optional chaining (x?.y
) are well supported now - the build would have failed if they weren't supported by the browsers we target, due to the browser config in package.json
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This component now handles single and multi link fields.
I originally had them as two separate components, but the differences were so minimal it just made more sense to have a isMulti
boolean prop and let the one component handle both scenarios.
Note that I've moved the LinkPickerTitle
out of the picker, so that it can be used to render the links when linkfield is multi.
The picker is now responsible for knowing what type was picked, and passing it through to the modal. The linkfield shouldn't care about that, it only cares about the link once the link has been created.
I've also moved the responsibility of rendering the modal. If it's a new link, the modal is rendered by the picker (indirectly, through the new LinkModalContainer
). The linkfield itself only renders modals for links that are being edited.
Finally, the logic for figuring out which link modal component to render was moved into LinkModalContainer
so it didn't get duplicated between the linkfield and the picker components.
/** | ||
* Call back used by LinkModal after the form has been submitted and the response has been received | ||
*/ | ||
const onSubmit = async (modalData, action, submitFn) => { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Moved a bunch of this logic from linkfield into here - linkfield doesn't care that the modal was submitted, it only cares about success. Similarly, the modal should be responsible for knowing about validation errors - linkfield itself doesn't care about them.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The only change to LinkPickerTitle
was to give it the classnames necessary for it to look correct when rendered outside of the picker component.
"@silverstripe/eslint-config": "^1.0.0-alpha6", | ||
"@silverstripe/webpack-config": "^2.0.0-alpha9", | ||
"@silverstripe/eslint-config": "^1.0.0", | ||
"@silverstripe/webpack-config": "^2.0.0", |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Shouldn't be using alpha anymore.
f9925f3
to
8852c98
Compare
value: PropTypes.number.isRequired, | ||
value: PropTypes.oneOfType([PropTypes.arrayOf(PropTypes.number), PropTypes.number]), |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Can't be required, because the value is undefined
when no links are in the field and the field is rendered in an elemental block.
The component handles undefined
perfectly so that's not a problem.
8852c98
to
e95916b
Compare
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I've reduced all of the &__
down to their full .link-picker__
forms so that we can easily do a text search for any given CSS class and find the style here.
<DropdownToggle className="link-picker__menu-toggle font-icon-link" caret>{i18n._t('LinkField.ADD_LINK', 'Add Link')}</DropdownToggle> | ||
<DropdownToggle className="link-picker__menu-toggle font-icon-plus-1" caret>{i18n._t('LinkField.ADD_LINK', 'Add Link')}</DropdownToggle> |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Now that we are rendering links below the picker, it makes way more sense to have a +
symbol for the picker, and a link symbol for the links. It's a super quick visual distinction.
### Silverstripe 5 | ||
|
||
```sh | ||
composer require silverstripe/linkfield | ||
``` | ||
|
||
### GraphQL v4 - Silverstripe 4 | ||
|
||
`composer require silverstripe/linkfield:^2` | ||
|
||
### GraphQL v3 - Silverstripe 4 | ||
|
||
```sh | ||
composer require silverstripe/linkfield:^1 | ||
``` |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This branch is only valid for CMS 5
## Migrating from Version `1.0.0` or `dev-master` | ||
|
||
Please be aware that in early versions of this module (and in untagged `dev-master`) there were no table names defined | ||
for our `Link` classes. These have now all been defined, which may mean that you need to rename your old tables, or | ||
migrate the data across. | ||
|
||
EG: `SilverStripe_LinkField_Models_Link` needs to be migrated to `LinkField_Link`. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
These are old and don't apply to the jump from 2.x/3.x to 4.x
const [data, setData] = useState({}); | ||
const [editing, setEditing] = useState(false); | ||
const [editingID, setEditingID] = useState(null); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
const [editingID, setEditingID] = useState(null); | |
const [editingID, setEditingID] = useState(-1); |
Are we using null because 0 is reserved for editing new unsaved Links?
-1
is better than null
because it's easy to make errors by testing the truthiness of editingID when 0 is a valid value. Instead explicitly test for !== -1
similar to indexOf()
.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We're using null because it's unset, and we can easily just do a truthy check against it. If there's an ID, it'll be truthy. I'll change to 0
if you want but -1
is nonsensical and isn't what we use anywhere to denote "not an ID".
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
OK 0
makes sense
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Done
bef7985
to
0706a98
Compare
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The regular singlelink field wasn't working when I tested with the latest changes, though the multilink field was working
const [data, setData] = useState({}); | ||
const [editing, setEditing] = useState(false); | ||
const [editingID, setEditingID] = useState(null); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
OK 0
makes sense
for (const linkID of linkIDs) { | ||
query.push(`itemID[]=${linkID}`); | ||
} | ||
const endpoint = `${Config.getSection(section).form.linkForm.dataUrl}/?${query.join('&')}`; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
const endpoint = `${Config.getSection(section).form.linkForm.dataUrl}/?${query.join('&')}`; | |
const endpoint = `${Config.getSection(section).form.linkForm.dataUrl}?${query.join('&')}`; |
Don't need the trailing slash since there's no params being used on the endpoint
Changes
/admin/linkfield/data/?itemID[]=6&itemID[]=10&itemID[]=11&itemID[]=12
to
/admin/linkfield/data?itemID[]=6&itemID[]=10&itemID[]=11&itemID[]=12
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Removed to avoid ping pong, though it doesn't affect anything one way or another.
if (!editingID && linkIDs.length > 0) { | ||
const query = []; | ||
for (const linkID of linkIDs) { | ||
query.push(`itemID[]=${linkID}`); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
query.push(`itemID[]=${linkID}`); | |
query.push(`itemIDs[]=${linkID}`); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Because we don't have behat tests yet small unnecessary changes like this are more likely to break things than to be useful. I don't want to keep making these small changes that don't actually affect anything - it means I have to test every bit of functionality every time it goes through a review.
Done.
private function itemIDsFromRequest(): array | ||
{ | ||
$request = $this->getRequest(); | ||
$itemIDs = $request->getVar('itemID'); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
$itemIDs = $request->getVar('itemID'); | |
$itemIDs = $request->getVar('itemIDs'); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Done
0706a98
to
cc21f8a
Compare
It's working for me locally with this set of changes - hopefully something I did fixed whatever was broken, but if there's still something about it that isn't working please describe the actual faulty behaviour so I know what specifically needs fixing. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Tested locally, worked good
Notes
Issue