Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

RFC87: add button to load GroupComparison table data in external tool #4938

Merged
merged 56 commits into from
Aug 13, 2024

Conversation

pappde
Copy link
Contributor

@pappde pappde commented Jul 4, 2024

Summary

This PR addresses all requirements for RFC87, which involves conditionally adding a button to the GroupComparison data tables (only visible to relevant users), then launching the external tool with the data.

This PR implements a config-driven solution for defining "CustomButtons", so anyone can update their instance application.properties to launch their own custom URL protocol/software with the table data. This PR alone will not impact the appearance of the website without the "custom_buttons_json" config being defined.

Key changes:

  • Config-Driven: no tool-specific logic.
  • Check UserAgent (e.g. Windows)
  • Check if CustomButton is installed.
  • FontDetector added to check if software is installed
  • CopyDownloadButtons: Inject button based on config
  • Launch CustomButton with the data
  • Visualize your Data page: add section based on config

Testing Notes

To test this PR with a CustomButton, if the backend application.properties don't define custom_button_json, then you can use either of the following methods.

Browser Console

Paste the following into the browser console, then reload the page.

localStorage.frontendConfig = `{"serverConfig":{"download_custom_buttons_json":"[{\\\"id\\\":\\\"avm\\\",\\\"name\\\":\\\"AVM for cBioPortal\\\",\\\"tooltip\\\":\\\"Launch AVM for cBioPortal with data (copied to clipboard)\\\",\\\"image_src\\\":\\\"https:\/\/aquminmedical.com\/images\/content\/AquminLogoSimple.png\\\",\\\"required_user_agent\\\":\\\"Win\\\",\\\"required_installed_font_family\\\":\\\"AVMInstalled\\\",\\\"url_format\\\":\\\"avm:\/\/?importclipboard&-AutoMode=true&-ProjectNameHint={studyName}&-ImportDataLength={dataLength}\\\",\\\"visualize_title\\\":\\\"AVM for cBioPortal (Windows)\\\",\\\"visualize_href\\\":\\\"https:\/\/bit.ly\/avm-cbioportal\\\",\\\"visualize_description\\\":\\\"Windows software that loads data into 3D Landscapes for interactive visualization and pathway analysis. Download table data directly from cBioPortal.\\\",\\\"visualize_image_src\\\":\\\"https:\/\/github.com\/user-attachments\/assets\/5c17f5ed-0357-4ffa-a6e1-5a9d435dd3c5\\\"}]"}}`

Bookmarklet

Create a bookmarklet that will run the netlify for this PR and set the config.

javascript:(function()%7Bvar%20pr%20%3D%20prompt%28%22Please%20enter%20PR%23%20%28will%20include%20AVM%29%22%29%3Bif%20%28pr%20%26%26%20Number%28pr%29%29%20%7BlocalStorage.netlify%20%3D%20%60deploy-preview-%24%7Bpr%7D--cbioportalfrontend%60%3BlocalStorage.frontendConfig%20%3D%20%60%7B%22serverConfig%22%3A%7B%22download_custom_buttons_json%22%3A%22%5B%7B%5C%5C%5C%22id%5C%5C%5C%22%3A%5C%5C%5C%22avm%5C%5C%5C%22%2C%5C%5C%5C%22name%5C%5C%5C%22%3A%5C%5C%5C%22AVM%20for%20cBioPortal%5C%5C%5C%22%2C%5C%5C%5C%22tooltip%5C%5C%5C%22%3A%5C%5C%5C%22Launch%20AVM%20for%20cBioPortal%20with%20data%20%28copied%20to%20clipboard%29%5C%5C%5C%22%2C%5C%5C%5C%22image_src%5C%5C%5C%22%3A%5C%5C%5C%22https%3A%5C%2F%5C%2Faquminmedical.com%5C%2Fimages%5C%2Fcontent%5C%2FAquminLogoSimple.png%5C%5C%5C%22%2C%5C%5C%5C%22required_user_agent%5C%5C%5C%22%3A%5C%5C%5C%22Win%5C%5C%5C%22%2C%5C%5C%5C%22required_installed_font_family%5C%5C%5C%22%3A%5C%5C%5C%22AVMInstalled%5C%5C%5C%22%2C%5C%5C%5C%22url_format%5C%5C%5C%22%3A%5C%5C%5C%22avm%3A%5C%2F%5C%2F%3Fimportclipboard%26-AutoMode%3Dtrue%26-ProjectNameHint%3D%7BstudyName%7D%26-ImportDataLength%3D%7BdataLength%7D%5C%5C%5C%22%2C%5C%5C%5C%22visualize_title%5C%5C%5C%22%3A%5C%5C%5C%22AVM%20for%20cBioPortal%20%28Windows%29%5C%5C%5C%22%2C%5C%5C%5C%22visualize_href%5C%5C%5C%22%3A%5C%5C%5C%22https%3A%5C%2F%5C%2Fbit.ly%5C%2Favm-cbioportal%5C%5C%5C%22%2C%5C%5C%5C%22visualize_description%5C%5C%5C%22%3A%5C%5C%5C%22Windows%20software%20that%20loads%20data%20into%203D%20Landscapes%20for%20interactive%20visualization%20and%20pathway%20analysis.%20Download%20table%20data%20directly%20from%20cBioPortal.%5C%5C%5C%22%2C%5C%5C%5C%22visualize_image_src%5C%5C%5C%22%3A%5C%5C%5C%22https%3A%5C%2F%5C%2Fgithub.com%5C%2Fuser-attachments%5C%2Fassets%5C%2F5c17f5ed-0357-4ffa-a6e1-5a9d435dd3c5%5C%5C%5C%22%7D%5D%22%7D%7D%60%3Bwindow.location.reload%28%29%3B%7D%7D)()

Tests

  • If no config is defined, then there will be no other visible impact to the website.
  • Visualize Your Data page: additional section at bottom once config defined.
  • Data Tables: additional button once config defined and tool installed.
  • Clicking the button will launch the tool.

With the above configuration, the button will only appear on a windows system with the software (and custom font) installed.

Changes Summary

Configuration

  • config/IAppConfig: added custom_buttons_json
  • src/config/serverConfigDefaults: default to empty string
  • Visualize.tsx: updated per RFC87, config-driven

CopyDownloadButtons Update

  • CopyDownloadButtons: added conditionally rendered section, config-driven
  • ICopyDownloadButtons.ts/SimpleCopyDownloadControls/CopyDownloadControls: expose the downloadData() function. Previously, it was hidden behind handleDownload/handleCopy but the implementation needs access to the actual data.

New Component

  • Added module to shared/components/CustomButtons/
  • Added unit tests
  • Added open source utility for checking if a font is installed to …/CustomButtons/utils. Since we can’t check if a URL protocol is supported, we instead use the solution from the specification for checking if a custom font is installed.
    • OPEN-SOURCE-DOCUMENTATION: file updated with note about (modified) code that is used
  • Clipboard: we use the window.clipboard API. It is supported on all major browsers. This is used to send the data to the app by copying the data to the clipboard prior to launching.

Global State Dependency

  • CustomButton leverages the fact that the GroupComparisonPage reference is injected into the window. It uses this to: determine the study name

Considerations

Some things to consider that we could update.

  • FontDetector: is the update to OPEN-SOURCE-DOCUMENTATION needed? Is there a more appropriate location for the module itself?
  • Commit Log: we could squash
  • Console.Log: noticed that generally the codebase avoids this, but there are a couple of logs I left in there, like when you click the button. That is a rare and important event so it seemed appropriate, but we could remove it.
  • Error Handling: there are a couple of places that console.error(). There is one place where if the launch fails we use alert(). There is precedent in the codebase, but we could use a different reporting mechanism if desired.

Checks

  • Added unit tests.

Notify reviewers

@alisman, @jjgao

Copy link

netlify bot commented Jul 4, 2024

Deploy Preview for cbioportalfrontend ready!

Name Link
🔨 Latest commit 3014aea
🔍 Latest deploy log https://app.netlify.com/sites/cbioportalfrontend/deploys/66ba71a1943003000881cc14
😎 Deploy Preview https://deploy-preview-4938--cbioportalfrontend.netlify.app
📱 Preview on mobile
Toggle QR Code...

QR Code

Use your smartphone camera to open QR code link.

To edit notification comments on pull requests, go to your Netlify site configuration.

pappde added 28 commits July 10, 2024 16:10
- added margin to seperate tool section from section header.
- made clear for "Windows" and visible
- icon
- display, copying standard button
- style
- WIP: not config driven and always appears
- ExternalToolConfig.ts to localize in one place
- IAppConfig.external_tools: pull in type definition
- serverConfigDefaults: pull in default
- changed to be config-driven
- support array
- removed local image and pull from URL (avoids relative path)
- renamed files to match folder (camelCase)
- handleDownloadWrapper WIP
- shrunk icon to be closer to the other
- change to class
- rename folder to match convention
- moved render() out of CopyDownloadButtons
- changed CopyDownloadControls to pass along the downloadData instead of handleDownload to give tool control
- restored local icon so not dependent on external
- iconImageSrc local fixed - have to use require()
- put urlFormat parameters in a new object
- cleanup
- use clipboard
- dataLength validation
- getSingleStudyName
- mock clipboard
- mock navigation
- changed function references to functions for jest
- fixed urlParameters indexing complaints
- isAvailable check
- required_platform
- added fontdetect
- localStorage caching
Removed unnecessary imports
- changed to launch url with open() so uses new tab
- add alert() if opening URL fails (probably won't happen)
- added Test Tool to config
- fixed showing multiple <ExternalTool>
- moved getServerConfig() dependency to a new file to fix dependency issue with tests
- removing unnecessary imports
- comment update
- removed image now stored in CDN
…ttern

EXPL: the "$" complicates parsing when running in JS.
- fix one leftover old term
- fixed CustomButtonConfig to catch all interface properties
@@ -186,4 +186,5 @@ export interface IServerConfig {
vaf_log_scale_default: boolean; // this has a default
skin_study_view_show_sv_table: boolean; // this has a default
enable_study_tags: boolean;
custom_buttons_json: string;
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This will be defined by the backend application.properties.

@@ -340,7 +340,7 @@ export function initializeServerConfiguration(rawConfiguration: any) {
);
} catch (err) {
// ignore
console.log('Error parsing localStorage.frontendConfig');
console.log('Error parsing localStorage.frontendConfig:' + localStorage.frontendConfig);
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Useful update to debugging if "localStorage.frontendConfig" testing is not working.

width: 550px;
padding-right: 40px;
}
}
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this class is used by the "3rd party" section - it is the same as toolArray but wider, which looks better here.

.customButtonImage {
width: 18px;
height: 18px;
}
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is to make sure that whatever size the image is, the "button" is basically the same size as the Copy+Download font awesome buttons.

return false;
};
}
}
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This utility is a workaround to basically check a boolean "installed" flag set on the client system. It appears there isn't a way to check if a particular URL protocol is supported, or a particular software is installed, so this was the workaround that I found. The software installs a special font, then the frontend code can check if that font is installed by indirectly checking if a fallback font is used (by looking at sizes).

This code is modified from the original to be more efficient.

ALTERNATIVES
A) We could remove the FontDetector code, then the CustomButton would appear depending on UserAgent only (e.g. all Windows browsers). That could work, since I believe we can detect if launching the URL protocol failed then direct the user to another page.
B) If there is another way to detect if a particular URL protocol is supported or a particular software is installed, then we could try that. However, my research into the issue seemed to conclude there wasn't.

);
});
}

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We display the components here following the Download button. I couldn't get the "" working in the HTML code so I moved the showDownload test to this function. I believe it's better that way anyways.

return this.props.downloadData().then(data => data.text);
}
}

Copy link
Contributor Author

@pappde pappde Jul 31, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The click handler for CustomButton needs the actual data, so downloadData() is passed to the component. There are two ways that buttons get data though, synchronous (SimpleCopyDownloadControls) and asynchronous (CopyDownloadControls). So we set pass downloadData as a Promise.

Comments related to this are tagged as "TECH_DOWNLOADDATA".

OPTIONAL: technically, CustomButton could take a Promise<ICopyDownloadData>, but then we would have to move that interface out of CopyDownloadControls.tsx and I wanted to minimize the changes.

@@ -20,4 +20,7 @@ export interface ICopyDownloadInputsProps {
downloadLabel?: string;
handleDownload?: () => void;
handleCopy?: () => void;
// expose downloadData() to allow button to handle the data on it's own.
// TECH_DOWNLOADDATA: CopyDownloadButtons.downloadData needs to be async so it can work with either async context (IAsyncCopyDownloadControlsProps) or synchronous context (SimpleCopyDownloadControls)
downloadData?: Promise<string>;
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

See comment in CopyDownloadControls.tsx about why downloadData is a Promise

return Promise.resolve(this.props.downloadData());
}
}

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

See comment in CopyDownloadControls.tsx about why downloadData is a Promise. Here we have a wrapper to convert the synchronous case to a Promise.

@pappde
Copy link
Contributor Author

pappde commented Aug 2, 2024

renamed property to download_custom_buttons_json

- renamed ICopyDownloadControls and ICustomButton property downloadDataAsync to be clear is async
- fixed downloadDataAsPromise() to not run downloadData() until resolved
@alisman alisman merged commit 0b828d8 into cBioPortal:master Aug 13, 2024
11 of 15 checks passed
Nelliney pushed a commit to Nelliney/cbioportal-frontend that referenced this pull request Aug 14, 2024
…cBioPortal#4938)

* Visualize Your Data: add 3rd party tools section, link, and image.
* Allow for configuration of custom buttons in data tables that send data to third-party tools on client system
Nelliney pushed a commit to Nelliney/cbioportal-frontend that referenced this pull request Aug 14, 2024
…cBioPortal#4938)

* Visualize Your Data: add 3rd party tools section, link, and image.
* Allow for configuration of custom buttons in data tables that send data to third-party tools on client system
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants