feat: allow for simple collapsible groups

This commit is contained in:
Braden Napier 2019-03-18 20:48:23 -07:00
parent 2f65f051e0
commit cc781aba91
9 changed files with 213 additions and 151 deletions

View File

@ -17,14 +17,14 @@ info:
It was **extended** to illustrate features of [generator-openapi-repo](https://github.com/Rebilly/generator-openapi-repo) It was **extended** to illustrate features of [generator-openapi-repo](https://github.com/Rebilly/generator-openapi-repo)
tool and [ReDoc](https://github.com/Rebilly/ReDoc) documentation. In addition to standard tool and [ReDoc](https://github.com/Rebilly/ReDoc) documentation. In addition to standard
OpenAPI syntax we use a few [vendor extensions](https://github.com/Rebilly/ReDoc/blob/master/docs/redoc-vendor-extensions.md). OpenAPI syntax we use a few [vendor extensions](https://github.com/Rebilly/ReDoc/blob/master/docs/redoc-vendor-extensions.md).
# OpenAPI Specification # OpenAPI Specification
This API is documented in **OpenAPI format** and is based on This API is documented in **OpenAPI format** and is based on
[Petstore sample](http://petstore.swagger.io/) provided by [swagger.io](http://swagger.io) team. [Petstore sample](http://petstore.swagger.io/) provided by [swagger.io](http://swagger.io) team.
It was **extended** to illustrate features of [generator-openapi-repo](https://github.com/Rebilly/generator-openapi-repo) It was **extended** to illustrate features of [generator-openapi-repo](https://github.com/Rebilly/generator-openapi-repo)
tool and [ReDoc](https://github.com/Rebilly/ReDoc) documentation. In addition to standard tool and [ReDoc](https://github.com/Rebilly/ReDoc) documentation. In addition to standard
OpenAPI syntax we use a few [vendor extensions](https://github.com/Rebilly/ReDoc/blob/master/docs/redoc-vendor-extensions.md). OpenAPI syntax we use a few [vendor extensions](https://github.com/Rebilly/ReDoc/blob/master/docs/redoc-vendor-extensions.md).
# Cross-Origin Resource Sharing # Cross-Origin Resource Sharing
This API features Cross-Origin Resource Sharing (CORS) implemented in compliance with [W3C spec](https://www.w3.org/TR/cors/). This API features Cross-Origin Resource Sharing (CORS) implemented in compliance with [W3C spec](https://www.w3.org/TR/cors/).
And that allows cross-domain communication from the browser. And that allows cross-domain communication from the browser.
@ -38,24 +38,24 @@ info:
OAuth2 - an open protocol to allow secure authorization in a simple OAuth2 - an open protocol to allow secure authorization in a simple
and standard method from web, mobile and desktop applications. and standard method from web, mobile and desktop applications.
<security-definitions /> <security-definitions />
version: 1.0.0 version: 1.0.0
title: Swagger Petstore title: Swagger Petstore
termsOfService: 'http://swagger.io/terms/' termsOfService: "http://swagger.io/terms/"
contact: contact:
name: API Support name: API Support
email: apiteam@swagger.io email: apiteam@swagger.io
url: https://github.com/Rebilly/ReDoc url: https://github.com/Rebilly/ReDoc
x-logo: x-logo:
url: 'https://rebilly.github.io/ReDoc/petstore-logo.png' url: "https://rebilly.github.io/ReDoc/petstore-logo.png"
altText: Petstore logo altText: Petstore logo
license: license:
name: Apache 2.0 name: Apache 2.0
url: 'http://www.apache.org/licenses/LICENSE-2.0.html' url: "http://www.apache.org/licenses/LICENSE-2.0.html"
externalDocs: externalDocs:
description: Find out how to create Github repo for your OpenAPI spec. description: Find out how to create Github repo for your OpenAPI spec.
url: 'https://github.com/Rebilly/generator-openapi-repo' url: "https://github.com/Rebilly/generator-openapi-repo"
tags: tags:
- name: pet - name: pet
description: Everything about your Pets description: Everything about your Pets
@ -63,14 +63,26 @@ tags:
description: Access to Petstore orders description: Access to Petstore orders
- name: user - name: user
description: Operations about user description: Operations about user
- name: SubItem One
description: |
SubItem One Description!
- name: SubItem Two
description: |
SubItem Two Description!
x-tagGroups: x-tagGroups:
- name: General - name: General
tags: tags:
- pet - pet
- store - store
- name: User Management - name: Collapsible Group
collapsible: true
tags: tags:
- user - SubItem One
- SubItem Two
# - name: User Management
# tags:
# - user
paths: paths:
/pet: /pet:
parameters: parameters:
@ -88,14 +100,14 @@ paths:
description: Add new pet to the store inventory. description: Add new pet to the store inventory.
operationId: addPet operationId: addPet
responses: responses:
'405': "405":
description: Invalid input description: Invalid input
security: security:
- petstore_auth: - petstore_auth:
- 'write:pets' - "write:pets"
- 'read:pets' - "read:pets"
x-code-samples: x-code-samples:
- lang: 'C#' - lang: "C#"
source: | source: |
PetStore.v1.Pet pet = new PetStore.v1.Pet(); PetStore.v1.Pet pet = new PetStore.v1.Pet();
pet.setApiKey("your api key"); pet.setApiKey("your api key");
@ -124,24 +136,24 @@ paths:
var_dump($e->getErrors()); var_dump($e->getErrors());
} }
requestBody: requestBody:
$ref: '#/components/requestBodies/Pet' $ref: "#/components/requestBodies/Pet"
put: put:
tags: tags:
- pet - pet
summary: Update an existing pet summary: Update an existing pet
description: '' description: ""
operationId: updatePet operationId: updatePet
responses: responses:
'400': "400":
description: Invalid ID supplied description: Invalid ID supplied
'404': "404":
description: Pet not found description: Pet not found
'405': "405":
description: Validation exception description: Validation exception
security: security:
- petstore_auth: - petstore_auth:
- 'write:pets' - "write:pets"
- 'read:pets' - "read:pets"
x-code-samples: x-code-samples:
- lang: PHP - lang: PHP
source: | source: |
@ -156,8 +168,8 @@ paths:
var_dump($e->getErrors()); var_dump($e->getErrors());
} }
requestBody: requestBody:
$ref: '#/components/requestBodies/Pet' $ref: "#/components/requestBodies/Pet"
'/pet/{petId}': "/pet/{petId}":
get: get:
tags: tags:
- pet - pet
@ -174,18 +186,18 @@ paths:
type: integer type: integer
format: int64 format: int64
responses: responses:
'200': "200":
description: successful operation description: successful operation
content: content:
application/json: application/json:
schema: schema:
$ref: '#/components/schemas/Pet' $ref: "#/components/schemas/Pet"
application/xml: application/xml:
schema: schema:
$ref: '#/components/schemas/Pet' $ref: "#/components/schemas/Pet"
'400': "400":
description: Invalid ID supplied description: Invalid ID supplied
'404': "404":
description: Pet not found description: Pet not found
security: security:
- api_key: [] - api_key: []
@ -193,7 +205,7 @@ paths:
tags: tags:
- pet - pet
summary: Updates a pet in the store with form data summary: Updates a pet in the store with form data
description: '' description: ""
operationId: updatePetWithForm operationId: updatePetWithForm
parameters: parameters:
- name: petId - name: petId
@ -204,12 +216,12 @@ paths:
type: integer type: integer
format: int64 format: int64
responses: responses:
'405': "405":
description: Invalid input description: Invalid input
security: security:
- petstore_auth: - petstore_auth:
- 'write:pets' - "write:pets"
- 'read:pets' - "read:pets"
requestBody: requestBody:
content: content:
application/x-www-form-urlencoded: application/x-www-form-urlencoded:
@ -226,7 +238,7 @@ paths:
tags: tags:
- pet - pet
summary: Deletes a pet summary: Deletes a pet
description: '' description: ""
operationId: deletePet operationId: deletePet
parameters: parameters:
- name: api_key - name: api_key
@ -243,18 +255,18 @@ paths:
type: integer type: integer
format: int64 format: int64
responses: responses:
'400': "400":
description: Invalid pet value description: Invalid pet value
security: security:
- petstore_auth: - petstore_auth:
- 'write:pets' - "write:pets"
- 'read:pets' - "read:pets"
'/pet/{petId}/uploadImage': "/pet/{petId}/uploadImage":
post: post:
tags: tags:
- pet - pet
summary: uploads an image summary: uploads an image
description: '' description: ""
operationId: uploadFile operationId: uploadFile
parameters: parameters:
- name: petId - name: petId
@ -265,16 +277,16 @@ paths:
type: integer type: integer
format: int64 format: int64
responses: responses:
'200': "200":
description: successful operation description: successful operation
content: content:
application/json: application/json:
schema: schema:
$ref: '#/components/schemas/ApiResponse' $ref: "#/components/schemas/ApiResponse"
security: security:
- petstore_auth: - petstore_auth:
- 'write:pets' - "write:pets"
- 'read:pets' - "read:pets"
requestBody: requestBody:
content: content:
application/octet-stream: application/octet-stream:
@ -306,25 +318,25 @@ paths:
- sold - sold
default: available default: available
responses: responses:
'200': "200":
description: successful operation description: successful operation
content: content:
application/json: application/json:
schema: schema:
type: array type: array
items: items:
$ref: '#/components/schemas/Pet' $ref: "#/components/schemas/Pet"
application/xml: application/xml:
schema: schema:
type: array type: array
items: items:
$ref: '#/components/schemas/Pet' $ref: "#/components/schemas/Pet"
'400': "400":
description: Invalid status value description: Invalid status value
security: security:
- petstore_auth: - petstore_auth:
- 'write:pets' - "write:pets"
- 'read:pets' - "read:pets"
/pet/findByTags: /pet/findByTags:
get: get:
tags: tags:
@ -346,25 +358,25 @@ paths:
items: items:
type: string type: string
responses: responses:
'200': "200":
description: successful operation description: successful operation
content: content:
application/json: application/json:
schema: schema:
type: array type: array
items: items:
$ref: '#/components/schemas/Pet' $ref: "#/components/schemas/Pet"
application/xml: application/xml:
schema: schema:
type: array type: array
items: items:
$ref: '#/components/schemas/Pet' $ref: "#/components/schemas/Pet"
'400': "400":
description: Invalid tag value description: Invalid tag value
security: security:
- petstore_auth: - petstore_auth:
- 'write:pets' - "write:pets"
- 'read:pets' - "read:pets"
/store/inventory: /store/inventory:
get: get:
tags: tags:
@ -373,7 +385,7 @@ paths:
description: Returns a map of status codes to quantities description: Returns a map of status codes to quantities
operationId: getInventory operationId: getInventory
responses: responses:
'200': "200":
description: successful operation description: successful operation
content: content:
application/json: application/json:
@ -389,19 +401,19 @@ paths:
tags: tags:
- store - store
summary: Place an order for a pet summary: Place an order for a pet
description: '' description: ""
operationId: placeOrder operationId: placeOrder
responses: responses:
'200': "200":
description: successful operation description: successful operation
content: content:
application/json: application/json:
schema: schema:
$ref: '#/components/schemas/Order' $ref: "#/components/schemas/Order"
application/xml: application/xml:
schema: schema:
$ref: '#/components/schemas/Order' $ref: "#/components/schemas/Order"
'400': "400":
description: Invalid Order description: Invalid Order
content: content:
application/json: application/json:
@ -412,10 +424,10 @@ paths:
content: content:
application/json: application/json:
schema: schema:
$ref: '#/components/schemas/Order' $ref: "#/components/schemas/Order"
description: order placed for purchasing the pet description: order placed for purchasing the pet
required: true required: true
'/store/order/{orderId}': "/store/order/{orderId}":
get: get:
tags: tags:
- store - store
@ -435,18 +447,18 @@ paths:
minimum: 1 minimum: 1
maximum: 5 maximum: 5
responses: responses:
'200': "200":
description: successful operation description: successful operation
content: content:
application/json: application/json:
schema: schema:
$ref: '#/components/schemas/Order' $ref: "#/components/schemas/Order"
application/xml: application/xml:
schema: schema:
$ref: '#/components/schemas/Order' $ref: "#/components/schemas/Order"
'400': "400":
description: Invalid ID supplied description: Invalid ID supplied
'404': "404":
description: Order not found description: Order not found
delete: delete:
tags: tags:
@ -465,9 +477,9 @@ paths:
type: string type: string
minimum: 1 minimum: 1
responses: responses:
'400': "400":
description: Invalid ID supplied description: Invalid ID supplied
'404': "404":
description: Order not found description: Order not found
/user: /user:
post: post:
@ -483,36 +495,36 @@ paths:
content: content:
application/json: application/json:
schema: schema:
$ref: '#/components/schemas/User' $ref: "#/components/schemas/User"
description: Created user object description: Created user object
required: true required: true
'/user/{username}': "/user/{username}":
get: get:
tags: tags:
- user - user
summary: Get user by user name summary: Get user by user name
description: '' description: ""
operationId: getUserByName operationId: getUserByName
parameters: parameters:
- name: username - name: username
in: path in: path
description: 'The name that needs to be fetched. Use user1 for testing. ' description: "The name that needs to be fetched. Use user1 for testing. "
required: true required: true
schema: schema:
type: string type: string
responses: responses:
'200': "200":
description: successful operation description: successful operation
content: content:
application/json: application/json:
schema: schema:
$ref: '#/components/schemas/User' $ref: "#/components/schemas/User"
application/xml: application/xml:
schema: schema:
$ref: '#/components/schemas/User' $ref: "#/components/schemas/User"
'400': "400":
description: Invalid username supplied description: Invalid username supplied
'404': "404":
description: User not found description: User not found
put: put:
tags: tags:
@ -528,15 +540,15 @@ paths:
schema: schema:
type: string type: string
responses: responses:
'400': "400":
description: Invalid user supplied description: Invalid user supplied
'404': "404":
description: User not found description: User not found
requestBody: requestBody:
content: content:
application/json: application/json:
schema: schema:
$ref: '#/components/schemas/User' $ref: "#/components/schemas/User"
description: Updated user object description: Updated user object
required: true required: true
delete: delete:
@ -553,40 +565,40 @@ paths:
schema: schema:
type: string type: string
responses: responses:
'400': "400":
description: Invalid username supplied description: Invalid username supplied
'404': "404":
description: User not found description: User not found
/user/createWithArray: /user/createWithArray:
post: post:
tags: tags:
- user - user
summary: Creates list of users with given input array summary: Creates list of users with given input array
description: '' description: ""
operationId: createUsersWithArrayInput operationId: createUsersWithArrayInput
responses: responses:
default: default:
description: successful operation description: successful operation
requestBody: requestBody:
$ref: '#/components/requestBodies/UserArray' $ref: "#/components/requestBodies/UserArray"
/user/createWithList: /user/createWithList:
post: post:
tags: tags:
- user - user
summary: Creates list of users with given input array summary: Creates list of users with given input array
description: '' description: ""
operationId: createUsersWithListInput operationId: createUsersWithListInput
responses: responses:
default: default:
description: successful operation description: successful operation
requestBody: requestBody:
$ref: '#/components/requestBodies/UserArray' $ref: "#/components/requestBodies/UserArray"
/user/login: /user/login:
get: get:
tags: tags:
- user - user
summary: Logs user into the system summary: Logs user into the system
description: '' description: ""
operationId: loginUser operationId: loginUser
parameters: parameters:
- name: username - name: username
@ -602,7 +614,7 @@ paths:
schema: schema:
type: string type: string
responses: responses:
'200': "200":
description: successful operation description: successful operation
headers: headers:
X-Rate-Limit: X-Rate-Limit:
@ -627,19 +639,19 @@ paths:
type: string type: string
examples: examples:
response: response:
value: <Message> OK </Message> value: <Message> OK </Message>
text/plain: text/plain:
examples: examples:
response: response:
value: OK value: OK
'400': "400":
description: Invalid username/password supplied description: Invalid username/password supplied
/user/logout: /user/logout:
get: get:
tags: tags:
- user - user
summary: Logs out current logged in user session summary: Logs out current logged in user session
description: '' description: ""
operationId: logoutUser operationId: logoutUser
responses: responses:
default: default:
@ -659,7 +671,7 @@ components:
Cat: Cat:
description: A representation of a cat description: A representation of a cat
allOf: allOf:
- $ref: '#/components/schemas/Pet' - $ref: "#/components/schemas/Pet"
- type: object - type: object
properties: properties:
huntingSkill: huntingSkill:
@ -679,7 +691,7 @@ components:
id: id:
description: Category ID description: Category ID
allOf: allOf:
- $ref: '#/components/schemas/Id' - $ref: "#/components/schemas/Id"
name: name:
description: Category name description: Category name
type: string type: string
@ -696,7 +708,7 @@ components:
Dog: Dog:
description: A representation of a dog description: A representation of a dog
allOf: allOf:
- $ref: '#/components/schemas/Pet' - $ref: "#/components/schemas/Pet"
- type: object - type: object
properties: properties:
packSize: packSize:
@ -710,7 +722,7 @@ components:
HoneyBee: HoneyBee:
description: A representation of a honey bee description: A representation of a honey bee
allOf: allOf:
- $ref: '#/components/schemas/Pet' - $ref: "#/components/schemas/Pet"
- type: object - type: object
properties: properties:
honeyPerDay: honeyPerDay:
@ -729,11 +741,11 @@ components:
id: id:
description: Order ID description: Order ID
allOf: allOf:
- $ref: '#/components/schemas/Id' - $ref: "#/components/schemas/Id"
petId: petId:
description: Pet ID description: Pet ID
allOf: allOf:
- $ref: '#/components/schemas/Id' - $ref: "#/components/schemas/Id"
quantity: quantity:
type: integer type: integer
format: int32 format: int32
@ -764,9 +776,9 @@ components:
discriminator: discriminator:
propertyName: petType propertyName: petType
mapping: mapping:
cat: '#/components/schemas/Cat' cat: "#/components/schemas/Cat"
dog: '#/components/schemas/Dog' dog: "#/components/schemas/Dog"
bee: '#/components/schemas/HoneyBee' bee: "#/components/schemas/HoneyBee"
properties: properties:
id: id:
externalDocs: externalDocs:
@ -774,11 +786,11 @@ components:
url: "https://example.com" url: "https://example.com"
description: Pet ID description: Pet ID
allOf: allOf:
- $ref: '#/components/schemas/Id' - $ref: "#/components/schemas/Id"
category: category:
description: Categories this pet belongs to description: Categories this pet belongs to
allOf: allOf:
- $ref: '#/components/schemas/Category' - $ref: "#/components/schemas/Category"
name: name:
description: The name given to a pet description: The name given to a pet
type: string type: string
@ -795,7 +807,7 @@ components:
format: url format: url
friend: friend:
allOf: allOf:
- $ref: '#/components/schemas/Pet' - $ref: "#/components/schemas/Pet"
tags: tags:
description: Tags attached to the pet description: Tags attached to the pet
type: array type: array
@ -804,7 +816,7 @@ components:
name: tag name: tag
wrapped: true wrapped: true
items: items:
$ref: '#/components/schemas/Tag' $ref: "#/components/schemas/Tag"
status: status:
type: string type: string
description: Pet status in the store description: Pet status in the store
@ -823,7 +835,7 @@ components:
id: id:
description: Tag ID description: Tag ID
allOf: allOf:
- $ref: '#/components/schemas/Id' - $ref: "#/components/schemas/Id"
name: name:
description: Tag name description: Tag name
type: string type: string
@ -834,11 +846,11 @@ components:
type: object type: object
properties: properties:
id: id:
$ref: '#/components/schemas/Id' $ref: "#/components/schemas/Id"
pet: pet:
oneOf: oneOf:
- $ref: '#/components/schemas/Pet' - $ref: "#/components/schemas/Pet"
- $ref: '#/components/schemas/Tag' - $ref: "#/components/schemas/Tag"
username: username:
description: User supplied username description: User supplied username
type: string type: string
@ -866,7 +878,7 @@ components:
as well as digits as well as digits
format: password format: password
minLength: 8 minLength: 8
pattern: '(?=.*[a-z])(?=.*[A-Z])(?=.*[0-9])' pattern: "(?=.*[a-z])(?=.*[A-Z])(?=.*[0-9])"
example: drowssaP123 example: drowssaP123
phone: phone:
description: User phone number in international format description: User phone number in international format
@ -888,10 +900,10 @@ components:
allOf: allOf:
- description: My Pet - description: My Pet
title: Pettie title: Pettie
- $ref: '#/components/schemas/Pet' - $ref: "#/components/schemas/Pet"
application/xml: application/xml:
schema: schema:
type: 'object' type: "object"
properties: properties:
name: name:
type: string type: string
@ -904,7 +916,7 @@ components:
schema: schema:
type: array type: array
items: items:
$ref: '#/components/schemas/User' $ref: "#/components/schemas/User"
description: List of user object description: List of user object
required: true required: true
securitySchemes: securitySchemes:
@ -915,10 +927,10 @@ components:
type: oauth2 type: oauth2
flows: flows:
implicit: implicit:
authorizationUrl: 'http://petstore.swagger.io/api/oauth/dialog' authorizationUrl: "http://petstore.swagger.io/api/oauth/dialog"
scopes: scopes:
'write:pets': modify pets in your account "write:pets": modify pets in your account
'read:pets': read your pets "read:pets": read your pets
api_key: api_key:
description: > description: >
For this sample, you can use the api key `special-key` to test the For this sample, you can use the api key `special-key` to test the

View File

@ -22,7 +22,8 @@ Information about tags group
| Field Name | Type | Description | | Field Name | Type | Description |
| :---------- | :--------: | :---------- | | :---------- | :--------: | :---------- |
| name | string | The group name | | name | string | The group name |
| tags | [ string ] | List of tags to include in this group | collapsible | boolean | Makes the group collapsible in the menu when true |
| tags | [ string ] | List of tags to include in this group |
###### x-tagGroups example ###### x-tagGroups example
json json
@ -35,6 +36,7 @@ json
}, },
{ {
"name": "Statistics", "name": "Statistics",
"collapsible": true,
"tags": ["Main Stats", "Secondary Stats"] "tags": ["Main Stats", "Secondary Stats"]
} }
] ]

View File

@ -52,27 +52,29 @@ export class MenuItem extends React.Component<MenuItemProps> {
{item.type === 'operation' ? ( {item.type === 'operation' ? (
<OperationMenuItemContent {...this.props} item={item as OperationModel} /> <OperationMenuItemContent {...this.props} item={item as OperationModel} />
) : ( ) : (
<MenuItemLabel depth={item.depth} active={item.active} type={item.type}> <MenuItemLabel
depth={item.depth}
active={item.active}
type={item.type}
collapsible={item.collapsible}
>
<MenuItemTitle title={item.name}> <MenuItemTitle title={item.name}>
{item.name} {item.name}
{this.props.children} {this.props.children}
</MenuItemTitle> </MenuItemTitle>
{(item.depth > 0 && {(item.collapsible === true && item.items.length > 0 && (
item.items.length > 0 && ( <ShelfIcon float={'right'} direction={item.expanded ? 'down' : 'right'} />
<ShelfIcon float={'right'} direction={item.expanded ? 'down' : 'right'} /> )) ||
)) ||
null} null}
</MenuItemLabel> </MenuItemLabel>
)} )}
{!withoutChildren && {!withoutChildren && item.items && item.items.length > 0 && (
item.items && <MenuItems
item.items.length > 0 && ( expanded={item.expanded}
<MenuItems items={item.items}
expanded={item.expanded} onActivate={this.props.onActivate}
items={item.items} />
onActivate={this.props.onActivate} )}
/>
)}
</MenuItemLi> </MenuItemLi>
); );
} }
@ -87,7 +89,12 @@ class OperationMenuItemContent extends React.Component<OperationMenuItemContentP
render() { render() {
const { item } = this.props; const { item } = this.props;
return ( return (
<MenuItemLabel depth={item.depth} active={item.active} deprecated={item.deprecated}> <MenuItemLabel
type={item.type}
depth={item.depth}
active={item.active}
deprecated={item.deprecated}
>
<OperationBadge type={item.httpVerb}>{shortenHTTPVerb(item.httpVerb)}</OperationBadge> <OperationBadge type={item.httpVerb}>{shortenHTTPVerb(item.httpVerb)}</OperationBadge>
<MenuItemTitle width="calc(100% - 38px)"> <MenuItemTitle width="calc(100% - 38px)">
{item.name} {item.name}

View File

@ -32,7 +32,11 @@ export class SideMenu extends React.Component<{ menu: MenuStore; className?: str
} }
activate = (item: IMenuItem) => { activate = (item: IMenuItem) => {
this.props.menu.activateAndScroll(item, true); if (item.expanded === true && item.items.length > 0) {
this.props.menu.collapse(item);
} else {
this.props.menu.activateAndScroll(item, true);
}
setTimeout(() => { setTimeout(() => {
if (this._updateScroll) { if (this._updateScroll) {
this._updateScroll(); this._updateScroll();

View File

@ -116,6 +116,8 @@ export interface MenuItemLabelType {
depth: number; depth: number;
active: boolean; active: boolean;
deprecated?: boolean; deprecated?: boolean;
collapsible?: boolean;
type?: string; type?: string;
} }
@ -128,9 +130,12 @@ export const MenuItemLabel = styled.label.attrs((props: MenuItemLabelType) => ({
cursor: pointer; cursor: pointer;
color: ${props => (props.active ? props.theme.colors.primary.main : props.theme.menu.textColor)}; color: ${props => (props.active ? props.theme.colors.primary.main : props.theme.menu.textColor)};
margin: 0; margin: 0;
margin-left: ${({ type, depth }) => (type === 'operation' && depth > 1 ? `${depth * 5}px` : '')};
padding: 12.5px ${props => props.theme.spacing.unit * 4}px; padding: 12.5px ${props => props.theme.spacing.unit * 4}px;
${({ depth, type, theme }) => ${({ depth, type, collapsible, theme }) =>
(type === 'section' && depth > 1 && 'padding-left: ' + theme.spacing.unit * 8 + 'px;') || ''} (((type === 'section' && depth > 1) || (type === 'tag' && collapsible === true)) &&
'padding-left: ' + theme.spacing.unit * 8 + 'px;') ||
''}
display: flex; display: flex;
justify-content: space-between; justify-content: space-between;
font-family: ${props => props.theme.typography.headings.fontFamily}; font-family: ${props => props.theme.typography.headings.fontFamily};

View File

@ -99,6 +99,7 @@ export class MenuBuilder {
const res: GroupModel[] = []; const res: GroupModel[] = [];
for (const group of groups) { for (const group of groups) {
const item = new GroupModel('group', group, parent); const item = new GroupModel('group', group, parent);
// item.depth = item.collapsible ? 1 : GROUP_DEPTH;
item.depth = GROUP_DEPTH; item.depth = GROUP_DEPTH;
item.items = MenuBuilder.getTagsItems(parser, tags, item, group, options); item.items = MenuBuilder.getTagsItems(parser, tags, item, group, options);
res.push(item); res.push(item);
@ -143,7 +144,7 @@ export class MenuBuilder {
continue; continue;
} }
const item = new GroupModel('tag', tag, parent); const item = new GroupModel('tag', tag, parent);
item.depth = GROUP_DEPTH + 1; item.depth = ((item.parent && item.parent.depth) || GROUP_DEPTH) + 1;
item.items = this.getOperationsItems(parser, item, tag, item.depth + 1, options); item.items = this.getOperationsItems(parser, item, tag, item.depth + 1, options);
// don't put empty tag into content, instead put its operations // don't put empty tag into content, instead put its operations

View File

@ -6,7 +6,7 @@ import { history as historyInst, HistoryService } from './HistoryService';
import { ScrollService } from './ScrollService'; import { ScrollService } from './ScrollService';
import { flattenByProp, SECURITY_SCHEMES_SECTION_PREFIX } from '../utils'; import { flattenByProp, SECURITY_SCHEMES_SECTION_PREFIX } from '../utils';
import { GROUP_DEPTH } from './MenuBuilder'; // import { GROUP_DEPTH } from './MenuBuilder';
export type MenuItemGroupType = 'group' | 'tag' | 'section'; export type MenuItemGroupType = 'group' | 'tag' | 'section';
export type MenuItemType = MenuItemGroupType | 'operation'; export type MenuItemType = MenuItemGroupType | 'operation';
@ -22,6 +22,9 @@ export interface IMenuItem {
expanded: boolean; expanded: boolean;
items: IMenuItem[]; items: IMenuItem[];
parent?: IMenuItem; parent?: IMenuItem;
collapsible?: boolean;
deprecated?: boolean; deprecated?: boolean;
type: MenuItemType; type: MenuItemType;
@ -128,7 +131,6 @@ export class MenuStore {
} }
itemIdx += step; itemIdx += step;
} }
this.activate(this.flatItems[itemIdx], true, true); this.activate(this.flatItems[itemIdx], true, true);
}; };
@ -160,6 +162,12 @@ export class MenuStore {
*/ */
getElementAt(idx: number): Element | null { getElementAt(idx: number): Element | null {
const item = this.flatItems[idx]; const item = this.flatItems[idx];
if (item.type === 'group') {
// group attempts to return first child - likely requires a smarter
// search function for highly-nested values, however at this time this
// should be enough.
return this.getElementAt(item.items[0].absoluteIdx || -1);
}
return (item && querySelector(`[${SECTION_ATTR}="${item.id}"]`)) || null; return (item && querySelector(`[${SECTION_ATTR}="${item.id}"]`)) || null;
} }
@ -190,24 +198,39 @@ export class MenuStore {
return; return;
} }
this.deactivate(this.activeItem); this.deactivate(this.activeItem);
if (!item) {
if (!item || item.collapsible === false) {
this.history.replace('', rewriteHistory); this.history.replace('', rewriteHistory);
this.activeItemIdx = -1;
return; return;
} }
// do not allow activating group items this.activeItemIdx = item.absoluteIdx || -1;
// TODO: control over options
if (item.depth <= GROUP_DEPTH) {
return;
}
this.activeItemIdx = item.absoluteIdx!;
if (updateLocation) { if (updateLocation) {
this.history.replace(item.id, rewriteHistory); this.history.replace(item.id, rewriteHistory);
} }
item.activate(); if (item.type === 'group') {
item.expand(); this.activate(item.items[0], updateLocation, rewriteHistory);
} else {
item.activate();
item.expand();
}
}
collapse(item: IMenuItem) {
this.activate(item.parent, true, true);
const activeItem = this.activeItem;
if (!activeItem) {
if (item.items.length > 0) {
this.scroll.scrollIntoView(this.getElementAt(item.absoluteIdx || -1));
} else if (item.parent && item.parent.type === 'group') {
this.scroll.scrollIntoView(this.getElementAt(item.parent.absoluteIdx || -1));
}
} else {
this.scroll.scrollIntoView(this.getElementAt(activeItem.absoluteIdx || -1));
}
} }
/** /**

View File

@ -19,6 +19,8 @@ export class GroupModel implements IMenuItem {
items: ContentItemModel[] = []; items: ContentItemModel[] = [];
parent?: GroupModel; parent?: GroupModel;
collapsible: boolean = true;
externalDocs?: OpenAPIExternalDocumentation; externalDocs?: OpenAPIExternalDocumentation;
@observable @observable
@ -43,10 +45,14 @@ export class GroupModel implements IMenuItem {
this.description = tagOrGroup.description || ''; this.description = tagOrGroup.description || '';
this.parent = parent; this.parent = parent;
this.externalDocs = (tagOrGroup as OpenAPITag).externalDocs; this.externalDocs = (tagOrGroup as OpenAPITag).externalDocs;
const isCollapsible = (tagOrGroup as OpenAPITag).collapsible;
// groups are active (expanded) by default if (isCollapsible != null) {
this.collapsible = Boolean(isCollapsible);
} else if (this.type === 'group') {
this.collapsible = false;
}
if (this.type === 'group') { if (this.type === 'group') {
this.expanded = true; this.expanded = this.collapsible ? false : true;
} }
} }
@ -66,7 +72,7 @@ export class GroupModel implements IMenuItem {
@action @action
collapse() { collapse() {
// disallow collapsing groups // disallow collapsing groups
if (this.type === 'group') { if (this.collapsible === false) {
return; return;
} }
this.expanded = false; this.expanded = false;

View File

@ -248,6 +248,8 @@ export interface OpenAPISecurityScheme {
export interface OpenAPITag { export interface OpenAPITag {
name: string; name: string;
collapsible?: boolean;
description?: string; description?: string;
externalDocs?: OpenAPIExternalDocumentation; externalDocs?: OpenAPIExternalDocumentation;
'x-displayName'?: string; 'x-displayName'?: string;