diff --git a/package.json b/package.json index 88846a91..f095ffe3 100644 --- a/package.json +++ b/package.json @@ -87,7 +87,8 @@ "worker-loader": "^0.7.1", "xml": "1.0.1", "xml-but-prettier": "^1.0.1", - "yaml-js": "0.2.0" + "yaml-js": "0.2.0", + "zenscroll": "4.0.0" }, "devDependencies": { "autoprefixer": "7.1.1", diff --git a/src/core/components/array-model.jsx b/src/core/components/array-model.jsx index e4a64c3b..0eec9b01 100644 --- a/src/core/components/array-model.jsx +++ b/src/core/components/array-model.jsx @@ -39,7 +39,7 @@ export default class ArrayModel extends Component { */ return - expandDepth } collapsedContent="[...]"> + [ { properties.size ? properties.entrySeq().map( ( [ key, v ] ) => ) : null diff --git a/src/core/components/model-collapse.jsx b/src/core/components/model-collapse.jsx index b71096f2..d501d4b1 100644 --- a/src/core/components/model-collapse.jsx +++ b/src/core/components/model-collapse.jsx @@ -4,31 +4,47 @@ import PropTypes from "prop-types" export default class ModelCollapse extends Component { static propTypes = { collapsedContent: PropTypes.any, - collapsed: PropTypes.bool, + expanded: PropTypes.bool, children: PropTypes.any, - title: PropTypes.element + title: PropTypes.element, + modelName: PropTypes.string, + onToggle: PropTypes.func.isRequired } static defaultProps = { collapsedContent: "{...}", - collapsed: true, + expanded: false, title: null } constructor(props, context) { super(props, context) - let { collapsed, collapsedContent } = this.props + let { expanded, collapsedContent } = this.props this.state = { - collapsed: collapsed !== undefined ? collapsed : ModelCollapse.defaultProps.collapsed, + expanded : expanded, collapsedContent: collapsedContent || ModelCollapse.defaultProps.collapsedContent } } + componentWillReceiveProps(nextProps){ + + if(this.props.expanded!= nextProps.expanded){ + this.setState({expanded: nextProps.expanded}) + } + + } + toggleCollapsed=()=>{ + + + if(this.props.onToggle){ + this.props.onToggle(this.props.modelName,!this.state.expanded) + } + this.setState({ - collapsed: !this.state.collapsed + expanded: !this.state.expanded }) } @@ -38,10 +54,10 @@ export default class ModelCollapse extends Component { { title && {title} } - + - { this.state.collapsed ? this.state.collapsedContent : this.props.children } + { this.state.expanded ? this.props.children :this.state.collapsedContent } ) } -} \ No newline at end of file +} diff --git a/src/core/components/model-wrapper.jsx b/src/core/components/model-wrapper.jsx index fa117c2b..24d90209 100644 --- a/src/core/components/model-wrapper.jsx +++ b/src/core/components/model-wrapper.jsx @@ -1,22 +1,41 @@ import React, { Component, } from "react" import PropTypes from "prop-types" +//import layoutActions from "actions/layout" + + +export default class ModelWrapper extends Component { + -export default class ModelComponent extends Component { static propTypes = { schema: PropTypes.object.isRequired, name: PropTypes.string, getComponent: PropTypes.func.isRequired, getConfigs: PropTypes.func.isRequired, specSelectors: PropTypes.object.isRequired, - expandDepth: PropTypes.number + expandDepth: PropTypes.number, + layoutActions: PropTypes.object, + layoutSelectors: PropTypes.object.isRequired + } + + onToggle = (name,isShown) => { + // If this prop is present, we'll have deepLinking for it + if(this.props.layoutActions) { + this.props.layoutActions.show(["models", name],isShown) + } } render(){ let { getComponent, getConfigs } = this.props const Model = getComponent("Model") + let expanded + if(this.props.layoutSelectors) { + // If this is prop is present, we'll have deepLinking for it + expanded = this.props.layoutSelectors.isShown(["models",this.props.name]) + } + return
- +
} } diff --git a/src/core/components/models.jsx b/src/core/components/models.jsx index 55965b12..6502e9b5 100644 --- a/src/core/components/models.jsx +++ b/src/core/components/models.jsx @@ -32,14 +32,17 @@ export default class Models extends Component { { definitions.entrySeq().map( ( [ name, model ])=>{ - return
+ + return
+ specSelectors={ specSelectors } + getConfigs = {getConfigs} + layoutSelectors = {layoutSelectors} + layoutActions = {layoutActions}/>
}).toArray() } diff --git a/src/core/components/object-model.jsx b/src/core/components/object-model.jsx index a20ce406..619e9a95 100644 --- a/src/core/components/object-model.jsx +++ b/src/core/components/object-model.jsx @@ -10,6 +10,8 @@ export default class ObjectModel extends Component { schema: PropTypes.object.isRequired, getComponent: PropTypes.func.isRequired, getConfigs: PropTypes.func.isRequired, + expanded: PropTypes.bool, + onToggle: PropTypes.func.isRequired, specSelectors: PropTypes.object.isRequired, name: PropTypes.string, isRef: PropTypes.bool, @@ -19,9 +21,8 @@ export default class ObjectModel extends Component { } render(){ - let { schema, name, isRef, getComponent, getConfigs, depth, specPath, expandDepth, ...otherProps } = this.props - let { specSelectors } = otherProps - + let { schema, name, isRef, getComponent, getConfigs, depth, onToggle, expanded, specPath, ...otherProps } = this.props + let { specSelectors,expandDepth } = otherProps const { isOAS3 } = specSelectors if(!schema) { @@ -61,7 +62,13 @@ export default class ObjectModel extends Component { return - expandDepth } collapsedContent={ collapsedContent }> + + { braceOpen } { !isRef ? null : diff --git a/src/core/plugins/deep-linking/spec-wrap-actions.js b/src/core/plugins/deep-linking/spec-wrap-actions.js index ebeebe5c..a90d924c 100644 --- a/src/core/plugins/deep-linking/spec-wrap-actions.js +++ b/src/core/plugins/deep-linking/spec-wrap-actions.js @@ -1,8 +1,7 @@ -import scrollTo from "scroll-to-element" +import zenscroll from "zenscroll" import { escapeDeepLinkPath } from "core/utils" -const SCROLL_OFFSET = -5 -let hasHashBeenParsed = false +let hasHashBeenParsed = false //TODO this forces code to only run once which may prevent scrolling if page not refreshed export const updateResolved = (ori, { layoutActions, getConfigs }) => (...args) => { @@ -12,7 +11,6 @@ export const updateResolved = (ori, { layoutActions, getConfigs }) => (...args) if(!isDeepLinkingEnabled || isDeepLinkingEnabled === "false") { return } - if(window.location.hash && !hasHashBeenParsed ) { let hash = window.location.hash.slice(1) // # is first character @@ -30,21 +28,23 @@ export const updateResolved = (ori, { layoutActions, getConfigs }) => (...args) let [tag, operationId] = hash.split("/") + let swaggerUI = document.querySelector(".swagger-ui") + let myScroller = zenscroll.createScroller(swaggerUI) + if(tag && operationId) { // Pre-expand and scroll to the operation layoutActions.show(["operations-tag", tag], true) layoutActions.show(["operations", tag, operationId], true) - scrollTo(`#operations-${escapeDeepLinkPath(tag)}-${escapeDeepLinkPath(operationId)}`, { - offset: SCROLL_OFFSET - }) + let target = document.getElementById(`operations-${escapeDeepLinkPath(tag)}-${escapeDeepLinkPath(operationId)}`) + myScroller.to(target) + } else if(tag) { // Pre-expand and scroll to the tag layoutActions.show(["operations-tag", tag], true) - scrollTo(`#operations-tag-${escapeDeepLinkPath(tag)}`, { - offset: SCROLL_OFFSET - }) + let target = document.getElementById(`operations-tag-${escapeDeepLinkPath(tag)}`) + myScroller.to(target) } } diff --git a/src/core/plugins/layout/actions.js b/src/core/plugins/layout/actions.js index d65d34e3..987395b0 100644 --- a/src/core/plugins/layout/actions.js +++ b/src/core/plugins/layout/actions.js @@ -37,17 +37,3 @@ export function changeMode(thing, mode="") { payload: {thing, mode} } } - - -// export function onlyShow(thing, shown=true) { -// thing = normalizeArray(thing) -// if(thing.length < 2) -// throw new Error("layoutActions.onlyShow only works, when `thing` is an array with length > 1") -// return { -// type: ONLY_SHOW, -// payload: {thing, shown} -// } -// } - - - diff --git a/src/core/plugins/layout/reducers.js b/src/core/plugins/layout/reducers.js index 4b561015..aa067abd 100644 --- a/src/core/plugins/layout/reducers.js +++ b/src/core/plugins/layout/reducers.js @@ -1,3 +1,4 @@ +import { fromJS } from "immutable" import { UPDATE_LAYOUT, UPDATE_FILTER, @@ -12,9 +13,14 @@ export default { [UPDATE_FILTER]: (state, action) => state.set("filter", action.payload), [SHOW]: (state, action) => { - let thing = action.payload.thing - let shown = action.payload.shown - return state.setIn(["shown"].concat(thing), shown) + const isShown = action.payload.shown + // This is one way to serialize an array, another (preferred) is to convert to json-pointer + // TODO: use json-pointer serilization instead of fromJS(...), for performance + const thingToShow = fromJS(action.payload.thing) + // This is a map of paths to bools + // eg: [one, two] => true + // eg: [one] => false + return state.update("shown", fromJS({}), a => a.set(thingToShow, isShown)) }, [UPDATE_MODE]: (state, action) => { @@ -24,4 +30,3 @@ export default { } } - diff --git a/src/core/plugins/layout/selectors.js b/src/core/plugins/layout/selectors.js index 42a3bcbb..7d75d37d 100644 --- a/src/core/plugins/layout/selectors.js +++ b/src/core/plugins/layout/selectors.js @@ -1,5 +1,6 @@ import { createSelector } from "reselect" import { normalizeArray } from "core/utils" +import { fromJS } from "immutable" const state = state => state @@ -9,7 +10,7 @@ export const currentFilter = state => state.get("filter") export const isShown = (state, thing, def) => { thing = normalizeArray(thing) - return Boolean(state.getIn(["shown", ...thing], def)) + return state.get("shown", fromJS({})).get(fromJS(thing), def) } export const whatMode = (state, thing, def="") => { @@ -21,4 +22,3 @@ export const showSummary = createSelector( state, state => !isShown(state, "editor") ) - diff --git a/test/components/models.js b/test/components/models.js index 49eff794..f00c691e 100644 --- a/test/components/models.js +++ b/test/components/models.js @@ -43,7 +43,7 @@ describe("", function(){ // Then should render tabs expect(wrapper.find("ModelCollapse").length).toEqual(1) - expect(wrapper.find("ModelComponent").length).toBeGreaterThan(0) + expect(wrapper.find("ModelWrapper").length).toBeGreaterThan(0) wrapper.find("ModelComponent").forEach((modelWrapper) => { expect(modelWrapper.props().expandDepth).toBe(0) })