Skip to content

Commit

Permalink
feat: add Icons plugin
Browse files Browse the repository at this point in the history
* Change existing icons to React wrapper components

* Add `icons` plugin to expose Icon components to plugin system

* Create components that re-export Lock and Unlock components so they can be changed separately in Authorise top button and Authorise operation summary button

* Add new Lock and Unlock icons to `auth` plugin

---------

Co-authored-by: Vladimír Gorej <vladimir.gorej@smartbear.com>
  • Loading branch information
damian-polewski-sb and char0n committed Jul 20, 2023
1 parent be9f944 commit f3ea2a2
Show file tree
Hide file tree
Showing 24 changed files with 386 additions and 36 deletions.
5 changes: 2 additions & 3 deletions src/core/components/auth/authorization-popup.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ export default class AuthorizationPopup extends React.Component {
let { authSelectors, authActions, getComponent, errSelectors, specSelectors, fn: { AST = {} } } = this.props
let definitions = authSelectors.shownDefinitions()
const Auths = getComponent("auths")
const CloseIcon = getComponent("CloseIcon")

return (
<div className="dialog-ux">
Expand All @@ -22,9 +23,7 @@ export default class AuthorizationPopup extends React.Component {
<div className="modal-ux-header">
<h3>Available authorizations</h3>
<button type="button" className="close-modal" onClick={ this.close }>
<svg width="20" height="20">
<use href="#close" xlinkHref="#close" />
</svg>
<CloseIcon />
</button>
</div>
<div className="modal-ux-content">
Expand Down
6 changes: 3 additions & 3 deletions src/core/components/auth/authorize-btn.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -14,14 +14,14 @@ export default class AuthorizeBtn extends React.Component {

//must be moved out of button component
const AuthorizationPopup = getComponent("authorizationPopup", true)
const LockAuthIcon = getComponent("LockAuthIcon", true)
const UnlockAuthIcon = getComponent("UnlockAuthIcon", true)

return (
<div className="auth-wrapper">
<button className={isAuthorized ? "btn authorize locked" : "btn authorize unlocked"} onClick={onClick}>
<span>Authorize</span>
<svg width="20" height="20">
<use href={ isAuthorized ? "#locked" : "#unlocked" } xlinkHref={ isAuthorized ? "#locked" : "#unlocked" } />
</svg>
{isAuthorized ? <LockAuthIcon /> : <UnlockAuthIcon />}
</button>
{ showPopup && <AuthorizationPopup /> }
</div>
Expand Down
14 changes: 8 additions & 6 deletions src/core/components/auth/authorize-operation-btn.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,8 @@ import PropTypes from "prop-types"
export default class AuthorizeOperationBtn extends React.Component {
static propTypes = {
isAuthorized: PropTypes.bool.isRequired,
onClick: PropTypes.func
onClick: PropTypes.func,
getComponent: PropTypes.func.isRequired
}

onClick =(e) => {
Expand All @@ -17,15 +18,16 @@ export default class AuthorizeOperationBtn extends React.Component {
}

render() {
let { isAuthorized } = this.props
let { isAuthorized, getComponent } = this.props

const LockAuthOperationIcon = getComponent("LockAuthOperationIcon", true)
const UnlockAuthOperationIcon = getComponent("UnlockAuthOperationIcon", true)

return (
<button className={isAuthorized ? "authorization__btn locked" : "authorization__btn unlocked"}
<button className="authorization__btn"
aria-label={isAuthorized ? "authorization button locked" : "authorization button unlocked"}
onClick={this.onClick}>
<svg width="20" height="20">
<use href={ isAuthorized ? "#locked" : "#unlocked" } xlinkHref={ isAuthorized ? "#locked" : "#unlocked" } />
</svg>
{isAuthorized ? <LockAuthOperationIcon className="locked" /> : <UnlockAuthOperationIcon className="unlocked"/>}
</button>

)
Expand Down
11 changes: 7 additions & 4 deletions src/core/components/copy-to-clipboard-btn.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,24 +3,27 @@ import { CopyToClipboard } from "react-copy-to-clipboard"
import PropTypes from "prop-types"

/**
* @param {{ textToCopy: string }} props
* @param {{ getComponent: func, textToCopy: string }} props
* @returns {JSX.Element}
* @constructor
*/
export default class CopyToClipboardBtn extends React.Component {
render() {
let { getComponent } = this.props

const CopyIcon = getComponent("CopyIcon")

return (
<div className="view-line-link copy-to-clipboard" title="Copy to clipboard">
<CopyToClipboard text={this.props.textToCopy}>
<svg width="15" height="16">
<use href="#copy" xlinkHref="#copy" />
</svg>
<CopyIcon />
</CopyToClipboard>
</div>
)
}

static propTypes = {
getComponent: PropTypes.func.isRequired,
textToCopy: PropTypes.string.isRequired,
}
}
6 changes: 3 additions & 3 deletions src/core/components/models.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,8 @@ export default class Models extends Component {
const Collapse = getComponent("Collapse")
const ModelCollapse = getComponent("ModelCollapse")
const JumpToPath = getComponent("JumpToPath", true)
const ArrowUpIcon = getComponent("ArrowUpIcon")
const ArrowDownIcon = getComponent("ArrowDownIcon")

return <section className={ showModels ? "models is-open" : "models"} ref={this.onLoadModels}>
<h4>
Expand All @@ -65,9 +67,7 @@ export default class Models extends Component {
onClick={() => layoutActions.show(specPathBase, !showModels)}
>
<span>{isOAS3 ? "Schemas" : "Models"}</span>
<svg width="20" height="20" aria-hidden="true" focusable="false">
<use xlinkHref={showModels ? "#large-arrow-up" : "#large-arrow-down"} />
</svg>
{showModels ? <ArrowUpIcon /> : <ArrowDownIcon />}
</button>
</h4>
<Collapse isOpened={showModels}>
Expand Down
13 changes: 8 additions & 5 deletions src/core/components/operation-summary.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -54,11 +54,13 @@ export default class OperationSummary extends PureComponent {

let security = operationProps.get("security")

const AuthorizeOperationBtn = getComponent("authorizeOperationBtn")
const AuthorizeOperationBtn = getComponent("authorizeOperationBtn", true)
const OperationSummaryMethod = getComponent("OperationSummaryMethod")
const OperationSummaryPath = getComponent("OperationSummaryPath")
const JumpToPath = getComponent("JumpToPath", true)
const CopyToClipboardBtn = getComponent("CopyToClipboardBtn", true)
const ArrowUpIcon = getComponent("ArrowUpIcon")
const ArrowDownIcon = getComponent("ArrowDownIcon")

const hasSecurity = security && !!security.count()
const securityIsOptional = hasSecurity && security.size === 1 && security.first().isEmpty()
Expand All @@ -82,9 +84,8 @@ export default class OperationSummary extends PureComponent {

{displayOperationId && (originalOperationId || operationId) ? <span className="opblock-summary-operation-id">{originalOperationId || operationId}</span> : null}

<svg className="arrow" width="20" height="20" aria-hidden="true" focusable="false">
<use href={isShown ? "#large-arrow-up" : "#large-arrow-down"} xlinkHref={isShown ? "#large-arrow-up" : "#large-arrow-down"} />
</svg>
{isShown ? <ArrowUpIcon className="arrow" /> : <ArrowDownIcon className="arrow" />}

</button>

{
Expand All @@ -97,7 +98,9 @@ export default class OperationSummary extends PureComponent {
}}
/>
}
<CopyToClipboardBtn textToCopy={`${specPath.get(1)}`} />
<CopyToClipboardBtn
textToCopy={`${specPath.get(1)}`}
/>
<JumpToPath path={specPath} />{/* TODO: use wrapComponents here, swagger-ui doesn't care about jumpToPath */}
</div>
)
Expand Down
6 changes: 3 additions & 3 deletions src/core/components/operation-tag.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,8 @@ export default class OperationTag extends React.Component {
const Markdown = getComponent("Markdown", true)
const DeepLink = getComponent("DeepLink")
const Link = getComponent("Link")
const ArrowUpIcon = getComponent("ArrowUpIcon")
const ArrowDownIcon = getComponent("ArrowDownIcon")

let tagDescription = tagObj.getIn(["tagDetails", "description"], null)
let tagExternalDocsDescription = tagObj.getIn(["tagDetails", "externalDocs", "description"])
Expand Down Expand Up @@ -107,9 +109,7 @@ export default class OperationTag extends React.Component {
title={showTag ? "Collapse operation" : "Expand operation"}
onClick={() => layoutActions.show(isShownKey, !showTag)}>

<svg className="arrow" width="20" height="20" aria-hidden="true" focusable="false">
<use href={showTag ? "#large-arrow-up" : "#large-arrow-down"} xlinkHref={showTag ? "#large-arrow-up" : "#large-arrow-down"} />
</svg>
{showTag ? <ArrowUpIcon className="arrow" /> : <ArrowDownIcon className="arrow" />}
</button>
</h3>

Expand Down
17 changes: 17 additions & 0 deletions src/core/plugins/auth/components/lock-auth-operation.jsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
/**
* @prettier
*/
import React from "react"
import PropTypes from "prop-types"

const LockAuthOperation = ({ getComponent, ...props }) => {
const LockIcon = getComponent("LockIcon")

return <LockIcon {...props} />
}

LockAuthOperation.propTypes = {
getComponent: PropTypes.func.isRequired
}

export default LockAuthOperation
17 changes: 17 additions & 0 deletions src/core/plugins/auth/components/lock-auth.jsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
/**
* @prettier
*/
import React from "react"
import PropTypes from "prop-types"

const LockAuth = ({ getComponent, ...props }) => {
const LockIcon = getComponent("LockIcon")

return <LockIcon {...props} />
}

LockAuth.propTypes = {
getComponent: PropTypes.func.isRequired
}

export default LockAuth
17 changes: 17 additions & 0 deletions src/core/plugins/auth/components/unlock-auth-operation.jsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
/**
* @prettier
*/
import React from "react"
import PropTypes from "prop-types"

const UnlockAuthOperation = ({ getComponent, ...props }) => {
const UnlockIcon = getComponent("UnlockIcon")

return <UnlockIcon {...props} />
}

UnlockAuthOperation.propTypes = {
getComponent: PropTypes.func.isRequired
}

export default UnlockAuthOperation
17 changes: 17 additions & 0 deletions src/core/plugins/auth/components/unlock-auth.jsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
/**
* @prettier
*/
import React from "react"
import PropTypes from "prop-types"

const UnlockAuth = ({ getComponent, ...props }) => {
const UnlockIcon = getComponent("UnlockIcon")

return <UnlockIcon {...props} />
}

UnlockAuth.propTypes = {
getComponent: PropTypes.func.isRequired
}

export default UnlockAuth
11 changes: 11 additions & 0 deletions src/core/plugins/auth/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,11 @@ import { execute as wrappedExecuteAction } from "./spec-extensions/wrap-actions"
import { loaded as wrappedLoadedAction } from "./configs-extensions/wrap-actions"
import { authorize as wrappedAuthorizeAction, logout as wrappedLogoutAction } from "./wrap-actions"

import LockAuthIcon from "./components/lock-auth"
import UnlockAuthIcon from "./components/unlock-auth"
import LockAuthOperationIcon from "./components/lock-auth-operation"
import UnlockAuthOperationIcon from "./components/unlock-auth-operation"

export default function() {
return {
afterLoad(system) {
Expand All @@ -13,6 +18,12 @@ export default function() {
this.rootInjects.preauthorizeApiKey = preauthorizeApiKey.bind(null, system)
this.rootInjects.preauthorizeBasic = preauthorizeBasic.bind(null, system)
},
components: {
LockAuthIcon: LockAuthIcon,
UnlockAuthIcon: UnlockAuthIcon,
LockAuthOperationIcon: LockAuthOperationIcon,
UnlockAuthOperationIcon: UnlockAuthOperationIcon,
},
statePlugins: {
auth: {
reducers,
Expand Down
33 changes: 33 additions & 0 deletions src/core/plugins/icons/components/arrow-down.jsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
/**
* @prettier
*/
import React from "react"
import PropTypes from "prop-types"

const ArrowDown = ({ className, width, height }) => (
<svg
xmlns="http://www.w3.org/2000/svg"
viewBox="0 0 20 20"
className={className}
width={width}
height={height}
aria-hidden="true"
focusable="false"
>
<path d="M17.418 6.109c.272-.268.709-.268.979 0s.271.701 0 .969l-7.908 7.83c-.27.268-.707.268-.979 0l-7.908-7.83c-.27-.268-.27-.701 0-.969.271-.268.709-.268.979 0L10 13.25l7.418-7.141z"/>
</svg>
)

ArrowDown.propTypes = {
className: PropTypes.string,
width: PropTypes.string,
height: PropTypes.string,
}

ArrowDown.defaultProps = {
className: null,
width: 20,
height: 20,
}

export default ArrowDown
33 changes: 33 additions & 0 deletions src/core/plugins/icons/components/arrow-up.jsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
/**
* @prettier
*/
import React from "react"
import PropTypes from "prop-types"

const ArrowUp = ({ className, width, height }) => (
<svg
xmlns="http://www.w3.org/2000/svg"
viewBox="0 0 20 20"
className={className}
width={width}
height={height}
aria-hidden="true"
focusable="false"
>
<path d="M 17.418 14.908 C 17.69 15.176 18.127 15.176 18.397 14.908 C 18.667 14.64 18.668 14.207 18.397 13.939 L 10.489 6.109 C 10.219 5.841 9.782 5.841 9.51 6.109 L 1.602 13.939 C 1.332 14.207 1.332 14.64 1.602 14.908 C 1.873 15.176 2.311 15.176 2.581 14.908 L 10 7.767 L 17.418 14.908 Z"/>
</svg>
)

ArrowUp.propTypes = {
className: PropTypes.string,
width: PropTypes.string,
height: PropTypes.string,
}

ArrowUp.defaultProps = {
className: null,
width: 20,
height: 20,
}

export default ArrowUp
33 changes: 33 additions & 0 deletions src/core/plugins/icons/components/arrow.jsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
/**
* @prettier
*/
import React from "react"
import PropTypes from "prop-types"

const Arrow = ({ className, width, height }) => (
<svg
xmlns="http://www.w3.org/2000/svg"
viewBox="0 0 20 20"
className={className}
width={width}
height={height}
aria-hidden="true"
focusable="false"
>
<path d="M13.25 10L6.109 2.58c-.268-.27-.268-.707 0-.979.268-.27.701-.27.969 0l7.83 7.908c.268.271.268.709 0 .979l-7.83 7.908c-.268.271-.701.27-.969 0-.268-.269-.268-.707 0-.979L13.25 10z"/>
</svg>
)

Arrow.propTypes = {
className: PropTypes.string,
width: PropTypes.string,
height: PropTypes.string,
}

Arrow.defaultProps = {
className: null,
width: 20,
height: 20,
}

export default Arrow
Loading

0 comments on commit f3ea2a2

Please sign in to comment.