feat(ux): enhance media-type switching experience in RequestBodyEditor (#6837)

* feat(ux): enhance media-type switching experience in RequestBodyEditor

1. When canceling the try-out mode the request body will be reset to its initial state.
2. When the user switches the media-type in the try-out mode, the experience is as follows:
   - If the user did edit the request body the body wont be touched and only media type is updated. This is to ensure that user content is NEVER accidentally overwritten with a default value.
   - If the user did not edit the request body it is safe to be replaced by the default value of the target media-type.

Multiple example needed some care in order to allow the retain example value to function properly

* fix(test): workaround cypress issue that can't be reproduced manually

* test: added new feature to ensure enhanced user editing flow

Signed-off-by: mathis-m <mathis.michel@outlook.de>
This commit is contained in:
Mahtis Michel
2021-01-25 20:16:07 +01:00
committed by GitHub
parent a5eb3dc0c3
commit e877580d54
12 changed files with 235 additions and 32 deletions

View File

@@ -36,16 +36,22 @@ export default class ExamplesSelectValueRetainer extends React.PureComponent {
examples: ImPropTypes.map,
onSelect: PropTypes.func,
updateValue: PropTypes.func, // mechanism to update upstream value
userHasEditedBody: PropTypes.bool,
getComponent: PropTypes.func.isRequired,
currentUserInputValue: PropTypes.any,
currentKey: PropTypes.string,
currentNamespace: PropTypes.string,
setRetainRequestBodyValueFlag: PropTypes.func.isRequired,
// (also proxies props for Examples)
}
static defaultProps = {
userHasEditedBody: false,
examples: Map({}),
currentNamespace: "__DEFAULT__NAMESPACE__",
setRetainRequestBodyValueFlag: () => {
// NOOP
},
onSelect: (...args) =>
console.log( // eslint-disable-line no-console
"ExamplesSelectValueRetainer: no `onSelect` function was provided",
@@ -72,11 +78,16 @@ export default class ExamplesSelectValueRetainer extends React.PureComponent {
lastDownstreamValue: valueFromExample,
isModifiedValueSelected:
// valueFromExample !== undefined &&
this.props.userHasEditedBody ||
this.props.currentUserInputValue !== valueFromExample,
}),
}
}
componentWillUnmount() {
this.props.setRetainRequestBodyValueFlag(false)
}
_getStateForCurrentNamespace = () => {
const { currentNamespace } = this.props
@@ -122,7 +133,12 @@ export default class ExamplesSelectValueRetainer extends React.PureComponent {
}
_onExamplesSelect = (key, { isSyntheticChange } = {}, ...otherArgs) => {
const { onSelect, updateValue, currentUserInputValue } = this.props
const {
onSelect,
updateValue,
currentUserInputValue,
userHasEditedBody,
} = this.props
const { lastUserEditedValue } = this._getStateForCurrentNamespace()
const valueFromExample = this._getValueForExample(key)
@@ -141,9 +157,8 @@ export default class ExamplesSelectValueRetainer extends React.PureComponent {
this._setStateForCurrentNamespace({
lastDownstreamValue: valueFromExample,
isModifiedValueSelected:
isSyntheticChange &&
!!currentUserInputValue &&
currentUserInputValue !== valueFromExample,
(isSyntheticChange && userHasEditedBody) ||
(!!currentUserInputValue && currentUserInputValue !== valueFromExample),
})
// we never want to send up value updates from synthetic changes
@@ -157,7 +172,12 @@ export default class ExamplesSelectValueRetainer extends React.PureComponent {
componentWillReceiveProps(nextProps) {
// update `lastUserEditedValue` as new currentUserInput values come in
const { currentUserInputValue: newValue, examples, onSelect } = nextProps
const {
currentUserInputValue: newValue,
examples,
onSelect,
userHasEditedBody,
} = nextProps
const {
lastUserEditedValue,
@@ -170,7 +190,7 @@ export default class ExamplesSelectValueRetainer extends React.PureComponent {
)
const exampleMatchingNewValue = examples.find(
example =>
(example) =>
example.get("value") === newValue ||
// sometimes data is stored as a string (e.g. in Request Bodies), so
// let's check against a stringified version of our example too
@@ -186,15 +206,23 @@ export default class ExamplesSelectValueRetainer extends React.PureComponent {
newValue !== lastUserEditedValue && // value isn't already tracked
newValue !== lastDownstreamValue // value isn't what we've seen on the other side
) {
this.props.setRetainRequestBodyValueFlag(true)
this._setStateForNamespace(nextProps.currentNamespace, {
lastUserEditedValue: nextProps.currentUserInputValue,
isModifiedValueSelected: newValue !== valueFromCurrentExample,
isModifiedValueSelected:
userHasEditedBody || newValue !== valueFromCurrentExample,
})
}
}
render() {
const { currentUserInputValue, examples, currentKey, getComponent } = this.props
const {
currentUserInputValue,
examples,
currentKey,
getComponent,
userHasEditedBody,
} = this.props
const {
lastDownstreamValue,
lastUserEditedValue,
@@ -212,9 +240,10 @@ export default class ExamplesSelectValueRetainer extends React.PureComponent {
!!lastUserEditedValue && lastUserEditedValue !== lastDownstreamValue
}
isValueModified={
currentUserInputValue !== undefined &&
isModifiedValueSelected &&
currentUserInputValue !== this._getCurrentExampleValue()
(currentUserInputValue !== undefined &&
isModifiedValueSelected &&
currentUserInputValue !== this._getCurrentExampleValue()) ||
userHasEditedBody
}
/>
)