From dc5fb59b0f8efcb7b835b6e2c7e490cb6bf7375b Mon Sep 17 00:00:00 2001 From: Lars Reimann Date: Sat, 25 Jun 2022 17:44:12 +0200 Subject: [PATCH 1/4] feat(gui): help menu with generic reporting options --- api-editor/gui/package-lock.json | 204 ++++++++++++++++++ api-editor/gui/package.json | 2 + .../gui/src/features/menuBar/HelpMenu.tsx | 38 ++++ .../gui/src/features/menuBar/MenuBar.tsx | 5 +- .../reporting/issueURLBuilder.test.ts | 22 ++ .../src/features/reporting/issueURLBuilder.ts | 22 ++ 6 files changed, 292 insertions(+), 1 deletion(-) create mode 100644 api-editor/gui/src/features/menuBar/HelpMenu.tsx create mode 100644 api-editor/gui/src/features/reporting/issueURLBuilder.test.ts create mode 100644 api-editor/gui/src/features/reporting/issueURLBuilder.ts diff --git a/api-editor/gui/package-lock.json b/api-editor/gui/package-lock.json index 996e06289..c15cf7344 100644 --- a/api-editor/gui/package-lock.json +++ b/api-editor/gui/package-lock.json @@ -43,6 +43,7 @@ "@testing-library/jest-dom": "^5.16.4", "@testing-library/react": "^13.3.0", "@types/jest": "^28.1.1", + "@types/node-fetch": "^2.6.2", "@types/react": "^18.0.14", "@types/react-dom": "^18.0.5", "@types/react-router-dom": "^5.3.3", @@ -51,6 +52,7 @@ "@types/uuid": "^8.3.4", "@vitejs/plugin-react-refresh": "^1.3.5", "jest": "^28.1.0", + "node-fetch": "^2.6.7", "ts-jest": "^28.0.4", "typescript": "^4.7.3", "vite": "^2.9.9" @@ -3127,6 +3129,16 @@ "integrity": "sha512-VL7RZyCpfYEmbyd3/Eq5RNYhZt7yoL1JThZQ3KzimzhLya2Qa86U1ZZmioNWAAjiz99z1ED1xF9NUV2srvfVrA==", "dev": true }, + "node_modules/@types/node-fetch": { + "version": "2.6.2", + "resolved": "https://registry.npmjs.org/@types/node-fetch/-/node-fetch-2.6.2.tgz", + "integrity": "sha512-DHqhlq5jeESLy19TYhLakJ07kNumXWjcDdxXsLUMJZ6ue8VZJj4kLPQVE/2mdHh3xZziNF1xppu5lwmS53HR+A==", + "dev": true, + "dependencies": { + "@types/node": "*", + "form-data": "^3.0.0" + } + }, "node_modules/@types/parse-json": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/@types/parse-json/-/parse-json-4.0.0.tgz", @@ -3383,6 +3395,12 @@ "node": ">=6.0" } }, + "node_modules/asynckit": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz", + "integrity": "sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==", + "dev": true + }, "node_modules/atob": { "version": "2.1.2", "resolved": "https://registry.npmjs.org/atob/-/atob-2.1.2.tgz", @@ -4297,6 +4315,18 @@ "resolved": "https://registry.npmjs.org/colorette/-/colorette-1.2.2.tgz", "integrity": "sha512-MKGMzyfeuutC/ZJ1cba9NqcNpfeqMUcYmyF1ZFY6/Cn7CNSAKx6a+s48sqLqyAiZuaP2TcqMhoo+dlwFnVxT9w==" }, + "node_modules/combined-stream": { + "version": "1.0.8", + "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz", + "integrity": "sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==", + "dev": true, + "dependencies": { + "delayed-stream": "~1.0.0" + }, + "engines": { + "node": ">= 0.8" + } + }, "node_modules/comma-separated-tokens": { "version": "1.0.8", "resolved": "https://registry.npmjs.org/comma-separated-tokens/-/comma-separated-tokens-1.0.8.tgz", @@ -4518,6 +4548,15 @@ "integrity": "sha512-0ISdNousHvZT2EiFlZeZAHBUvSxmKswVCEf8hW7KWgG4a8MVEu/3Vb6uWYozkjylyCxe0JBIiRB1jV45S70WVQ==", "dev": true }, + "node_modules/delayed-stream": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz", + "integrity": "sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==", + "dev": true, + "engines": { + "node": ">=0.4.0" + } + }, "node_modules/dequal": { "version": "2.0.2", "resolved": "https://registry.npmjs.org/dequal/-/dequal-2.0.2.tgz", @@ -5307,6 +5346,20 @@ "node": ">=10" } }, + "node_modules/form-data": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/form-data/-/form-data-3.0.1.tgz", + "integrity": "sha512-RHkBKtLWUVwd7SqRIvCZMEvAMoGUp0XU+seQiZejj0COz3RI3hWP4sCv3gZWWLjJTd7rGwcsF5eKZGii0r/hbg==", + "dev": true, + "dependencies": { + "asynckit": "^0.4.0", + "combined-stream": "^1.0.8", + "mime-types": "^2.1.12" + }, + "engines": { + "node": ">= 6" + } + }, "node_modules/format": { "version": "0.2.2", "resolved": "https://registry.npmjs.org/format/-/format-0.2.2.tgz", @@ -9506,6 +9559,27 @@ "node": ">=8.6" } }, + "node_modules/mime-db": { + "version": "1.52.0", + "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz", + "integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==", + "dev": true, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/mime-types": { + "version": "2.1.35", + "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.35.tgz", + "integrity": "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==", + "dev": true, + "dependencies": { + "mime-db": "1.52.0" + }, + "engines": { + "node": ">= 0.6" + } + }, "node_modules/mimic-fn": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/mimic-fn/-/mimic-fn-2.1.0.tgz", @@ -9588,6 +9662,26 @@ "integrity": "sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw==", "dev": true }, + "node_modules/node-fetch": { + "version": "2.6.7", + "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.6.7.tgz", + "integrity": "sha512-ZjMPFEfVx5j+y2yF35Kzx5sF7kDzxuDj6ziH4FFbOp87zKDZNx8yExJIb05OGF4Nlt9IHFIMBkRl41VdvcNdbQ==", + "dev": true, + "dependencies": { + "whatwg-url": "^5.0.0" + }, + "engines": { + "node": "4.x || >=6.0.0" + }, + "peerDependencies": { + "encoding": "^0.1.0" + }, + "peerDependenciesMeta": { + "encoding": { + "optional": true + } + } + }, "node_modules/node-int64": { "version": "0.4.0", "resolved": "https://registry.npmjs.org/node-int64/-/node-int64-0.4.0.tgz", @@ -11290,6 +11384,12 @@ "node": ">=6" } }, + "node_modules/tr46": { + "version": "0.0.3", + "resolved": "https://registry.npmjs.org/tr46/-/tr46-0.0.3.tgz", + "integrity": "sha512-N3WMsuqV66lT30CrXNbEjx4GEwlow3v6rr4mCcv6prnfwhS01rkgyFdjPNBYd9br7LpXV1+Emh01fHnq2Gdgrw==", + "dev": true + }, "node_modules/trough": { "version": "2.0.2", "resolved": "https://registry.npmjs.org/trough/-/trough-2.0.2.tgz", @@ -11990,6 +12090,22 @@ "url": "https://github.com/sponsors/wooorm" } }, + "node_modules/webidl-conversions": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-3.0.1.tgz", + "integrity": "sha512-2JAn3z8AR6rjK8Sm8orRC0h/bcl/DqL7tRPdGZ4I1CjdF+EaMLmYxBHyXuKL849eucPFhvBoxMsflfOb8kxaeQ==", + "dev": true + }, + "node_modules/whatwg-url": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-5.0.0.tgz", + "integrity": "sha512-saE57nupxk6v3HY35+jzBwYa0rKSy0XR8JSxZPwgLr7ys0IBzhGviA1/TUGJLmSVqs8pb9AnvICXEuOHLprYTw==", + "dev": true, + "dependencies": { + "tr46": "~0.0.3", + "webidl-conversions": "^3.0.0" + } + }, "node_modules/which": { "version": "2.0.2", "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", @@ -14465,6 +14581,16 @@ "integrity": "sha512-VL7RZyCpfYEmbyd3/Eq5RNYhZt7yoL1JThZQ3KzimzhLya2Qa86U1ZZmioNWAAjiz99z1ED1xF9NUV2srvfVrA==", "dev": true }, + "@types/node-fetch": { + "version": "2.6.2", + "resolved": "https://registry.npmjs.org/@types/node-fetch/-/node-fetch-2.6.2.tgz", + "integrity": "sha512-DHqhlq5jeESLy19TYhLakJ07kNumXWjcDdxXsLUMJZ6ue8VZJj4kLPQVE/2mdHh3xZziNF1xppu5lwmS53HR+A==", + "dev": true, + "requires": { + "@types/node": "*", + "form-data": "^3.0.0" + } + }, "@types/parse-json": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/@types/parse-json/-/parse-json-4.0.0.tgz", @@ -14692,6 +14818,12 @@ "integrity": "sha512-V+SM7AbUwJ+EBnB8+DXs0hPZHO0W6pqBcc0dW90OwtVG02PswOu/teuARoLQjdDOH+t9pJgGnW5/Qmouf3gPJg==", "dev": true }, + "asynckit": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz", + "integrity": "sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==", + "dev": true + }, "atob": { "version": "2.1.2", "resolved": "https://registry.npmjs.org/atob/-/atob-2.1.2.tgz", @@ -15361,6 +15493,15 @@ "resolved": "https://registry.npmjs.org/colorette/-/colorette-1.2.2.tgz", "integrity": "sha512-MKGMzyfeuutC/ZJ1cba9NqcNpfeqMUcYmyF1ZFY6/Cn7CNSAKx6a+s48sqLqyAiZuaP2TcqMhoo+dlwFnVxT9w==" }, + "combined-stream": { + "version": "1.0.8", + "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz", + "integrity": "sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==", + "dev": true, + "requires": { + "delayed-stream": "~1.0.0" + } + }, "comma-separated-tokens": { "version": "1.0.8", "resolved": "https://registry.npmjs.org/comma-separated-tokens/-/comma-separated-tokens-1.0.8.tgz", @@ -15542,6 +15683,12 @@ "integrity": "sha512-0ISdNousHvZT2EiFlZeZAHBUvSxmKswVCEf8hW7KWgG4a8MVEu/3Vb6uWYozkjylyCxe0JBIiRB1jV45S70WVQ==", "dev": true }, + "delayed-stream": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz", + "integrity": "sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==", + "dev": true + }, "dequal": { "version": "2.0.2", "resolved": "https://registry.npmjs.org/dequal/-/dequal-2.0.2.tgz", @@ -16038,6 +16185,17 @@ "tslib": "^2.0.3" } }, + "form-data": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/form-data/-/form-data-3.0.1.tgz", + "integrity": "sha512-RHkBKtLWUVwd7SqRIvCZMEvAMoGUp0XU+seQiZejj0COz3RI3hWP4sCv3gZWWLjJTd7rGwcsF5eKZGii0r/hbg==", + "dev": true, + "requires": { + "asynckit": "^0.4.0", + "combined-stream": "^1.0.8", + "mime-types": "^2.1.12" + } + }, "format": { "version": "0.2.2", "resolved": "https://registry.npmjs.org/format/-/format-0.2.2.tgz", @@ -19000,6 +19158,21 @@ "picomatch": "^2.3.1" } }, + "mime-db": { + "version": "1.52.0", + "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz", + "integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==", + "dev": true + }, + "mime-types": { + "version": "2.1.35", + "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.35.tgz", + "integrity": "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==", + "dev": true, + "requires": { + "mime-db": "1.52.0" + } + }, "mimic-fn": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/mimic-fn/-/mimic-fn-2.1.0.tgz", @@ -19061,6 +19234,15 @@ "integrity": "sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw==", "dev": true }, + "node-fetch": { + "version": "2.6.7", + "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.6.7.tgz", + "integrity": "sha512-ZjMPFEfVx5j+y2yF35Kzx5sF7kDzxuDj6ziH4FFbOp87zKDZNx8yExJIb05OGF4Nlt9IHFIMBkRl41VdvcNdbQ==", + "dev": true, + "requires": { + "whatwg-url": "^5.0.0" + } + }, "node-int64": { "version": "0.4.0", "resolved": "https://registry.npmjs.org/node-int64/-/node-int64-0.4.0.tgz", @@ -20289,6 +20471,12 @@ "resolved": "https://registry.npmjs.org/totalist/-/totalist-2.0.0.tgz", "integrity": "sha512-+Y17F0YzxfACxTyjfhnJQEe7afPA0GSpYlFkl2VFMxYP7jshQf9gXV7cH47EfToBumFThfKBvfAcoUn6fdNeRQ==" }, + "tr46": { + "version": "0.0.3", + "resolved": "https://registry.npmjs.org/tr46/-/tr46-0.0.3.tgz", + "integrity": "sha512-N3WMsuqV66lT30CrXNbEjx4GEwlow3v6rr4mCcv6prnfwhS01rkgyFdjPNBYd9br7LpXV1+Emh01fHnq2Gdgrw==", + "dev": true + }, "trough": { "version": "2.0.2", "resolved": "https://registry.npmjs.org/trough/-/trough-2.0.2.tgz", @@ -20754,6 +20942,22 @@ "resolved": "https://registry.npmjs.org/web-namespaces/-/web-namespaces-2.0.0.tgz", "integrity": "sha512-dE7ELZRVWh0ceQsRgkjLgsAvwTuv3kcjSY/hLjqL0llleUlQBDjE9JkB9FCBY5F2mnFEwiyJoowl8+NVGHe8dw==" }, + "webidl-conversions": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-3.0.1.tgz", + "integrity": "sha512-2JAn3z8AR6rjK8Sm8orRC0h/bcl/DqL7tRPdGZ4I1CjdF+EaMLmYxBHyXuKL849eucPFhvBoxMsflfOb8kxaeQ==", + "dev": true + }, + "whatwg-url": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-5.0.0.tgz", + "integrity": "sha512-saE57nupxk6v3HY35+jzBwYa0rKSy0XR8JSxZPwgLr7ys0IBzhGviA1/TUGJLmSVqs8pb9AnvICXEuOHLprYTw==", + "dev": true, + "requires": { + "tr46": "~0.0.3", + "webidl-conversions": "^3.0.0" + } + }, "which": { "version": "2.0.2", "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", diff --git a/api-editor/gui/package.json b/api-editor/gui/package.json index 4fc036914..5e9c4819f 100644 --- a/api-editor/gui/package.json +++ b/api-editor/gui/package.json @@ -47,6 +47,7 @@ "@testing-library/jest-dom": "^5.16.4", "@testing-library/react": "^13.3.0", "@types/jest": "^28.1.1", + "@types/node-fetch": "^2.6.2", "@types/react": "^18.0.14", "@types/react-dom": "^18.0.5", "@types/react-router-dom": "^5.3.3", @@ -55,6 +56,7 @@ "@types/uuid": "^8.3.4", "@vitejs/plugin-react-refresh": "^1.3.5", "jest": "^28.1.0", + "node-fetch": "^2.6.7", "ts-jest": "^28.0.4", "typescript": "^4.7.3", "vite": "^2.9.9" diff --git a/api-editor/gui/src/features/menuBar/HelpMenu.tsx b/api-editor/gui/src/features/menuBar/HelpMenu.tsx new file mode 100644 index 000000000..f7353d423 --- /dev/null +++ b/api-editor/gui/src/features/menuBar/HelpMenu.tsx @@ -0,0 +1,38 @@ +import { Box, Button, Icon, Menu, MenuButton, MenuGroup, MenuItem, MenuList } from '@chakra-ui/react'; +import React from 'react'; +import { FaBug, FaChevronDown, FaLightbulb } from 'react-icons/fa'; +import { bugReportURL, featureRequestURL } from '../reporting/issueURLBuilder'; + +export const HelpMenu = function () { + return ( + + + }> + Help + + + + { + window.open(bugReportURL, '_blank'); + }} + icon={} + > + Report a Bug + + { + window.open(featureRequestURL, '_blank'); + }} + icon={} + > + Request a Feature + + + + + + ); +}; diff --git a/api-editor/gui/src/features/menuBar/MenuBar.tsx b/api-editor/gui/src/features/menuBar/MenuBar.tsx index dbced7bc6..e91a00606 100644 --- a/api-editor/gui/src/features/menuBar/MenuBar.tsx +++ b/api-editor/gui/src/features/menuBar/MenuBar.tsx @@ -61,6 +61,7 @@ import { import { selectUsages } from '../usages/usageSlice'; import { useLocation, useNavigate } from 'react-router-dom'; import { SelectionBreadcrumbs } from './SelectionBreadcrumbs'; +import { HelpMenu } from './HelpMenu'; interface MenuBarProps { displayInferErrors: (errors: string[]) => void; @@ -278,7 +279,7 @@ export const MenuBar: React.FC = function ({ displayInferErrors }) Toggle Completion of Selected Element - + dispatch(setBatchMode(BatchMode.Constant))} @@ -477,6 +478,8 @@ export const MenuBar: React.FC = function ({ displayInferErrors }) + + diff --git a/api-editor/gui/src/features/reporting/issueURLBuilder.test.ts b/api-editor/gui/src/features/reporting/issueURLBuilder.test.ts new file mode 100644 index 000000000..2a358ec59 --- /dev/null +++ b/api-editor/gui/src/features/reporting/issueURLBuilder.test.ts @@ -0,0 +1,22 @@ +import { bugReportURL, featureRequestURL, missingAnnotationURL, wrongAnnotationURL } from './issueURLBuilder'; +import fetch from 'node-fetch'; + +test('bug report URL should be correct', async () => { + const response = await fetch(bugReportURL); + expect(response.status).toBe(200); +}); + +test('feature request URL should be correct', async () => { + const response = await fetch(featureRequestURL); + expect(response.status).toBe(200); +}); + +test('missing annotation URL should be correct', async () => { + const response = await fetch(missingAnnotationURL('foo')); + expect(response.status).toBe(200); +}); + +test('wrong annotation URL should be correct', async () => { + const response = await fetch(wrongAnnotationURL('required', { target: 'foo' })); + expect(response.status).toBe(200); +}); diff --git a/api-editor/gui/src/features/reporting/issueURLBuilder.ts b/api-editor/gui/src/features/reporting/issueURLBuilder.ts new file mode 100644 index 000000000..e54e8e617 --- /dev/null +++ b/api-editor/gui/src/features/reporting/issueURLBuilder.ts @@ -0,0 +1,22 @@ +import { Annotation } from '../annotations/annotationSlice'; + +const baseURL = 'https://github.com/lars-reimann/api-editor/issues/new'; + +export const bugReportURL = `${baseURL}?assignees=&labels=bug&template=bug_report.yml`; +export const featureRequestURL = `${baseURL}?assignees=&labels=enhancement&template=feature_request.yml`; + +const baseMissingAnnotationURL = `${baseURL}?assignees=&labels=bug%2Cmissing+annotation&template=missing_annotation.yml`; + +export const missingAnnotationURL = function (target: string): string { + const urlHash = encodeURIComponent(`\`${target}\``); + return `${baseMissingAnnotationURL}&url-hash=${urlHash}`; +}; + +const baseWrongAnnotationURL = `${baseURL}?assignees=&labels=bug%2Cwrong+annotation&template=wrong_annotation.yml`; + +export const wrongAnnotationURL = function (annotationType: string, annotation: Annotation): string { + const urlHash = encodeURIComponent(`\`${annotation.target}\``); + const actualAnnotationType = encodeURIComponent(`\`@${annotationType}\``); + const actualAnnotationInputs = encodeURIComponent(`\`\`\`json5\n${JSON.stringify(annotation, null, 4)}\`\`\``); + return `${baseWrongAnnotationURL}&url-hash=${urlHash}&actual-annotation-type=${actualAnnotationType}&actual-annotation-inputs=${actualAnnotationInputs}`; +}; From be0ad4e4a45051783a73defb03f4b9dba2aca8c6 Mon Sep 17 00:00:00 2001 From: Lars Reimann Date: Sat, 25 Jun 2022 18:24:52 +0200 Subject: [PATCH 2/4] feat(gui): report wrong annotations --- .../features/annotations/AnnotationView.tsx | 31 +++++++++++++++++-- .../src/features/reporting/issueURLBuilder.ts | 15 +++++++-- 2 files changed, 41 insertions(+), 5 deletions(-) diff --git a/api-editor/gui/src/features/annotations/AnnotationView.tsx b/api-editor/gui/src/features/annotations/AnnotationView.tsx index a9fcf4814..40d7c9fad 100644 --- a/api-editor/gui/src/features/annotations/AnnotationView.tsx +++ b/api-editor/gui/src/features/annotations/AnnotationView.tsx @@ -1,6 +1,6 @@ import { Button, ButtonGroup, Icon, IconButton, Stack, Text as ChakraText } from '@chakra-ui/react'; import React from 'react'; -import { FaCheck, FaTrash, FaWrench } from 'react-icons/fa'; +import { FaCheck, FaFlag, FaTrash, FaWrench } from 'react-icons/fa'; import { useAppDispatch, useAppSelector } from '../../app/hooks'; import { Annotation, @@ -65,6 +65,7 @@ import { showTodoAnnotationForm, } from '../ui/uiSlice'; import { truncate } from '../../common/util/stringOperations'; +import { wrongAnnotationURL } from '../reporting/issueURLBuilder'; interface AnnotationViewProps { target: string; @@ -132,6 +133,7 @@ export const AnnotationView: React.FC = function ({ target onReview={() => { dispatch(reviewBoundary(target)); }} + reportable /> )} {Object.keys(calledAfterAnnotation).map((calledAfterName) => ( @@ -154,6 +156,7 @@ export const AnnotationView: React.FC = function ({ target onReview={() => { dispatch(reviewConstant(target)); }} + reportable /> )} {descriptionAnnotation && ( @@ -177,6 +180,7 @@ export const AnnotationView: React.FC = function ({ target onReview={() => { dispatch(reviewEnum(target)); }} + reportable /> )} {Object.keys(groupAnnotations).map((groupName) => ( @@ -212,6 +216,7 @@ export const AnnotationView: React.FC = function ({ target onReview={() => { dispatch(reviewOptional(target)); }} + reportable /> )} {pureAnnotation && ( @@ -228,6 +233,7 @@ export const AnnotationView: React.FC = function ({ target annotation={removeAnnotation} onDelete={() => dispatch(removeRemove(target))} onReview={() => dispatch(reviewRemove(target))} + reportable /> )} {renameAnnotation && ( @@ -248,6 +254,7 @@ export const AnnotationView: React.FC = function ({ target annotation={requiredAnnotation} onDelete={() => dispatch(removeRequired(target))} onReview={() => dispatch(reviewRequired(target))} + reportable /> )} {todoAnnotation && ( @@ -309,11 +316,21 @@ interface AnnotationTagProps { onEdit?: () => void; onDelete: () => void; onReview: () => void; + reportable?: boolean; } -const AnnotationTag: React.FC = function ({ type, name, annotation, onDelete, onEdit, onReview }) { +const AnnotationTag: React.FC = function ({ + type, + name, + annotation, + onDelete, + onEdit, + onReview, + reportable = false, +}) { const isValidUsername = useAppSelector(selectUsernameIsValid); const isCorrect = (annotation.reviewers?.length ?? 0) > 0; + const isReportable = reportable && (annotation.authors ?? []).includes("$autogen$") return ( @@ -355,6 +372,16 @@ const AnnotationTag: React.FC = function ({ type, name, anno Mark as Correct )} + {isReportable && ( + } + aria-label="Report Wrong Annotation" + colorScheme="orange" + onClick={() => { + window.open(wrongAnnotationURL(type, annotation), '_blank'); + }} + /> + )} ); }; diff --git a/api-editor/gui/src/features/reporting/issueURLBuilder.ts b/api-editor/gui/src/features/reporting/issueURLBuilder.ts index e54e8e617..8ebd1003d 100644 --- a/api-editor/gui/src/features/reporting/issueURLBuilder.ts +++ b/api-editor/gui/src/features/reporting/issueURLBuilder.ts @@ -8,15 +8,24 @@ export const featureRequestURL = `${baseURL}?assignees=&labels=enhancement&templ const baseMissingAnnotationURL = `${baseURL}?assignees=&labels=bug%2Cmissing+annotation&template=missing_annotation.yml`; export const missingAnnotationURL = function (target: string): string { - const urlHash = encodeURIComponent(`\`${target}\``); + const urlHash = encodeURIComponent(`\`#/${target}\``); return `${baseMissingAnnotationURL}&url-hash=${urlHash}`; }; const baseWrongAnnotationURL = `${baseURL}?assignees=&labels=bug%2Cwrong+annotation&template=wrong_annotation.yml`; export const wrongAnnotationURL = function (annotationType: string, annotation: Annotation): string { - const urlHash = encodeURIComponent(`\`${annotation.target}\``); + const minimalAnnotation = { ...annotation }; + + // noinspection JSConstantReassignment + delete minimalAnnotation.authors; + // noinspection JSConstantReassignment + delete minimalAnnotation.reviewers; + + const urlHash = encodeURIComponent(`\`#/${annotation.target}\``); const actualAnnotationType = encodeURIComponent(`\`@${annotationType}\``); - const actualAnnotationInputs = encodeURIComponent(`\`\`\`json5\n${JSON.stringify(annotation, null, 4)}\`\`\``); + const actualAnnotationInputs = encodeURIComponent( + `\`\`\`json5\n${JSON.stringify(minimalAnnotation, null, 4)}\n\`\`\``, + ); return `${baseWrongAnnotationURL}&url-hash=${urlHash}&actual-annotation-type=${actualAnnotationType}&actual-annotation-inputs=${actualAnnotationInputs}`; }; From 3f5562186228073e0f4679f19aab3466ab6a9086 Mon Sep 17 00:00:00 2001 From: Lars Reimann Date: Sat, 25 Jun 2022 18:27:43 +0200 Subject: [PATCH 3/4] feat(gui): report missing annotations --- .../annotations/MissingAnnotationButton.tsx | 23 +++++++++++++++++++ .../packageData/selectionView/ClassView.tsx | 2 ++ .../selectionView/FunctionView.tsx | 2 ++ .../selectionView/ParameterNode.tsx | 2 ++ 4 files changed, 29 insertions(+) create mode 100644 api-editor/gui/src/features/annotations/MissingAnnotationButton.tsx diff --git a/api-editor/gui/src/features/annotations/MissingAnnotationButton.tsx b/api-editor/gui/src/features/annotations/MissingAnnotationButton.tsx new file mode 100644 index 000000000..cbb7a5867 --- /dev/null +++ b/api-editor/gui/src/features/annotations/MissingAnnotationButton.tsx @@ -0,0 +1,23 @@ +import { IconButton } from '@chakra-ui/react'; +import React from 'react'; +import { FaFlag } from 'react-icons/fa'; +import { missingAnnotationURL } from '../reporting/issueURLBuilder'; + +interface MissingAnnotationButtonProps { + target: string; +} + +export const MissingAnnotationButton: React.FC = function ({ target }) { + return ( + } + aria-label="Report Missing Annotation" + size="sm" + variant="outline" + colorScheme="orange" + onClick={() => { + window.open(missingAnnotationURL(target), '_blank'); + }} + /> + ); +}; diff --git a/api-editor/gui/src/features/packageData/selectionView/ClassView.tsx b/api-editor/gui/src/features/packageData/selectionView/ClassView.tsx index ec058b659..b35e2c980 100644 --- a/api-editor/gui/src/features/packageData/selectionView/ClassView.tsx +++ b/api-editor/gui/src/features/packageData/selectionView/ClassView.tsx @@ -6,6 +6,7 @@ import { PythonClass } from '../model/PythonClass'; import { DocumentationText } from './DocumentationText'; import { SectionListViewItem } from './SectionListViewItem'; import { CompleteButton } from '../../annotations/CompleteButton'; +import {MissingAnnotationButton} from "../../annotations/MissingAnnotationButton"; interface ClassViewProps { pythonClass: PythonClass; @@ -25,6 +26,7 @@ export const ClassView: React.FC = function ({ pythonClass }) { )} + diff --git a/api-editor/gui/src/features/packageData/selectionView/FunctionView.tsx b/api-editor/gui/src/features/packageData/selectionView/FunctionView.tsx index 0ec799cb1..1041d2c77 100644 --- a/api-editor/gui/src/features/packageData/selectionView/FunctionView.tsx +++ b/api-editor/gui/src/features/packageData/selectionView/FunctionView.tsx @@ -13,6 +13,7 @@ import { CompleteButton } from '../../annotations/CompleteButton'; import { PythonParameter } from '../model/PythonParameter'; import { selectFilter, selectSorter } from '../../ui/uiSlice'; import { selectUsages } from '../../usages/usageSlice'; +import {MissingAnnotationButton} from "../../annotations/MissingAnnotationButton"; interface FunctionViewProps { pythonFunction: PythonFunction; @@ -49,6 +50,7 @@ export const FunctionView: React.FC = function ({ pythonFunct /> )} + diff --git a/api-editor/gui/src/features/packageData/selectionView/ParameterNode.tsx b/api-editor/gui/src/features/packageData/selectionView/ParameterNode.tsx index 88a52558a..6ae0c232a 100644 --- a/api-editor/gui/src/features/packageData/selectionView/ParameterNode.tsx +++ b/api-editor/gui/src/features/packageData/selectionView/ParameterNode.tsx @@ -6,6 +6,7 @@ import { PythonParameter } from '../model/PythonParameter'; import { DocumentationText } from './DocumentationText'; import { CompleteButton } from '../../annotations/CompleteButton'; import { Link } from 'react-router-dom'; +import {MissingAnnotationButton} from "../../annotations/MissingAnnotationButton"; interface ParameterNodeProps { pythonParameter: PythonParameter; @@ -46,6 +47,7 @@ export const ParameterNode: React.FC = function ({ isTitle, /> )} + From 1896605893c9a5eafb6517b42894614516757b7c Mon Sep 17 00:00:00 2001 From: lars-reimann Date: Sat, 25 Jun 2022 16:36:29 +0000 Subject: [PATCH 4/4] style: apply automatic fixes of linters --- api-editor/gui/src/features/annotations/AnnotationView.tsx | 2 +- .../gui/src/features/packageData/selectionView/ClassView.tsx | 2 +- .../gui/src/features/packageData/selectionView/FunctionView.tsx | 2 +- .../src/features/packageData/selectionView/ParameterNode.tsx | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/api-editor/gui/src/features/annotations/AnnotationView.tsx b/api-editor/gui/src/features/annotations/AnnotationView.tsx index 40d7c9fad..ceb35762e 100644 --- a/api-editor/gui/src/features/annotations/AnnotationView.tsx +++ b/api-editor/gui/src/features/annotations/AnnotationView.tsx @@ -330,7 +330,7 @@ const AnnotationTag: React.FC = function ({ }) { const isValidUsername = useAppSelector(selectUsernameIsValid); const isCorrect = (annotation.reviewers?.length ?? 0) > 0; - const isReportable = reportable && (annotation.authors ?? []).includes("$autogen$") + const isReportable = reportable && (annotation.authors ?? []).includes('$autogen$'); return ( diff --git a/api-editor/gui/src/features/packageData/selectionView/ClassView.tsx b/api-editor/gui/src/features/packageData/selectionView/ClassView.tsx index b35e2c980..c1df2babb 100644 --- a/api-editor/gui/src/features/packageData/selectionView/ClassView.tsx +++ b/api-editor/gui/src/features/packageData/selectionView/ClassView.tsx @@ -6,7 +6,7 @@ import { PythonClass } from '../model/PythonClass'; import { DocumentationText } from './DocumentationText'; import { SectionListViewItem } from './SectionListViewItem'; import { CompleteButton } from '../../annotations/CompleteButton'; -import {MissingAnnotationButton} from "../../annotations/MissingAnnotationButton"; +import { MissingAnnotationButton } from '../../annotations/MissingAnnotationButton'; interface ClassViewProps { pythonClass: PythonClass; diff --git a/api-editor/gui/src/features/packageData/selectionView/FunctionView.tsx b/api-editor/gui/src/features/packageData/selectionView/FunctionView.tsx index 1041d2c77..7ccb32baf 100644 --- a/api-editor/gui/src/features/packageData/selectionView/FunctionView.tsx +++ b/api-editor/gui/src/features/packageData/selectionView/FunctionView.tsx @@ -13,7 +13,7 @@ import { CompleteButton } from '../../annotations/CompleteButton'; import { PythonParameter } from '../model/PythonParameter'; import { selectFilter, selectSorter } from '../../ui/uiSlice'; import { selectUsages } from '../../usages/usageSlice'; -import {MissingAnnotationButton} from "../../annotations/MissingAnnotationButton"; +import { MissingAnnotationButton } from '../../annotations/MissingAnnotationButton'; interface FunctionViewProps { pythonFunction: PythonFunction; diff --git a/api-editor/gui/src/features/packageData/selectionView/ParameterNode.tsx b/api-editor/gui/src/features/packageData/selectionView/ParameterNode.tsx index 6ae0c232a..55d743bed 100644 --- a/api-editor/gui/src/features/packageData/selectionView/ParameterNode.tsx +++ b/api-editor/gui/src/features/packageData/selectionView/ParameterNode.tsx @@ -6,7 +6,7 @@ import { PythonParameter } from '../model/PythonParameter'; import { DocumentationText } from './DocumentationText'; import { CompleteButton } from '../../annotations/CompleteButton'; import { Link } from 'react-router-dom'; -import {MissingAnnotationButton} from "../../annotations/MissingAnnotationButton"; +import { MissingAnnotationButton } from '../../annotations/MissingAnnotationButton'; interface ParameterNodeProps { pythonParameter: PythonParameter;