From 69bc0ec20f5976a6b80cf8f29686b932fee74f38 Mon Sep 17 00:00:00 2001 From: Noah Koontz Date: Tue, 20 Oct 2020 13:16:25 -0700 Subject: [PATCH] feat: add settings modal with NerdVault integration --- .extended-webpackrc.js | 15 + nerdlets/maintainer-dashboard/index.js | 175 +-------- nerdlets/maintainer-dashboard/issueLabel.js | 66 ++++ nerdlets/maintainer-dashboard/settings.js | 368 +++++++++++++++++++ nerdlets/maintainer-dashboard/storageUtil.js | 49 +++ nerdlets/maintainer-dashboard/styles.scss | 7 +- package-lock.json | 166 ++++++++- package.json | 5 +- 8 files changed, 673 insertions(+), 178 deletions(-) create mode 100644 .extended-webpackrc.js create mode 100644 nerdlets/maintainer-dashboard/issueLabel.js create mode 100644 nerdlets/maintainer-dashboard/settings.js create mode 100644 nerdlets/maintainer-dashboard/storageUtil.js diff --git a/.extended-webpackrc.js b/.extended-webpackrc.js new file mode 100644 index 0000000..902e04b --- /dev/null +++ b/.extended-webpackrc.js @@ -0,0 +1,15 @@ +module.exports = { + module: { + rules: [ + { + test: /\.(png|jpe?g|gif|svg|ttf|eot|woff2)$/, + use: [ + { + loader: 'file-loader', + options: {}, + }, + ], + }, + ], + }, +}; \ No newline at end of file diff --git a/nerdlets/maintainer-dashboard/index.js b/nerdlets/maintainer-dashboard/index.js index efe4271..dd3b379 100644 --- a/nerdlets/maintainer-dashboard/index.js +++ b/nerdlets/maintainer-dashboard/index.js @@ -34,10 +34,14 @@ import { Icon, Modal, TextField, - Link + Link, + Select, + SelectItem, + UserStorageMutation, } from 'nr1'; -import { GitHub } from 'react-feather'; import { getGithubData } from './githubData'; +import { IssueLabel, KNOWN_LABEL_COLORS } from './issueLabel'; +import SettingsUI from './settings'; import PullRequestLogo from './img/git-pull-request-16.svg'; import IssueLogo from './img/issue-opened-16.svg'; import NewRelicUsers from './data/userdata-sample.json'; @@ -48,21 +52,8 @@ const STALE_TIME = 1000 * 60 * 60 * 24 * 14; // 2 weeks in ms const RELICS = Object.values(NewRelicUsers) .filter(u => u.user_type === 'relic' || u.user_type === 'contractor') - .map(u => u.login); - -const KNOWN_LABEL_COLORS = new Map([ - ['bug', 'd73a4a'], - ['documentation', '0075ca'], - ['duplicate', 'cfd3d7'], - ['enhancement', 'a2eeef'], - ['good first issue', '7057ff'], - ['help wanted', '008672'], - ['invalid', 'e4e669'], - ['question', 'd876e3'], - ['wontfix', 'ffffff'], - ['dependencies', '0366d6'], - ['repolinter', 'fbca04'], -]); + .map(u => u.login) + .sort(); const REPOS = [ 'newrelic/go-agent', @@ -117,15 +108,6 @@ const REPOS = [ 'newrelic/k8s-webhook-cert-manager' */ ]; -// stolen from https://stackoverflow.com/questions/3942878/how-to-decide-font-color-in-white-or-black-depending-on-background-color -function pickTextColorBasedOnBgColor(bgColor, lightColor, darkColor) { - const color = bgColor.charAt(0) === '#' ? bgColor.substring(1, 7) : bgColor; - const r = parseInt(color.substring(0, 2), 16); // hexToR - const g = parseInt(color.substring(2, 4), 16); // hexToG - const b = parseInt(color.substring(4, 6), 16); // hexToB - return r * 0.299 + g * 0.587 + b * 0.114 > 186 ? darkColor : lightColor; -} - class IssueTable extends React.PureComponent { constructor(props) { super(props); @@ -226,44 +208,7 @@ class IssueTable extends React.PureComponent { }), filterValue: cell => cell.map(({ name }) => name), formatter: cell => - cell.map(({ name, color }) => { - const bgColor = KNOWN_LABEL_COLORS.has(name) - ? KNOWN_LABEL_COLORS.get(name) - : color; - return ( - - - {name} - - - ); - }), + cell.map(({ name, color }) => ), }, { dataField: 'title', @@ -343,104 +288,10 @@ export default class MaintainerDashboard extends React.Component { return (
{!this.state.newSearchItems || !this.state.staleSearchItems ? ( diff --git a/nerdlets/maintainer-dashboard/issueLabel.js b/nerdlets/maintainer-dashboard/issueLabel.js new file mode 100644 index 0000000..28a78b7 --- /dev/null +++ b/nerdlets/maintainer-dashboard/issueLabel.js @@ -0,0 +1,66 @@ +import React from 'react'; +import { BlockText } from 'nr1'; + +export const KNOWN_LABEL_COLORS = new Map([ + ['bug', 'd73a4a'], + ['documentation', '0075ca'], + ['duplicate', 'cfd3d7'], + ['enhancement', 'a2eeef'], + ['good first issue', '7057ff'], + ['help wanted', '008672'], + ['invalid', 'e4e669'], + ['question', 'd876e3'], + ['wontfix', 'ffffff'], + ['dependencies', '0366d6'], + ['repolinter', 'fbca04'] +]); + +// stolen from https://stackoverflow.com/questions/3942878/how-to-decide-font-color-in-white-or-black-depending-on-background-color +function pickTextColorBasedOnBgColor(bgColor, lightColor, darkColor) { + const color = bgColor.charAt(0) === '#' ? bgColor.substring(1, 7) : bgColor; + const r = parseInt(color.substring(0, 2), 16); // hexToR + const g = parseInt(color.substring(2, 4), 16); // hexToG + const b = parseInt(color.substring(4, 6), 16); // hexToB + return r * 0.299 + g * 0.587 + b * 0.114 > 186 ? darkColor : lightColor; +} + +export class IssueLabel extends React.PureComponent { + constructor(props) { + super(props); + } + + render() { + const bgColor = KNOWN_LABEL_COLORS.has(this.props.name) + ? KNOWN_LABEL_COLORS.get(this.props.name) + : this.props.color; + return ( + + + {this.props.name} + + + ); + } +} diff --git a/nerdlets/maintainer-dashboard/settings.js b/nerdlets/maintainer-dashboard/settings.js new file mode 100644 index 0000000..928c758 --- /dev/null +++ b/nerdlets/maintainer-dashboard/settings.js @@ -0,0 +1,368 @@ +import gql from 'graphql-tag'; +import React from 'react'; +import { + HeadingText, + Stack, + StackItem, + Button, + BlockText, + Icon, + TextField, + Link, + Select, + SelectItem +} from 'nr1'; +import { Multiselect } from 'react-widgets'; +import { IssueLabel } from './issueLabel'; +import { + writeToken, + writeSettings, + readToken, + readSettings, + removeToken, +} from './storageUtil'; + +const GET_CUR_USER_INFO = gql` + query($repoCursor: String) { + viewer { + login + repositories( + affiliations: [OWNER, COLLABORATOR, ORGANIZATION_MEMBER] + first: 100 + after: $repoCursor + ) { + nodes { + nameWithOwner + } + } + } + } +`; + +export default class SettingsUI extends React.Component { + constructor(props) { + super(props); + this.state = { + repoValue: [], + labelValue: [], + userValue: [], + timeUnit: 60, + timeValue: 0, + token: '', + patStatus: {}, + submitting: false + }; + this.handlePATToken = this.handlePATToken.bind(this); + this.handlePATRemove = this.handlePATRemove.bind(this); + this.handleSubmit = this.handleSubmit.bind(this); + this.curFetchIndex = 0; + } + + async componentDidMount() { + const [prevToken, prevSettings] = await Promise.all([ + readToken(), + readSettings() + ]); + // add saved values back into settings + // TODO: do not write the previous token back to the input form, as it may be unsafe + if (prevSettings) { + const { repos, labels, users, staleTime } = prevSettings; + this.setState({ + repoValue: repos || [], + labelValue: labels || [], + userValue: users || [], + timeValue: staleTime ? staleTime / 60 : 0, + patStatus: prevToken ? { valid: true } : {}, + token: prevToken || '' + }); + } + // re-test the PAT + if (prevToken) await this.updateUserInfo(prevToken); + } + + async updateUserInfo(token) { + // indicate we are currently testing the token + this.setState({ + patStatus: { + testing: true + } + }); + // test the token with a user information query + const curNum = ++this.curFetchIndex; + return this.props.client + .query({ + query: GET_CUR_USER_INFO, + fetchPolicy: 'network-only', + context: { + headers: { + authorization: `Bearer ${token}` + } + } + }) + .then(({ data }) => { + // prevent race conditions + if (curNum === this.curFetchIndex) { + this.setState({ + patStatus: { + valid: true, + userName: data?.viewer?.login, + repoOptions: data?.viewer?.repositories?.nodes?.map?.( + ({ nameWithOwner }) => nameWithOwner + ) + } + }); + } + }) + .catch(e => { + // prevent race condition + if (curNum === this.curFetchIndex) { + if (e?.networkError?.statusCode === 401) + this.setState({ + patStatus: { + valid: false, + message: 'Token returned authorization error' + } + }); + else + this.setState({ + patStatus: { + valid: false, + message: 'Unknown GitHub API error' + } + }); + } + }); + } + + handlePATToken(event) { + const token = event.target.value; + this.setState({ token }); + // error if the token is invalid + if (token.length < 20) { + return this.setState({ + patStatus: { + valid: false, + message: 'Token is less than 20 characters in length' + } + }); + } + // else update the user information using the token + this.updateUserInfo(token); + } + + async handlePATRemove() { + this.setState({ token: '', patStatus: {} }); + await removeToken(); + } + + async handleSubmit() { + // tell the user we are currently submitting + this.setState({ submitting: true }); + // write the token to NerdVault + // write the everything else to UserStorage + await Promise.all([ + writeToken(this.state.token), + writeSettings({ + repos: this.state.repoValue, + users: this.state.userValue, + labels: this.state.labelValue, + staleTime: this.state.timeValue * this.state.timeUnit + }) + ]); + // tell the user we're done + this.setState({ submitting: false }); + // call the callback + this.props.onSubmit(); + } + + getFormError() { + if (!this.state.patStatus.valid) return 'Please enter a valid PAT'; + if (this.state.repoValue.length === 0) + return 'Please select at least one repository'; + else if (isNaN(this.state.timeValue) || this.state.timeValue === 0) + return 'Please enter a valid stale time'; + return null; + } + + render() { + return ( +
+ + + + Dashboard Configuration + + + + + Supply a personal access token to allow this dashboard to access + GitHub's GraphQL API. The token does not need to have any special + permissions. See the{' '} + + GitHub documentation + {' '} + for more information on creating and using personal access tokens. + + + + + Your personal access token will stored in NerdStorage vault, and + only be accessible to you. The token can be removed or revoked at + any time. + + + + + + + + + + + + {this.state.patStatus.valid ? ( + + Authenticated as {this.state.patStatus.userName} + + ) : null} + + + + Select which repositories you would like this tool to scan. + + + + + this.setState(({ repoValue }) => ({ + repoValue: repoValue.concat([name]) + })) + } + onChange={value => this.setState({ repoValue: value })} + value={this.state.repoValue} + data={this.state.patStatus.repoOptions} + placeholder="Add repositories to monitor (ex. newrelic/nr1-ospo)" + /> + + + + Optionally select users or labels this tool should ignore items + from. + + + + + this.setState(({ labelValue }) => ({ + labelValue: labelValue.concat([name]) + })) + } + onChange={value => this.setState({ labelValue: value })} + value={this.state.labelValue} + placeholder="Select labels to filter" + data={this.props.labelOptions} + textField="name" + itemComponent={({ item }) => ( + + )} + /> + + + + this.setState(({ userValue }) => ({ + userValue: userValue.concat([name]) + })) + } + onChange={value => this.setState({ userValue: value })} + value={this.state.userValue} + data={[]} + placeholder="Select users to filter" + /> + + + + Optionally adjust the time required for an item to be considered + stale. + + + + + + + this.setState({ timeValue: parseFloat(target.value) }) + } + invalid={ + this.state.timeValue !== null && isNaN(this.state.timeValue) + ? 'Value is not a number' + : false + } + value={this.state.timeValue.toString()} + /> + + + + + + + + + + {this.getFormError()} + + + +
+ ); + } +} diff --git a/nerdlets/maintainer-dashboard/storageUtil.js b/nerdlets/maintainer-dashboard/storageUtil.js new file mode 100644 index 0000000..472094e --- /dev/null +++ b/nerdlets/maintainer-dashboard/storageUtil.js @@ -0,0 +1,49 @@ +import { UserSecretsMutation, UserSecretsQuery } from '@newrelic/nr1-community'; +import { UserStorageMutation, UserStorageQuery } from 'nr1'; + +const GH_TOKEN_KEY = 'githubToken'; +const SETTINGS_KEY = 'nr1OspoSettings'; + +export async function writeToken(token) { + return UserSecretsMutation.mutate({ + actionType: UserSecretsMutation.ACTION_TYPE.WRITE_SECRET, + name: GH_TOKEN_KEY, + value: token + }); +} + +export async function readToken() { + const { data } = await UserSecretsQuery.query({ + name: GH_TOKEN_KEY + }); + return data?.value; +} + +export async function removeToken() { + return UserSecretsMutation.mutate({ + actionType: UserSecretsMutation.ACTION_TYPE.DELETE_SECRET, + name: GH_TOKEN_KEY + }); +} + +export async function writeSettings({ repos, labels, users, staleTime }) { + return UserStorageMutation.mutate({ + actionType: UserStorageMutation.ACTION_TYPE.WRITE_DOCUMENT, + collection: SETTINGS_KEY, + documentId: SETTINGS_KEY, + document: { + repos, + labels, + users, + staleTime + } + }); +} + +export async function readSettings() { + const { data } = await UserStorageQuery.query({ + collection: SETTINGS_KEY, + documentId: SETTINGS_KEY + }); + return data; +} diff --git a/nerdlets/maintainer-dashboard/styles.scss b/nerdlets/maintainer-dashboard/styles.scss index 197777e..18b7567 100644 --- a/nerdlets/maintainer-dashboard/styles.scss +++ b/nerdlets/maintainer-dashboard/styles.scss @@ -1,5 +1,8 @@ -@use 'react-bootstrap-table-next/dist/react-bootstrap-table2.min.css'; -@use 'react-bootstrap-table2-filter/dist/react-bootstrap-table2-filter.min.css'; +@import '~react-bootstrap-table-next/dist/react-bootstrap-table2.min.css'; +@import '~react-bootstrap-table2-filter/dist/react-bootstrap-table2-filter.min.css'; +$font-path: '~react-widgets/lib/fonts'; +$img-path: '~react-widgets/lib/img'; +@import '~react-widgets/lib/scss/react-widgets'; .ospo-tableheader { line-height: 30px; diff --git a/package-lock.json b/package-lock.json index 00cccac..d86c7f1 100644 --- a/package-lock.json +++ b/package-lock.json @@ -142,12 +142,12 @@ } }, "@newrelic/nr1-community": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/@newrelic/nr1-community/-/nr1-community-1.2.0.tgz", - "integrity": "sha512-lYPJRxoUjqIEVlSft/w3qmp4iSODKx8twDkVEYiFnSWVZUtfy24dK7nVKijBP8GXLP4pwGz7cGvGNzdaSje2MA==", + "version": "1.3.0-alpha.5", + "resolved": "https://registry.npmjs.org/@newrelic/nr1-community/-/nr1-community-1.3.0-alpha.5.tgz", + "integrity": "sha512-wBY4qhTIDgRmkf3/nRGxpwiBj49b7dJDVM41kKhT9inZfp9pFoCWGDgbf85Mg/qujwgHNuc5ZbTxxqBsvZQH9A==", "requires": { "crypto-random-string": "^3.2.0", - "date-fns": "^2.13.0", + "date-fns": "^2.15.0", "funnel-graph-js": "^1.4.2", "lodash.get": "^4.4.2", "nice-color-palettes": "^3.0.0" @@ -223,6 +223,20 @@ "resolved": "https://registry.npmjs.org/@types/node/-/node-14.11.5.tgz", "integrity": "sha512-jVFzDV6NTbrLMxm4xDSIW/gKnk8rQLF9wAzLWIOg+5nU6ACrIMndeBdXci0FGtqJbP9tQvm6V39eshc96TO2wQ==" }, + "@types/prop-types": { + "version": "15.7.3", + "resolved": "https://registry.npmjs.org/@types/prop-types/-/prop-types-15.7.3.tgz", + "integrity": "sha512-KfRL3PuHmqQLOG+2tGpRO26Ctg+Cq1E01D2DMriKEATHgWLfeNDmq9e29Q9WIky0dQ3NPkd1mzYH8Lm936Z9qw==" + }, + "@types/react": { + "version": "16.9.52", + "resolved": "https://registry.npmjs.org/@types/react/-/react-16.9.52.tgz", + "integrity": "sha512-EHRjmnxiNivwhGdMh9sz1Yw9AUxTSZFxKqdBWAAzyZx3sufWwx6ogqHYh/WB1m/I4ZpjkoZLExF5QTy2ekVi/Q==", + "requires": { + "@types/prop-types": "*", + "csstype": "^3.0.2" + } + }, "@types/zen-observable": { "version": "0.8.1", "resolved": "https://registry.npmjs.org/@types/zen-observable/-/zen-observable-0.8.1.tgz", @@ -716,6 +730,11 @@ "resolved": "https://registry.npmjs.org/csstype/-/csstype-3.0.3.tgz", "integrity": "sha512-jPl+wbWPOWJ7SXsWyqGRk3lGecbar0Cb0OvZF/r/ZU011R4YqiRehgkQ9p4eQfo9DSDLqLL3wHwfxeJiuIsNag==" }, + "date-arithmetic": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/date-arithmetic/-/date-arithmetic-3.1.0.tgz", + "integrity": "sha1-H80D29UEudvuK5B4yFpfHH08wtM=" + }, "date-fns": { "version": "2.16.1", "resolved": "https://registry.npmjs.org/date-fns/-/date-fns-2.16.1.tgz", @@ -738,6 +757,11 @@ "mimic-response": "^1.0.0" } }, + "deconstruct-number-format": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/deconstruct-number-format/-/deconstruct-number-format-0.0.1.tgz", + "integrity": "sha1-+7XX3NRb0rL6Dlee2RtZx+KbOGo=" + }, "deep-is": { "version": "0.1.3", "resolved": "https://registry.npmjs.org/deep-is/-/deep-is-0.1.3.tgz", @@ -1408,6 +1432,20 @@ "integrity": "sha512-r5wGx7YeOwNWNlCA0wQ86zKyDLMQr+/RB8xy74M4hTphfmjlijTSSXGuH8rnvKZnfT9i+75zmd8jcKdMR4O6jA==", "dev": true }, + "format-number": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/format-number/-/format-number-2.0.2.tgz", + "integrity": "sha1-i+Kqb6bvXsQ4MIkrHa7nJwX5tGI=" + }, + "format-number-with-string": { + "version": "0.0.2", + "resolved": "https://registry.npmjs.org/format-number-with-string/-/format-number-with-string-0.0.2.tgz", + "integrity": "sha1-65bo8WQMkh90FEZsz43LO7aALLo=", + "requires": { + "deconstruct-number-format": "~0.0.1", + "format-number": "^2.0.1" + } + }, "fs-extra": { "version": "9.0.1", "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-9.0.1.tgz", @@ -1719,6 +1757,14 @@ "side-channel": "^1.0.2" } }, + "invariant": { + "version": "2.2.4", + "resolved": "https://registry.npmjs.org/invariant/-/invariant-2.2.4.tgz", + "integrity": "sha512-phJfQVBuaJM5raOpJjSfkiD6BpbCE4Ns//LaXl6wGYtUBY83nWS6Rf9tXm2e8VaK60JEjYldbPif/A2B1C2gNA==", + "requires": { + "loose-envify": "^1.0.0" + } + }, "is-arrayish": { "version": "0.2.1", "resolved": "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.2.1.tgz", @@ -2381,6 +2427,25 @@ "react-is": "^16.8.1" } }, + "prop-types-extra": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/prop-types-extra/-/prop-types-extra-1.1.1.tgz", + "integrity": "sha512-59+AHNnHYCdiC+vMwY52WmvP5dM3QLeoumYuEyceQDi9aEhtwN9zIQ2ZNo25sMyXnbh32h+P1ezDsUpUH3JAew==", + "requires": { + "react-is": "^16.3.2", + "warning": "^4.0.0" + }, + "dependencies": { + "warning": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/warning/-/warning-4.0.3.tgz", + "integrity": "sha512-rpJyN222KWIvHJ/F53XSZv0Zl/accqHR8et1kpaMTD/fLCRxtV8iX8czMzY7sVZupTI3zcUTg8eycS2kNF9l6w==", + "requires": { + "loose-envify": "^1.0.0" + } + } + } + }, "pump": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/pump/-/pump-3.0.0.tgz", @@ -2435,6 +2500,15 @@ "version": "https://gitpkg.now.sh/prototypicalpro/react-bootstrap-table2/packages/react-bootstrap-table2-filter?feat/with-build", "integrity": "sha512-3cyXzg8a4Ic7YRsoY2ck6m/2/SMayg15iZ3bhxcEU5vy7Shzbk9STEeuqnLIfOhK22u7x8p1fUnAas1BinvdWA==" }, + "react-component-managers": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/react-component-managers/-/react-component-managers-3.2.2.tgz", + "integrity": "sha512-SqtB09hS1ir0koBNybvNbNAB3k/r7IbIGbXSxvkkTV0m50s+4oJ59KYsbPAQ/2DhE169Rc5V26d674EcGcDbGA==", + "requires": { + "prop-types": "^15.6.1", + "spy-on-component": "^1.1.0" + } + }, "react-dom": { "version": "16.6.3", "resolved": "https://registry.npmjs.org/react-dom/-/react-dom-16.6.3.tgz", @@ -2447,19 +2521,16 @@ "scheduler": "^0.11.2" } }, - "react-feather": { - "version": "2.0.8", - "resolved": "https://registry.npmjs.org/react-feather/-/react-feather-2.0.8.tgz", - "integrity": "sha512-J0dCEOvOxpovHeOVj3+8mAhN3/UERTfX6rSxnV6x4E+0s+STY536jhSjRfpSvTQA0SSFjYr4KrpPfdsLmK+zZg==", - "requires": { - "prop-types": "^15.7.2" - } - }, "react-is": { "version": "16.13.1", "resolved": "https://registry.npmjs.org/react-is/-/react-is-16.13.1.tgz", "integrity": "sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ==" }, + "react-lifecycles-compat": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/react-lifecycles-compat/-/react-lifecycles-compat-3.0.4.tgz", + "integrity": "sha512-fBASbA6LnOU9dOU2eW7aQ8xmYBSXUIWr+UmF9b1efZBazGNO+rcXT/icdKnYm2pTwcRylVUYwW7H1PHfLekVzA==" + }, "react-transition-group": { "version": "4.4.1", "resolved": "https://registry.npmjs.org/react-transition-group/-/react-transition-group-4.4.1.tgz", @@ -2471,6 +2542,53 @@ "prop-types": "^15.6.2" } }, + "react-widgets": { + "version": "4.6.0", + "resolved": "https://registry.npmjs.org/react-widgets/-/react-widgets-4.6.0.tgz", + "integrity": "sha512-rfQhu5Eoh2V/qhjkJQy4ZrtGYmldmpyCN6pNFWldGhAUVGTRr8reXfVqD/LDzOycal8XeRVUaW6oABPSgn14FQ==", + "requires": { + "classnames": "^2.2.6", + "date-arithmetic": "^3.1.0", + "dom-helpers": "^3.3.1", + "invariant": "^2.2.4", + "prop-types-extra": "^1.0.1", + "react-component-managers": "^3.1.0", + "react-lifecycles-compat": "^3.0.4", + "react-transition-group": "^2.4.0", + "uncontrollable": "^7.1.1", + "warning": "^3.0.0" + }, + "dependencies": { + "dom-helpers": { + "version": "3.4.0", + "resolved": "https://registry.npmjs.org/dom-helpers/-/dom-helpers-3.4.0.tgz", + "integrity": "sha512-LnuPJ+dwqKDIyotW1VzmOZ5TONUN7CwkCR5hrgawTUbkBGYdeoNLZo6nNfGkCrjtE1nXXaj7iMMpDa8/d9WoIA==", + "requires": { + "@babel/runtime": "^7.1.2" + } + }, + "react-transition-group": { + "version": "2.9.0", + "resolved": "https://registry.npmjs.org/react-transition-group/-/react-transition-group-2.9.0.tgz", + "integrity": "sha512-+HzNTCHpeQyl4MJ/bdE0u6XRMe9+XG/+aL4mCxVN4DnPBQ0/5bfHWPDuOZUzYdMj94daZaZdCCc1Dzt9R/xSSg==", + "requires": { + "dom-helpers": "^3.4.0", + "loose-envify": "^1.4.0", + "prop-types": "^15.6.2", + "react-lifecycles-compat": "^3.0.4" + } + } + } + }, + "react-widgets-simple-number": { + "version": "4.1.25", + "resolved": "https://registry.npmjs.org/react-widgets-simple-number/-/react-widgets-simple-number-4.1.25.tgz", + "integrity": "sha512-xBFx2IHPxs2cxvLePgj6S9aBtW04vhym4+tybEenQOGUti2kG/PcRA5Sf1xRjDxjX3TV3aeKvrBsZFFYXkfKtA==", + "requires": { + "deconstruct-number-format": "^0.0.1", + "format-number-with-string": "^0.0.2" + } + }, "read-pkg": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/read-pkg/-/read-pkg-2.0.0.tgz", @@ -2708,6 +2826,11 @@ "integrity": "sha1-BOaSb2YolTVPPdAVIDYzuFcpfiw=", "dev": true }, + "spy-on-component": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/spy-on-component/-/spy-on-component-1.1.3.tgz", + "integrity": "sha512-a7jgnoBSdkcDWIQQwtEgUq4etajwG6+wGIjfC9ARUKwKOdHxJd+utgHTgLn81ETizpsw4xddUS3W8VePedtaIQ==" + }, "string-width": { "version": "4.2.0", "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.0.tgz", @@ -2939,6 +3062,17 @@ "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.8.1.tgz", "integrity": "sha512-4dbzIzqvjtgiM5rw1k5rEHtBANKmdudhGyBEajN01fEyhaAIhsoKNy6y7+IN93IfpFtwY9iqi7kD+xwKhQsNJA==" }, + "uncontrollable": { + "version": "7.1.1", + "resolved": "https://registry.npmjs.org/uncontrollable/-/uncontrollable-7.1.1.tgz", + "integrity": "sha512-EcPYhot3uWTS3w00R32R2+vS8Vr53tttrvMj/yA1uYRhf8hbTG2GyugGqWDY0qIskxn0uTTojVd6wPYW9ZEf8Q==", + "requires": { + "@babel/runtime": "^7.6.3", + "@types/react": "^16.9.11", + "invariant": "^2.2.4", + "react-lifecycles-compat": "^3.0.4" + } + }, "underscore": { "version": "1.9.1", "resolved": "https://registry.npmjs.org/underscore/-/underscore-1.9.1.tgz", @@ -2983,6 +3117,14 @@ "spdx-expression-parse": "^3.0.0" } }, + "warning": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/warning/-/warning-3.0.0.tgz", + "integrity": "sha1-MuU3fLVy3kqwR1O9+IIcAe1gW3w=", + "requires": { + "loose-envify": "^1.0.0" + } + }, "which": { "version": "2.0.2", "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", diff --git a/package.json b/package.json index 7248aff..18da06d 100644 --- a/package.json +++ b/package.json @@ -14,7 +14,7 @@ "email": "opensource+nr1-ospo@newrelic.com" }, "dependencies": { - "@newrelic/nr1-community": "latest", + "@newrelic/nr1-community": "^1.3.0-alpha.5", "apollo-cache-inmemory": "^1.6.5", "apollo-client": "^2.6.8", "apollo-link": "^1.2.13", @@ -29,7 +29,8 @@ "react-apollo": "~2", "react-bootstrap-table-next": "https://gitpkg.now.sh/prototypicalpro/react-bootstrap-table2/packages/react-bootstrap-table2?feat/with-build", "react-bootstrap-table2-filter": "https://gitpkg.now.sh/prototypicalpro/react-bootstrap-table2/packages/react-bootstrap-table2-filter?feat/with-build", - "react-feather": "^2.0.8" + "react-widgets": "^4.6.0", + "react-widgets-simple-number": "^4.1.25" }, "browserslist": [ "last 2 versions",