Skip to content

Commit a5cab61

Browse files
authored
Merge pull request #3196 from gwynjudd/filter
Added maxRows and filter support
2 parents 4a66219 + 66c8778 commit a5cab61

File tree

10 files changed

+91
-29
lines changed

10 files changed

+91
-29
lines changed

README.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -144,6 +144,8 @@ modelPropertyMacro | MUST be a function. Function to set default values to each
144144
docExpansion | Controls the default expansion setting for the operations and tags. It can be 'list' (expands only the tags), 'full' (expands the tags and operations) or 'none' (expands nothing). The default is 'list'.
145145
displayOperationId | Controls the display of operationId in operations list. The default is `false`.
146146
displayRequestDuration | Controls the display of the request duration (in milliseconds) for `Try it out` requests. The default is `false`.
147+
maxDisplayedTags | If set, limits the number of tagged operations displayed to at most this many. The default is to show all operations.
148+
filter | If set, enables filtering. The top bar will show an edit box that you can use to filter the tagged operations that are shown. Can be true/false to enable or disable, or an explicit filter string in which case filtering will be enabled using that string as the filter expression. Filtering is case sensitive matching the filter expression anywhere inside the tag.
147149

148150
### Plugins
149151

src/core/components/operations.jsx

Lines changed: 15 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -33,7 +33,21 @@ export default class Operations extends React.Component {
3333
const Collapse = getComponent("Collapse")
3434

3535
let showSummary = layoutSelectors.showSummary()
36-
let { docExpansion, displayOperationId, displayRequestDuration } = getConfigs()
36+
let { docExpansion, displayOperationId, displayRequestDuration, maxDisplayedTags } = getConfigs()
37+
38+
let filter = layoutSelectors.currentFilter()
39+
40+
if (filter) {
41+
if (filter !== true) {
42+
taggedOps = taggedOps.filter((tagObj, tag) => {
43+
return tag.indexOf(filter) !== -1
44+
})
45+
}
46+
}
47+
48+
if (maxDisplayedTags && !isNaN(maxDisplayedTags) && maxDisplayedTags >= 0) {
49+
taggedOps = taggedOps.slice(0, maxDisplayedTags)
50+
}
3751

3852
return (
3953
<div>

src/core/index.js

Lines changed: 8 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ import ApisPreset from "core/presets/apis"
66
import * as AllPlugins from "core/plugins/all"
77
import { parseSeach, filterConfigs } from "core/utils"
88

9-
const CONFIGS = [ "url", "urls", "urls.primaryName", "spec", "validatorUrl", "onComplete", "onFailure", "authorizations", "docExpansion",
9+
const CONFIGS = [ "url", "urls", "urls.primaryName", "spec", "validatorUrl", "onComplete", "onFailure", "authorizations", "docExpansion", "maxDisplayedTags", "filter",
1010
"apisSorter", "operationsSorter", "supportedSubmitMethods", "dom_id", "defaultModelRendering", "oauth2RedirectUrl",
1111
"showRequestHeaders", "custom", "modelPropertyMacro", "parameterMacro", "displayOperationId" , "displayRequestDuration"]
1212

@@ -26,6 +26,8 @@ module.exports = function SwaggerUI(opts) {
2626
urls: null,
2727
layout: "BaseLayout",
2828
docExpansion: "list",
29+
maxDisplayedTags: null,
30+
filter: null,
2931
validatorUrl: "https://online.swagger.io/validator",
3032
configs: {},
3133
custom: {},
@@ -50,7 +52,9 @@ module.exports = function SwaggerUI(opts) {
5052
store: { },
5153
}
5254

53-
const constructorConfig = deepExtend({}, defaults, opts)
55+
let queryConfig = parseSeach()
56+
57+
const constructorConfig = deepExtend({}, defaults, opts, queryConfig)
5458

5559
const storeConfigs = deepExtend({}, constructorConfig.store, {
5660
system: {
@@ -59,7 +63,8 @@ module.exports = function SwaggerUI(opts) {
5963
plugins: constructorConfig.presets,
6064
state: {
6165
layout: {
62-
layout: constructorConfig.layout
66+
layout: constructorConfig.layout,
67+
filter: constructorConfig.filter
6368
},
6469
spec: {
6570
spec: "",
@@ -80,7 +85,6 @@ module.exports = function SwaggerUI(opts) {
8085
store.register([constructorConfig.plugins, inlinePlugin])
8186

8287
var system = store.getSystem()
83-
let queryConfig = parseSeach()
8488

8589
system.initOAuth = system.authActions.configureAuth
8690

src/core/plugins/layout/actions.js

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
import { normalizeArray } from "core/utils"
22

33
export const UPDATE_LAYOUT = "layout_update_layout"
4+
export const UPDATE_FILTER = "layout_update_filter"
45
export const UPDATE_MODE = "layout_update_mode"
56
export const SHOW = "layout_show"
67

@@ -13,6 +14,13 @@ export function updateLayout(layout) {
1314
}
1415
}
1516

17+
export function updateFilter(filter) {
18+
return {
19+
type: UPDATE_FILTER,
20+
payload: filter
21+
}
22+
}
23+
1624
export function show(thing, shown=true) {
1725
thing = normalizeArray(thing)
1826
return {

src/core/plugins/layout/reducers.js

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
import {
22
UPDATE_LAYOUT,
3+
UPDATE_FILTER,
34
UPDATE_MODE,
45
SHOW
56
} from "./actions"
@@ -8,6 +9,8 @@ export default {
89

910
[UPDATE_LAYOUT]: (state, action) => state.set("layout", action.payload),
1011

12+
[UPDATE_FILTER]: (state, action) => state.set("filter", action.payload),
13+
1114
[SHOW]: (state, action) => {
1215
let thing = action.payload.thing
1316
let shown = action.payload.shown

src/core/plugins/layout/selectors.js

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,8 @@ const state = state => state
55

66
export const current = state => state.get("layout")
77

8+
export const currentFilter = state => state.get("filter")
9+
810
export const isShown = (state, thing, def) => {
911
thing = normalizeArray(thing)
1012
return Boolean(state.getIn(["shown", ...thing], def))

src/plugins/topbar/topbar.jsx

Lines changed: 16 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,11 @@ import Logo from "./logo_small.png"
66

77
export default class Topbar extends React.Component {
88

9+
static propTypes = {
10+
layoutSelectors: PropTypes.object.isRequired,
11+
layoutActions: PropTypes.object.isRequired
12+
}
13+
914
constructor(props, context) {
1015
super(props, context)
1116
this.state = { url: props.specSelectors.url(), selectedIndex: 0 }
@@ -80,13 +85,19 @@ export default class Topbar extends React.Component {
8085
}
8186
}
8287

88+
onFilterChange =(e) => {
89+
let {target: {value}} = e
90+
this.props.layoutActions.updateFilter(value)
91+
}
92+
8393
render() {
84-
let { getComponent, specSelectors, getConfigs } = this.props
94+
let { getComponent, specSelectors, getConfigs, layoutSelectors } = this.props
8595
const Button = getComponent("Button")
8696
const Link = getComponent("Link")
8797

8898
let isLoading = specSelectors.loadingStatus() === "loading"
8999
let isFailed = specSelectors.loadingStatus() === "failed"
100+
let filter = layoutSelectors.currentFilter()
90101

91102
let inputStyle = {}
92103
if(isFailed) inputStyle.color = "red"
@@ -124,6 +135,10 @@ export default class Topbar extends React.Component {
124135
<img height="30" width="30" src={ Logo } alt="Swagger UX"/>
125136
<span>swagger</span>
126137
</Link>
138+
{
139+
filter === null || filter === false ? null :
140+
<input className="operation-filter-input" placeholder="filter..." type="text" onChange={this.onFilterChange} value={filter === true ? "" : filter} disabled={isLoading} style={inputStyle} />
141+
}
127142
<form className="download-url-wrapper" onSubmit={formOnSubmit}>
128143
{control}
129144
</form>

src/standalone/layout.jsx

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,7 @@ export default class StandaloneLayout extends React.Component {
2929
return (
3030

3131
<Container className='swagger-ui'>
32-
{ Topbar ? <Topbar/> : null }
32+
{ Topbar ? <Topbar /> : null }
3333
{ loadingStatus === "loading" &&
3434
<div className="info">
3535
<h4 className="title">Loading...</h4>
@@ -45,7 +45,7 @@ export default class StandaloneLayout extends React.Component {
4545
<h4 className="title">Failed to load config.</h4>
4646
</div>
4747
}
48-
{ !loadingStatus || loadingStatus === "success" && <BaseLayout/> }
48+
{ !loadingStatus || loadingStatus === "success" && <BaseLayout /> }
4949
<Row>
5050
<Col>
5151
<OnlineValidatorBadge />

src/style/_layout.scss

Lines changed: 28 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -45,6 +45,7 @@ body
4545
.opblock-tag
4646
{
4747
display: flex;
48+
align-items: center;
4849

4950
padding: 10px 20px 10px 10px;
5051

@@ -53,8 +54,6 @@ body
5354

5455
border-bottom: 1px solid rgba(#3b4151, .3);
5556

56-
align-items: center;
57-
5857
&:hover
5958
{
6059
background: rgba(#000,.02);
@@ -106,9 +105,10 @@ body
106105
font-size: 14px;
107106
font-weight: normal;
108107

108+
flex: 1;
109+
109110
padding: 0 10px;
110111

111-
flex: 1;
112112
@include text_body();
113113
}
114114
}
@@ -134,6 +134,8 @@ body
134134
transition: all .5s;
135135
}
136136

137+
138+
137139
.opblock
138140
{
139141
margin: 0 0 15px 0;
@@ -154,24 +156,23 @@ body
154156
.opblock-section-header
155157
{
156158
display: flex;
159+
align-items: center;
157160

158161
padding: 8px 20px;
159162

160163
background: rgba(#fff,.8);
161164
box-shadow: 0 1px 2px rgba(#000,.1);
162165

163-
align-items: center;
164-
165166
label
166167
{
167168
font-size: 12px;
168169
font-weight: bold;
169170

170171
display: flex;
172+
align-items: center;
171173

172174
margin: 0;
173175

174-
align-items: center;
175176
@include text_headline();
176177

177178
span
@@ -184,9 +185,10 @@ body
184185
{
185186
font-size: 14px;
186187

188+
flex: 1;
189+
187190
margin: 0;
188191

189-
flex: 1;
190192
@include text_headline();
191193
}
192194
}
@@ -215,11 +217,11 @@ body
215217
font-size: 16px;
216218

217219
display: flex;
220+
align-items: center;
218221

219222
padding: 0 10px;
220223

221224
@include text_code();
222-
align-items: center;
223225

224226
.view-line-link
225227
{
@@ -258,18 +260,18 @@ body
258260
font-size: 13px;
259261

260262
flex: 1;
263+
261264
@include text_body();
262265
}
263266

264267
.opblock-summary
265268
{
266269
display: flex;
270+
align-items: center;
267271

268272
padding: 5px;
269273

270274
cursor: pointer;
271-
272-
align-items: center;
273275
}
274276

275277
&.opblock-post
@@ -316,12 +318,12 @@ body
316318

317319
.opblock-schemes
318320
{
319-
padding: 8px 20px;
321+
padding: 8px 20px;
320322

321-
.schemes-title
322-
{
323-
padding: 0 10px 0 0;
324-
}
323+
.schemes-title
324+
{
325+
padding: 0 10px 0 0;
326+
}
325327
}
326328
}
327329

@@ -498,13 +500,11 @@ body
498500
margin: 0;
499501
padding: 10px;
500502

501-
503+
white-space: pre-wrap;
502504
word-wrap: break-word;
503505
word-break: break-all;
504506
word-break: break-word;
505507
hyphens: auto;
506-
white-space: pre-wrap;
507-
508508

509509
border-radius: 4px;
510510
background: #41444e;
@@ -533,10 +533,9 @@ body
533533
.schemes
534534
{
535535
display: flex;
536-
537536
align-items: center;
538537

539-
> label
538+
> label
540539
{
541540
font-size: 12px;
542541
font-weight: bold;
@@ -624,3 +623,12 @@ body
624623
opacity: 0;
625624
}
626625
}
626+
627+
628+
section
629+
{
630+
h3
631+
{
632+
@include text_headline();
633+
}
634+
}

src/style/_topbar.scss

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,12 @@
2929
padding: 0 10px;
3030
}
3131
}
32+
.operation-filter-input
33+
{
34+
border: 2px solid #547f00;
35+
border-right: none;
36+
border-radius: 4px 0 0 4px;
37+
}
3238

3339
.download-url-wrapper
3440
{
@@ -43,7 +49,7 @@
4349
margin: 0;
4450

4551
border: 2px solid #547f00;
46-
border-radius: 4px 0 0 4px;
52+
border-radius: 0 0 0 0;
4753
outline: none;
4854
}
4955

0 commit comments

Comments
 (0)