diff --git a/.changeset/calm-items-think.md b/.changeset/calm-items-think.md new file mode 100644 index 00000000..137ac1e7 --- /dev/null +++ b/.changeset/calm-items-think.md @@ -0,0 +1,5 @@ +--- +'squareone': minor +--- + +Users can now download the Jupyter Notebook (ipynb) file that they are viewing, with the current parameters filled in. This enables further interactive exploration. diff --git a/.changeset/smart-pandas-deny.md b/.changeset/smart-pandas-deny.md new file mode 100644 index 00000000..e3a0e9dd --- /dev/null +++ b/.changeset/smart-pandas-deny.md @@ -0,0 +1,5 @@ +--- +'squareone': minor +--- + +Times Square notebook pages show a link to the source notebook on GitHub. diff --git a/apps/squareone/package.json b/apps/squareone/package.json index 4e752ad5..50c76929 100644 --- a/apps/squareone/package.json +++ b/apps/squareone/package.json @@ -37,6 +37,7 @@ "dependencies": { "@fontsource/source-sans-pro": "^4.5.11", "@fortawesome/fontawesome-svg-core": "^6.3.0", + "@fortawesome/free-brands-svg-icons": "^6.5.2", "@fortawesome/free-solid-svg-icons": "^6.3.0", "@fortawesome/react-fontawesome": "^0.2.0", "@lsst-sqre/global-css": "workspace:*", diff --git a/apps/squareone/src/components/TimesSquareGitHubPagePanel/GitHubEditLink.js b/apps/squareone/src/components/TimesSquareGitHubPagePanel/GitHubEditLink.js new file mode 100644 index 00000000..0e980bd6 --- /dev/null +++ b/apps/squareone/src/components/TimesSquareGitHubPagePanel/GitHubEditLink.js @@ -0,0 +1,25 @@ +import styled from 'styled-components'; +import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'; + +export default function GitHubEditLink({ owner, repository, sourcePath }) { + if (!owner || !repository || !sourcePath) { + return null; + } + + const editUrl = `https://github.com/${owner}/${repository}/blob/main/${sourcePath}`; + + return ( +

+ + + {owner}/{repository} + +

+ ); +} + +const StyledFontAwesomeIcon = styled(FontAwesomeIcon)` + margin-right: 0.2em; + font-size: 1em; + color: ${(props) => props.color || 'inherit'}; +`; diff --git a/apps/squareone/src/components/TimesSquareGitHubPagePanel/GitHubEditLink.stories.js b/apps/squareone/src/components/TimesSquareGitHubPagePanel/GitHubEditLink.stories.js new file mode 100644 index 00000000..079b475d --- /dev/null +++ b/apps/squareone/src/components/TimesSquareGitHubPagePanel/GitHubEditLink.stories.js @@ -0,0 +1,31 @@ +import React from 'react'; + +import GitHubEditLink from './GitHubEditLink'; + +export default { + component: GitHubEditLink, + title: 'Components/TimesSquare/GitHubEditLink', + parameters: { + viewport: { + viewports: { + sidebar: { + name: 'Sidebar', + styles: { + width: '280px', + height: '900px', + }, + }, + }, + }, + defaultViewport: 'sidebar', + }, +}; + +const Template = (args) => ; + +export const Default = Template.bind({}); +Default.args = { + owner: 'lsst-sqre', + repository: 'times-square-demo', + sourcePath: 'demo.ipynb', +}; diff --git a/apps/squareone/src/components/TimesSquareGitHubPagePanel/IpynbDownloadLink.js b/apps/squareone/src/components/TimesSquareGitHubPagePanel/IpynbDownloadLink.js new file mode 100644 index 00000000..99838a3f --- /dev/null +++ b/apps/squareone/src/components/TimesSquareGitHubPagePanel/IpynbDownloadLink.js @@ -0,0 +1,25 @@ +import styled from 'styled-components'; +import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'; + +export default function IpynbDownloadLink({ url, sourcePath }) { + // get the filename from the sourcePath + const filename = sourcePath.split('/').pop(); + + return ( + + + Download notebook + + + ); +} + +const StyledP = styled.p` + margin-top: 2rem; +`; + +const StyledFontAwesomeIcon = styled(FontAwesomeIcon)` + margin-right: 0.2em; + font-size: 1em; + color: ${(props) => props.color || 'inherit'}; +`; diff --git a/apps/squareone/src/components/TimesSquareGitHubPagePanel/TimesSquareGitHubPagePanel.js b/apps/squareone/src/components/TimesSquareGitHubPagePanel/TimesSquareGitHubPagePanel.js index d8ec2a6e..b6f852f4 100644 --- a/apps/squareone/src/components/TimesSquareGitHubPagePanel/TimesSquareGitHubPagePanel.js +++ b/apps/squareone/src/components/TimesSquareGitHubPagePanel/TimesSquareGitHubPagePanel.js @@ -11,11 +11,15 @@ import Head from 'next/head'; import Error from 'next/error'; import useTimesSquarePage from '../../hooks/useTimesSquarePage'; +import { TimesSquareUrlParametersContext } from '../TimesSquareUrlParametersProvider'; import TimesSquareParameters from '../TimesSquareParameters'; import ExecStats from './ExecStats'; +import GitHubEditLink from './GitHubEditLink'; +import IpynbDownloadLink from './IpynbDownloadLink'; export default function TimesSquareGitHubPagePanel({}) { const { publicRuntimeConfig } = getConfig(); + const { urlQueryString } = React.useContext(TimesSquareUrlParametersContext); const pageData = useTimesSquarePage(); if (pageData.loading) { @@ -27,6 +31,8 @@ export default function TimesSquareGitHubPagePanel({}) { const { title, description } = pageData; + const ipynbDownloadUrl = `${pageData.renderedIpynbUrl}?${urlQueryString}`; + return ( @@ -37,8 +43,19 @@ export default function TimesSquareGitHubPagePanel({}) { {description && (
)} + + + +
diff --git a/apps/squareone/src/hooks/useTimesSquarePage.js b/apps/squareone/src/hooks/useTimesSquarePage.js index fb203aea..79aba8ae 100644 --- a/apps/squareone/src/hooks/useTimesSquarePage.js +++ b/apps/squareone/src/hooks/useTimesSquarePage.js @@ -9,6 +9,15 @@ function useTimesSquarePage() { const { tsPageUrl } = React.useContext(TimesSquareUrlParametersContext); const { data, error } = useSWR(tsPageUrl, fetcher); + const githubInfo = data + ? { + owner: data.github.owner ? data.github.owner : null, + repository: data.github.repository ? data.github.repository : null, + sourcePath: data.github.source_path ? data.github.source_path : null, + sidecarPath: data.github.sidecar_path ? data.github.sidecar_path : null, + } + : { owner: null, repository: null, sourcePath: null, sidecarPath: null }; + return { error: error, loading: !error && !data, @@ -18,6 +27,8 @@ function useTimesSquarePage() { htmlUrl: data ? data.html_url : null, htmlStatusUrl: data ? data.html_status_url : null, htmlEventsUrl: data ? data.html_events_url : null, + renderedIpynbUrl: data ? data.rendered_url : null, + github: githubInfo, }; } diff --git a/apps/squareone/src/styles/icons.js b/apps/squareone/src/styles/icons.js index 790a3900..eac8a248 100644 --- a/apps/squareone/src/styles/icons.js +++ b/apps/squareone/src/styles/icons.js @@ -13,7 +13,9 @@ import { faCircleCheck, faCircleMinus, faCodeCommit, + faDownload, } from '@fortawesome/free-solid-svg-icons'; +import { faGithub } from '@fortawesome/free-brands-svg-icons'; // Add icons to the global Font Awesome library library.add(faAngleDown); @@ -24,3 +26,5 @@ library.add(faCircleXmark); library.add(faCircleCheck); library.add(faCircleMinus); library.add(faCodeCommit); +library.add(faDownload); +library.add(faGithub); diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 0fc7ff48..0fb39130 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -41,6 +41,9 @@ importers: '@fortawesome/fontawesome-svg-core': specifier: ^6.3.0 version: 6.3.0 + '@fortawesome/free-brands-svg-icons': + specifier: ^6.5.2 + version: 6.5.2 '@fortawesome/free-solid-svg-icons': specifier: ^6.3.0 version: 6.3.0 @@ -3605,6 +3608,12 @@ packages: requiresBuild: true dev: false + /@fortawesome/fontawesome-common-types@6.5.2: + resolution: {integrity: sha512-gBxPg3aVO6J0kpfHNILc+NMhXnqHumFxOmjYCFfOiLZfwhnnfhtsdA2hfJlDnj+8PjAs6kKQPenOTKj3Rf7zHw==} + engines: {node: '>=6'} + requiresBuild: true + dev: false + /@fortawesome/fontawesome-svg-core@6.3.0: resolution: {integrity: sha512-uz9YifyKlixV6AcKlOX8WNdtF7l6nakGyLYxYaCa823bEBqyj/U2ssqtctO38itNEwXb8/lMzjdoJ+aaJuOdrw==} engines: {node: '>=6'} @@ -3613,6 +3622,14 @@ packages: '@fortawesome/fontawesome-common-types': 6.3.0 dev: false + /@fortawesome/free-brands-svg-icons@6.5.2: + resolution: {integrity: sha512-zi5FNYdmKLnEc0jc0uuHH17kz/hfYTg4Uei0wMGzcoCL/4d3WM3u1VMc0iGGa31HuhV5i7ZK8ZlTCQrHqRHSGQ==} + engines: {node: '>=6'} + requiresBuild: true + dependencies: + '@fortawesome/fontawesome-common-types': 6.5.2 + dev: false + /@fortawesome/free-solid-svg-icons@6.3.0: resolution: {integrity: sha512-x5tMwzF2lTH8pyv8yeZRodItP2IVlzzmBuD1M7BjawWgg9XAvktqJJ91Qjgoaf8qJpHQ8FEU9VxRfOkLhh86QA==} engines: {node: '>=6'}