Skip to content

Commit

Permalink
feat(aboutModal): issues/29 version display
Browse files Browse the repository at this point in the history
* add the ability to copy and paste version information
* add UI version information
  • Loading branch information
cdcabrera committed Mar 26, 2019
1 parent feaa5ef commit 33cba88
Show file tree
Hide file tree
Showing 12 changed files with 196 additions and 32 deletions.
2 changes: 2 additions & 0 deletions .env.development
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
REACT_APP_ENV=development

REACT_APP_UI_VERSION=$npm_package_version

REACT_APP_AUTH_TOKEN=csrfspoof
REACT_APP_AUTH_HEADER=X-CSRFToken
REACT_APP_AJAX_TIMEOUT=60000
Expand Down
2 changes: 2 additions & 0 deletions .env.production
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
REACT_APP_ENV=production

REACT_APP_UI_VERSION=${UI_VERSION}

REACT_APP_AUTH_TOKEN=csrftoken
REACT_APP_AUTH_HEADER=X-CSRFToken
REACT_APP_AJAX_TIMEOUT=60000
Expand Down
1 change: 1 addition & 0 deletions .env.staging
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
REACT_APP_ENV=staging

BROWSER=none
REACT_APP_UI_VERSION=$npm_package_version

REACT_APP_AUTH_TOKEN=csrftoken
REACT_APP_AUTH_HEADER=X-CSRFToken
Expand Down
2 changes: 2 additions & 0 deletions .env.test
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
REACT_APP_ENV=test

REACT_APP_UI_VERSION=0.0.0.0000000

REACT_APP_AUTH_TOKEN=csrfspoof
REACT_APP_AUTH_HEADER=X-CSRFToken
REACT_APP_AJAX_TIMEOUT=60000
Expand Down
3 changes: 2 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -53,12 +53,13 @@
"api:review": "sh ./scripts/api.sh -p 9443 -t review",
"api:stage": "sh ./scripts/api.sh -p 5001 -t stage",
"api:update": "npm run api:build; sh ./scripts/api.sh -t update",
"build": "run-s -l build:clean build:css build:js build:finish test:integration",
"build": "run-s -l build:clean build:version build:css build:js build:finish test:integration",
"build:clean": "rm -rf -- \"$(pwd)\"/build",
"build:css": "node-sass-chokidar --include-path ./src --include-path ./node_modules src/styles/index.scss -o src/styles/.css",
"build:finish": "sh ./scripts/build.sh",
"build:js": "react-scripts build",
"build:rh": "sh -ac 'export REACT_APP_RH_BRAND=true && run-s -l build:clean build:css build:js build:finish'",
"build:version": "sh ./scripts/version.sh",
"release": "> ./CHANGELOG.md && standard-version",
"start": "open http://localhost:5050/docs/api; run-p -l api:dev watch:css start:js",
"start:js": "react-scripts start",
Expand Down
9 changes: 9 additions & 0 deletions scripts/version.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
#!/usr/bin/env bash
#
#
# main()
#
{
echo "Compiling version information..."
echo UI_VERSION="$(node -p 'require(`./package.json`).version').$(git rev-parse --short HEAD)" > ./.env.production.local
}
2 changes: 2 additions & 0 deletions src/common/__tests__/__snapshots__/helpers.test.js.snap
Original file line number Diff line number Diff line change
Expand Up @@ -67,6 +67,8 @@ Object {
"REJECTED_ACTION": [Function],
"RH_BRAND": false,
"TEST_MODE": true,
"UI_VERSION": "0.0.0.0000000",
"copyClipboard": [Function],
"createViewQueryObject": [Function],
"devModeNormalizeCount": [Function],
"downloadData": [Function],
Expand Down
36 changes: 36 additions & 0 deletions src/common/helpers.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,38 @@ import moment from 'moment';
import _get from 'lodash/get';
import _set from 'lodash/set';

const copyClipboard = text => {
let successful;

try {
window.getSelection().removeAllRanges();

const newTextarea = document.createElement('pre');
newTextarea.appendChild(document.createTextNode(text));

newTextarea.style.position = 'absolute';
newTextarea.style.top = '-9999px';
newTextarea.style.left = '-9999px';

const range = document.createRange();
window.document.body.appendChild(newTextarea);

range.selectNode(newTextarea);

window.getSelection().addRange(range);

successful = window.document.execCommand('copy');

window.document.body.removeChild(newTextarea);
window.getSelection().removeAllRanges();
} catch (e) {
successful = false;
console.warn('Copy to clipboard failed.', e.message);
}

return successful;
};

const devModeNormalizeCount = (count, modulus = 100) => Math.abs(count) % modulus;

const downloadData = (data = '', fileName = 'download.txt', fileType = 'text/plain') =>
Expand Down Expand Up @@ -292,13 +324,16 @@ const TEST_MODE = process.env.REACT_APP_ENV === 'test';

const RH_BRAND = process.env.REACT_APP_RH_BRAND === 'true';

const UI_VERSION = process.env.REACT_APP_UI_VERSION;

const FULFILLED_ACTION = base => `${base}_FULFILLED`;

const PENDING_ACTION = base => `${base}_PENDING`;

const REJECTED_ACTION = base => `${base}_REJECTED`;

const helpers = {
copyClipboard,
devModeNormalizeCount,
downloadData,
generateId,
Expand All @@ -319,6 +354,7 @@ const helpers = {
DEV_MODE,
TEST_MODE,
RH_BRAND,
UI_VERSION,
FULFILLED_ACTION,
PENDING_ACTION,
REJECTED_ACTION
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,26 +15,52 @@ exports[`AboutModal Component should contain brand: brand 1`] = `
show={true}
trademarkText="Copyright (c) 2019 Red Hat Inc."
>
<AboutModalVersions
className=""
<div
aria-label="Application information copied"
aria-live="polite"
tabIndex={-1}
>
<AboutModalVersionItem
className=""
label="Version"
versionText="unknown (Build: unknown)"
/>
</AboutModalVersions>
<AboutModalVersions
className="quipucords-about-modal-list"
>
<AboutModalVersionItem
className=""
label="UI Version"
versionText="0.0.0.0000000"
/>
</AboutModalVersions>
</div>
<div
className="quipucords-about-modal-copy-footer"
>
<Button
active={false}
block={false}
bsClass="btn"
bsStyle="default"
className="quipucords-about-modal-copy-button"
disabled={false}
onClick={[Function]}
title="Copy application information"
>
<Icon
name="paste"
type="fa"
/>
</Button>
</div>
</AboutModal>
`;

exports[`AboutModal Component should render a connected component with default props: connected 1`] = `
<AboutModal
apiVersion={1}
brand={false}
build={null}
getStatus={[Function]}
getUser={[Function]}
resetTimer={3000}
serverVersion={null}
show={true}
uiVersion="0.0.0.0000000"
username="lorem"
/>
`;
Expand All @@ -43,10 +69,12 @@ exports[`AboutModal Component should render a non-connected component: hidden mo
<AboutModal
apiVersion={1}
brand={false}
build={null}
getStatus={[Function]}
getUser={[Function]}
resetTimer={3000}
serverVersion={null}
show={false}
uiVersion="0.0.0.0000000"
username="admin"
>
<AboutModal
Expand Down
94 changes: 74 additions & 20 deletions src/components/aboutModal/aboutModal.js
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import React from 'react';
import PropTypes from 'prop-types';
import { detect } from 'detect-browser';
import { AboutModal as PfAboutModal } from 'patternfly-react';
import { AboutModal as PfAboutModal, Button, Icon } from 'patternfly-react';
import { connect, reduxActions, reduxTypes, store } from '../../redux';
import helpers from '../../common/helpers';
import logoImg from '../../styles/images/logo.svg';
Expand All @@ -10,26 +10,66 @@ import logoImgBrand from '../../styles/images/logo-brand.svg';
import titleImgBrand from '../../styles/images/title-brand.svg';

class AboutModal extends React.Component {
selectElement = React.createRef();

state = {
copied: false,
timer: null
};

componentDidUpdate() {
const { apiVersion, getStatus, show } = this.props;
const { serverVersion, getStatus, show } = this.props;

if (show && apiVersion === null) {
if (show && serverVersion === null) {
getStatus();
}
}

onCopy = () => {
const { timer } = this.state;
const selectElement = this.selectElement.current;
const success = helpers.copyClipboard(selectElement.innerText);

selectElement.focus();
clearTimeout(timer);

this.setState(
{
copied: success
},
() => this.resetStateTimer()
);
};

onClose = () => {
store.dispatch({
type: reduxTypes.aboutModal.ABOUT_MODAL_HIDE
});
};

resetStateTimer() {
const { resetTimer } = this.props;
const selectElement = this.selectElement.current;

const timer = setTimeout(() => {
selectElement.blur();

this.setState({
copied: false
});
}, resetTimer);

this.setState({ timer });
}

render() {
const { apiVersion, brand, build, show, username } = this.props;
const versionText = `${apiVersion || 'unknown'} (Build: ${build || 'unknown'})`;
const { copied } = this.state;
const { brand, show, serverVersion, uiVersion, username } = this.props;
const browser = detect();

const props = {
show,
onHide: this.onClose,
logo: logoImg,
productTitle: <img src={titleImg} alt="Entitlements Reporting" />,
altLogo: 'ER'
Expand All @@ -43,36 +83,51 @@ class AboutModal extends React.Component {
}

return (
<PfAboutModal show={show} onHide={this.onClose} {...props}>
<PfAboutModal.Versions>
<PfAboutModal.VersionItem label="Version" versionText={versionText} />
{username && <PfAboutModal.VersionItem label="Username" versionText={username || ''} />}
{browser && (
<PfAboutModal.VersionItem label="Browser Version" versionText={`${browser.name} ${browser.version}`} />
)}
{browser && <PfAboutModal.VersionItem label="Browser OS" versionText={browser.os || ''} />}
</PfAboutModal.Versions>
<PfAboutModal {...props}>
<div ref={this.selectElement} tabIndex={-1} aria-label="Application information copied" aria-live="polite">
<PfAboutModal.Versions className="quipucords-about-modal-list">
{username && <PfAboutModal.VersionItem label="Username" versionText={username || ''} />}
{browser && (
<PfAboutModal.VersionItem label="Browser Version" versionText={`${browser.name} ${browser.version}`} />
)}
{browser && <PfAboutModal.VersionItem label="Browser OS" versionText={browser.os || ''} />}
{serverVersion && <PfAboutModal.VersionItem label="Server Version" versionText={serverVersion} />}
{uiVersion && <PfAboutModal.VersionItem label="UI Version" versionText={uiVersion} />}
</PfAboutModal.Versions>
</div>
<div className="quipucords-about-modal-copy-footer">
<Button
onClick={this.onCopy}
title="Copy application information"
className="quipucords-about-modal-copy-button"
>
{copied && <Icon type="fa" name="check" />}
{!copied && <Icon type="fa" name="paste" />}
</Button>
</div>
</PfAboutModal>
);
}
}

AboutModal.propTypes = {
apiVersion: PropTypes.oneOfType([PropTypes.string, PropTypes.number]),
brand: PropTypes.bool,
build: PropTypes.oneOfType([PropTypes.string, PropTypes.number]),
getStatus: PropTypes.func,
getUser: PropTypes.func,
resetTimer: PropTypes.number,
serverVersion: PropTypes.string,
show: PropTypes.bool.isRequired,
uiVersion: PropTypes.string,
username: PropTypes.string
};

AboutModal.defaultProps = {
apiVersion: null,
brand: helpers.RH_BRAND,
build: null,
getStatus: helpers.noop,
getUser: helpers.noop,
resetTimer: 3000,
serverVersion: null,
uiVersion: helpers.UI_VERSION,
username: null
};

Expand All @@ -81,8 +136,7 @@ const mapDispatchToProps = dispatch => ({
});

const mapStateToProps = state => ({
apiVersion: state.status.apiVersion,
build: state.status.build,
serverVersion: state.status.serverVersion,
show: state.aboutModal.show,
username: state.user.session.username
});
Expand Down
26 changes: 26 additions & 0 deletions src/styles/entitlements/_aboutModal.scss
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
.quipucords-about-modal-copy-footer {
margin-top: -28px;
text-align: right;
}

.quipucords-about-modal-copy-button {
box-shadow: 0 0 3px $color-pf-black-300;
}

.quipucords-about-modal-list {
box-shadow: 0 0 2px $color-pf-black-300;
padding: 10px;

&:focus,
&:hover {
box-shadow: 0 0 5px $color-pf-black-100;
}

.list-unstyled li {
display: flex;

strong {
white-space: nowrap;
}
}
}
1 change: 1 addition & 0 deletions src/styles/index.scss
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ $icon-font-path: '~patternfly/dist/fonts/';
@import 'patternfly/dist/sass/patternfly/mixins';

@import 'entitlements/overrides';
@import 'entitlements/aboutModal';
@import 'entitlements/common';
@import 'entitlements/login';
@import 'entitlements/app';
Expand Down

0 comments on commit 33cba88

Please sign in to comment.