-
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
ENH Better versioned history #209
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Large diffs are not rendered by default.
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -45,10 +45,11 @@ const section = 'SilverStripe\\LinkField\\Controllers\\LinkFieldController'; | |
* ownerID - ID of the owner DataObject | ||
* ownerClass - class name of the owner DataObject | ||
* ownerRelation - name of the relation on the owner DataObject | ||
* inHistoryViewer - if the field is being viewed in the context of the history viewer | ||
*/ | ||
const LinkField = ({ | ||
value = null, | ||
onChange, | ||
onChange = () => {}, | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This is a default value that's being set now that it's no longer a required prop |
||
types = {}, | ||
actions, | ||
isMulti = false, | ||
|
@@ -59,6 +60,7 @@ const LinkField = ({ | |
ownerClass, | ||
ownerRelation, | ||
excludeLinkTextField = false, | ||
inHistoryViewer, | ||
}) => { | ||
const [data, setData] = useState({}); | ||
const [editingID, setEditingID] = useState(0); | ||
|
@@ -67,6 +69,7 @@ const LinkField = ({ | |
const [focusOnNewLinkWhenClosed, setFocusOnNewLinkWhenClosed] = useState(false); | ||
const [focusOnNewLink, setFocusOnNewLink] = useState(false); | ||
const [loading, setLoading] = useState(false); | ||
const [loadingError, setLoadingError] = useState(false); | ||
const [forceFetch, setForceFetch] = useState(0); | ||
const [isSorting, setIsSorting] = useState(false); | ||
const [linksClassName, setLinksClassName] = useState(classnames({'link-picker-links': true})); | ||
|
@@ -156,16 +159,16 @@ const LinkField = ({ | |
.then(response => response.json()) | ||
.then(responseJson => { | ||
setData(responseJson); | ||
}) | ||
.catch(() => { | ||
setLoadingError(true); | ||
}) | ||
.finally(() => { | ||
setLoading(false); | ||
// isSorting is set to true on drag start and only set to false here to prevent | ||
// the loading indicator for flickering | ||
setIsSorting(false); | ||
}) | ||
.catch(() => { | ||
actions.toasts.error(i18n._t('LinkField.FAILED_TO_LOAD_LINKS', 'Failed to load links')) | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Need to not show a red toast here because this will pop up when viewing old versions of an owner where the link has been deleted. I've changed this to show an inline message when the link fails to load. |
||
setLoading(false); | ||
setIsSorting(false); | ||
}); | ||
} | ||
}, [editingID, value && value.length, forceFetch]); | ||
|
||
|
@@ -441,14 +444,25 @@ const LinkField = ({ | |
}); | ||
} | ||
|
||
const saveRecordFirst = ownerID === 0; | ||
const renderPicker = !saveRecordFirst && (isMulti || Object.keys(data).length === 0); | ||
const renderModal = !saveRecordFirst && Boolean(editingID); | ||
const saveRecordFirst = !loadingError && ownerID === 0; | ||
const renderLoadingError = loadingError; | ||
const renderPicker = !loadingError && !inHistoryViewer && !saveRecordFirst && (isMulti || Object.keys(data).length === 0); | ||
const renderModal = !loadingError && !saveRecordFirst && Boolean(editingID); | ||
const loadingErrorText = i18n._t('LinkField.FAILED_TO_LOAD_LINKS', 'Failed to load link(s)'); | ||
const saveRecordFirstText = i18n._t('LinkField.SAVE_RECORD_FIRST', 'Cannot add links until the record has been saved'); | ||
const links = renderLinks(); | ||
|
||
return <LinkFieldContext.Provider value={{ ownerID, ownerClass, ownerRelation, actions, loading, excludeLinkTextField }}> | ||
return <LinkFieldContext.Provider value={{ | ||
ownerID, | ||
ownerClass, | ||
ownerRelation, | ||
actions, | ||
loading, | ||
excludeLinkTextField, | ||
inHistoryViewer | ||
}}> | ||
<div className="link-field__container"> | ||
{ renderLoadingError && <div className="link-field__loading-error">{loadingErrorText}</div> } | ||
{ saveRecordFirst && <div className="link-field__save-record-first">{saveRecordFirstText}</div>} | ||
{ loading && !isSorting && !saveRecordFirst && <Loading containerClass="link-field__loading"/> } | ||
{ renderPicker && <LinkPicker | ||
|
@@ -478,7 +492,7 @@ const LinkField = ({ | |
|
||
LinkField.propTypes = { | ||
value: PropTypes.oneOfType([PropTypes.arrayOf(PropTypes.number), PropTypes.number]), | ||
onChange: PropTypes.func.isRequired, | ||
onChange: PropTypes.func, | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This should never have been a required field as it only makes sense in an entwine context |
||
types: PropTypes.object.isRequired, | ||
actions: PropTypes.object.isRequired, | ||
isMulti: PropTypes.bool, | ||
|
@@ -489,6 +503,7 @@ LinkField.propTypes = { | |
ownerClass: PropTypes.string.isRequired, | ||
ownerRelation: PropTypes.string.isRequired, | ||
excludeLinkTextField: PropTypes.bool, | ||
inHistoryViewer: PropTypes.bool, | ||
}; | ||
|
||
// redux actions loaded into props - used to get toast notifications | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,6 +1,6 @@ | ||
/* global jest, test, expect, document */ | ||
import React from 'react'; | ||
import { render, screen, waitFor } from '@testing-library/react'; | ||
import { render, screen } from '@testing-library/react'; | ||
import userEvent from '@testing-library/user-event'; | ||
import '@testing-library/jest-dom'; | ||
import { Component as LinkField } from '../LinkField'; | ||
|
@@ -50,6 +50,7 @@ function makeProps(obj = {}) { | |
canCreate: true, | ||
readonly: false, | ||
disabled: false, | ||
inHistoryViewer: false, | ||
ownerID: 123, | ||
ownerClass: 'Page', | ||
ownerRelation: 'MyRelation', | ||
|
@@ -87,7 +88,7 @@ test('LinkField returns list of links if they exist', async () => { | |
test('LinkField will render disabled state if disabled is true', async () => { | ||
const { container } = render(<LinkField {...makeProps({ | ||
ownerID: 1, | ||
disabled: true | ||
disabled: true, | ||
})} | ||
/>); | ||
doResolve(); | ||
|
@@ -99,10 +100,10 @@ test('LinkField will render disabled state if disabled is true', async () => { | |
test('LinkField will render readonly state if readonly is true', async () => { | ||
const { container } = render(<LinkField {...makeProps({ | ||
ownerID: 1, | ||
readonly: true | ||
readonly: true, | ||
value: null, | ||
})} | ||
/>); | ||
doResolve(); | ||
await screen.findByText('Cannot create link'); | ||
expect(container.querySelectorAll('.link-picker')).toHaveLength(1); | ||
expect(container.querySelectorAll('.link-picker')[0]).toHaveTextContent('Cannot create link'); | ||
|
@@ -180,15 +181,22 @@ test('LinkField will render loading indicator if ownerID is not 0', async () => | |
expect(container.querySelectorAll('.link-picker')).toHaveLength(1); | ||
}); | ||
|
||
test('LinkField will render link-picker if ownerID is not 0 and has finished loading', async () => { | ||
test('LinkField will render link-picker if ownerID is not 0 and isMulti and has finished loading', async () => { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This jest test was technically wrong before - it shouldn't render a |
||
const { container } = render(<LinkField {...makeProps({ | ||
ownerID: 1 | ||
ownerID: 1, | ||
isMulti: true, | ||
})} | ||
/>); | ||
doResolve(); | ||
await waitFor(() => { | ||
expect(container.querySelectorAll('.link-field__save-record-first')).toHaveLength(0); | ||
expect(container.querySelectorAll('.link-field__loading')).toHaveLength(0); | ||
expect(container.querySelectorAll('.link-picker')).toHaveLength(1); | ||
}, { timeout: 100 }); | ||
await doResolve({ json: () => ({ | ||
123: { | ||
Title: 'First title', | ||
Sort: 1, | ||
typeKey: 'mylink', | ||
}, | ||
}) }); | ||
await screen.findByText('First title'); | ||
|
||
expect(container.querySelectorAll('.link-field__save-record-first')).toHaveLength(0); | ||
expect(container.querySelectorAll('.link-field__loading')).toHaveLength(0); | ||
expect(container.querySelectorAll('.link-picker')).toHaveLength(1); | ||
}); |
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -23,6 +23,7 @@ const LinkModalContainer = ({ types, typeKey, linkID = 0, isOpen, onSuccess, onC | |
// which will causes bugs with validation | ||
if (!LinkModal) { | ||
setLinkModal(() => loadComponent(`LinkModal.${handlerName}`)); | ||
return; | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This should is to fix a console error where the state hasn't yet been updated from the call the setLinkModal, meaning that the |
||
} | ||
|
||
return <LinkModal | ||
|
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 is a minor improvement to the message as it's used for both LinkField and for MultiLinkField