Skip to content

Commit

Permalink
Merge pull request temando#117 from brendo/issue-111-add-tag-support
Browse files Browse the repository at this point in the history
Add support for top level Tags. Closes temando#111
  • Loading branch information
quangkhoa authored Jun 16, 2017
2 parents a870d02 + cfb3a8b commit 3e8420e
Show file tree
Hide file tree
Showing 13 changed files with 264 additions and 30 deletions.
6 changes: 3 additions & 3 deletions docs/open-api-v3-support.md
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ This document outlines this project's support for visualising the [Open API V3][
- [x] [paths](#paths-object)
- [ ] [components](#components-object)
- [x] [security](#security-requirement-object)
- [ ] [tags](#tag-object)
- [x] [tags](#tag-object)
- [ ] [externalDocs](#external-documentation-object)

### [Info](https://github.com/OAI/OpenAPI-Specification/blob/OpenAPI.next/versions/3.0.md#info-object) object
Expand Down Expand Up @@ -207,8 +207,8 @@ See [parameter](#parameter-object) object.

### [Tag](https://github.com/OAI/OpenAPI-Specification/blob/OpenAPI.next/versions/3.0.md#tag-object) object

- [ ] name
- [ ] description
- [x] name
- [x] description
- [ ] [externalDocs](#external-documentation-object)

### [Reference](https://github.com/OAI/OpenAPI-Specification/blob/OpenAPI.next/versions/3.0.md#reference-object) object
Expand Down
4 changes: 4 additions & 0 deletions src/components/Description/Description.scss
Original file line number Diff line number Diff line change
Expand Up @@ -2,3 +2,7 @@
.description-inline p {
display: inline;
}

.description p {
margin: .5rem 0;
}
1 change: 1 addition & 0 deletions src/components/Navigation/Navigation.js
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,7 @@ export default class Navigation extends Component {
<NavigationTag
key={tag.title}
title={tag.title}
description={tag.description}
methods={tag.methods}
shouldBeExpanded={shouldBeExpanded}
onClick={this.onClick}
Expand Down
10 changes: 5 additions & 5 deletions src/components/Navigation/Navigation.scss
Original file line number Diff line number Diff line change
Expand Up @@ -9,14 +9,14 @@
top: 0;
box-sizing: border-box;
overflow-y: auto;
a {
text-decoration: none;
}

.indicator {
float: right;
display: block;
margin-top: 5px;
}

> div {
margin: 25px 20px;
> div + div {
border-top: 1px solid darken(#FFF, 80%);
}
}
15 changes: 6 additions & 9 deletions src/components/NavigationMethod/NavigationMethod.scss
Original file line number Diff line number Diff line change
@@ -1,11 +1,14 @@
@import '../../base.scss';

.nav-tag-methods {
margin-bottom: .5gqgrem
}

.nav-method {
display: flex;
margin: 10px 5px;
padding: 10px;
padding: .7rem 1.5rem;
font-size: smaller;
color: white;
color: #FFF;

&.active {
background-color: $content-background;
Expand All @@ -19,18 +22,12 @@
display: none;
}

&:first-child {
margin-top: 20px;
}

.method-type {
width: 55px;
}

.method-title {
padding-left: 5px;
width: calc(100% - 55px);
text-indent: 0;
}
}

Expand Down
14 changes: 11 additions & 3 deletions src/components/NavigationTag/NavigationTag.js
Original file line number Diff line number Diff line change
@@ -1,7 +1,9 @@
import React, { Component } from 'react'
import PropTypes from 'prop-types'

import Indicator from '../Indicator/Indicator'
import NavigationMethod from '../NavigationMethod/NavigationMethod'
import Description from '../Description/Description'

import './NavigationTag.scss'

Expand All @@ -22,6 +24,10 @@ export default class NavigationTag extends Component {
componentWillMount () {
const { title, methods, location, onClick } = this.props

if (!methods) {
return null
}

const method = methods.find(
(method) => (`#${method.link}` === location.hash)
)
Expand All @@ -36,7 +42,7 @@ export default class NavigationTag extends Component {
}

render () {
const { title, shouldBeExpanded, methods, location } = this.props
const { title, description, shouldBeExpanded, methods, location } = this.props

// If tag has any method that matches location hash, then it is considered active
let isActiveTag = false
Expand All @@ -63,8 +69,9 @@ export default class NavigationTag extends Component {
href={`#${title}`}
onClick={this.handleClick}
>
{title}
<span>{title}</span>
<Indicator direction={indicatorDirection} />
{description && <Description description={description} />}
</a>
<div className='nav-tag-methods'>
{methods && methods.map((method) => {
Expand All @@ -80,8 +87,9 @@ export default class NavigationTag extends Component {

NavigationTag.propTypes = {
title: PropTypes.string.isRequired,
description: PropTypes.string,
methods: PropTypes.array,
shouldBeExpanded: PropTypes.bool,
onClick: PropTypes.func.isRequired,
location: PropTypes.object
location: PropTypes.object.isRequired
}
14 changes: 10 additions & 4 deletions src/components/NavigationTag/NavigationTag.scss
Original file line number Diff line number Diff line change
@@ -1,9 +1,15 @@
@import '../../base.scss';

.nav-tag {
display: block;
color: white;
margin-top: 15px;
padding: 1rem 1rem .5rem;
color: #FFF;

&:hover {
text-decoration: none;

span {
text-decoration: underline;
}
}

&.active {
background-color: rgb(12, 18, 29);
Expand Down
45 changes: 39 additions & 6 deletions src/parser/open-api/v3/open-api-v3-parser.js
Original file line number Diff line number Diff line change
Expand Up @@ -89,17 +89,17 @@ function getUINavigationAndServices (tags, paths, globalSecurity = [], securityD

/**
* Add media type info, e.g. schema and examples to UI object
* This method modifies the uiObject input
* This method mutates the `uiObject` parameter.
*
* @param {Object} uiObj
* @param {Object} uiObject
* @param {Object} mediaType Open API mediaType object
*/
function addMediaTypeInfoToUIObject (uiObj, mediaType) {
function addMediaTypeInfoToUIObject (uiObject, mediaType) {
if (mediaType.schema) {
const schema = getUIReadySchema(mediaType.schema)

if (schema.length) {
uiObj.schema = schema
uiObject.schema = schema
}
}

Expand All @@ -114,7 +114,7 @@ function addMediaTypeInfoToUIObject (uiObj, mediaType) {
}

if (examples.length) {
uiObj.examples = examples
uiObject.examples = examples
}
}

Expand Down Expand Up @@ -286,6 +286,34 @@ function getTags (paths) {
return tagCollection.sort()
}

/**
* If tag definitions exist, extract this information and add it to the
* navigation array. This mutates the `navigation` parameter.
*
* @param {Array} navigation
* @param {Array} tagDefinitions
*/
function addTagDetailsToNavigation (navigation, tagDefinitions) {
const getTag = (tag) => tagDefinitions.find((def) => def.name === tag)

for (const navGroup of navigation) {
const tagDefinition = getTag(navGroup.title)

if (tagDefinition) {
navGroup.handle = navGroup.title
navGroup.title = tagDefinition.name

if (tagDefinition.description) {
navGroup.description = tagDefinition.description
}

if (tagDefinition.externalDocs) {
navGroup.externalDocs = tagDefinition.externalDocs
}
}
}
}

/**
* Converts openApiV3 object to new object ready to be consumed by the UI
*
Expand All @@ -303,7 +331,7 @@ export default async function getUIReadyDefinition (openApiV3) {
const info = derefOpenApiV3.info
const paths = derefOpenApiV3.paths

// Get tags
// Get tags from the paths
const tags = getTags(paths)

// Get security definitions
Expand All @@ -312,6 +340,11 @@ export default async function getUIReadyDefinition (openApiV3) {
// Construction navigation and services
const {navigation, services} = getUINavigationAndServices(tags, paths, derefOpenApiV3.security || [], security)

// If we have tag information, let's add it to the navigation
if (derefOpenApiV3.tags) {
addTagDetailsToNavigation(navigation, derefOpenApiV3.tags)
}

const definition = {
title: info.title,
version: info.version,
Expand Down
44 changes: 44 additions & 0 deletions test/components/NavigationTag.test.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
import React from 'react'
import NavigationTag from './../../src/components/NavigationTag/NavigationTag'
import renderer from 'react-test-renderer'

describe('<NavigationTag />', () => {
const location = {
hash: '#pets'
}

it('renders with minimum props', () => {
const tree = renderer.create(
<NavigationTag title='pets' onClick={jest.fn()} location={location} />
)

expect(tree).toMatchSnapshot()
})

it('renders with methods', () => {
const methods = [{
type: 'get',
title: 'Get pets',
link: 'pets'
},
{
type: 'get',
title: 'Get pets',
link: 'pets/:id'
}]

const tree = renderer.create(
<NavigationTag title='pets' onClick={jest.fn()} location={location} methods={methods} />
)

expect(tree).toMatchSnapshot()
})

it('renders with description', () => {
const tree = renderer.create(
<NavigationTag title='Pets' handle='pets' description='Access to Pets' onClick={jest.fn()} location={location} />
)

expect(tree).toMatchSnapshot()
})
})
Loading

0 comments on commit 3e8420e

Please sign in to comment.