Skip to content

Commit

Permalink
Merge pull request #55 from lsst-sqre/tickets/DM-34723
Browse files Browse the repository at this point in the history
DM-34723: Port from lsst-sqre/times-square-ui
  • Loading branch information
jonathansick authored May 11, 2022
2 parents e6e4603 + f004952 commit 313f115
Show file tree
Hide file tree
Showing 23 changed files with 483 additions and 6 deletions.
7 changes: 6 additions & 1 deletion CHANGELOG.rst
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,15 @@
Change log
##########

0.7.0 (unreleased)
==================

- Add support for `Times Square <https://github.com/lsst-sqre/times-square>`__.

0.6.0 (2022-04-14)
==================

- Informational broadcast messages are now displayed ith Rubin's primary teal as the background color (see `lsst-sqre/semaphore#29 <https://github.com/lsst-sqre/semaphore/pull/29>`__ for more information).
- Informational broadcast messages are now displayed with Rubin's primary teal as the background color (see `lsst-sqre/semaphore#29 <https://github.com/lsst-sqre/semaphore/pull/29>`__ for more information).
- Replaced custom fetch hook for the Semaphore broadcast message data with swr, enabling us to automatically refresh broadcast data.
- Updated the component layout in the source code.

Expand Down
18 changes: 18 additions & 0 deletions next.config.js
Original file line number Diff line number Diff line change
Expand Up @@ -52,10 +52,28 @@ module.exports = (phase, { defaultConfig }) => {
serverRuntimeConfig: { ...serverYamlConfig },
async rewrites() {
return [
// Mock Gafaelfawr (this is never triggered by a production ingress)
{
source: '/auth/api/v1/user-info',
destination: '/api/dev/user-info',
},
// Mock Times Square (this is never triggered by a production ingress)
{
source: '/times-square/api/v1/pages',
destination: '/api/dev/times-square/v1/pages',
},
{
source: '/times-square/api/v1/pages/:page/html',
destination: '/api/dev/times-square/v1/pages/:page/html',
},
{
source: '/times-square/api/v1/pages/:page/htmlstatus',
destination: '/api/dev/times-square/v1/pages/:page/htmlstatus',
},
{
source: '/times-square/api/v1/pages/:page',
destination: '/api/dev/times-square/v1/pages/:page',
},
];
},
};
Expand Down
4 changes: 2 additions & 2 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

7 changes: 6 additions & 1 deletion squareone.config.schema.json
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,12 @@
"semaphoreUrl": {
"type": "string",
"title": "Semaphore URL",
"description": "URL of the Semaphore API service for obtaining notifications and broadcasts"
"description": "URL prefix of the Semaphore API service for obtaining notifications and broadcasts. Does not end in /. Omit or set as null to disable Semaphore features."
},
"timesSquareUrl": {
"type": "string",
"title": "Times Square API URL",
"description": "URL prefix of the Times Square API service. Does not end in /. Omit or set as null to disable the /times-square/ pages."
}
}
}
1 change: 1 addition & 0 deletions squareone.config.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -3,3 +3,4 @@ baseUrl: 'http://localhost:3000'
siteDescription: |
The site description.
semaphoreUrl: 'https://data-dev.lsst.cloud/semaphore'
timesSquareUrl: 'http://localhost:3000/times-square/api'
2 changes: 1 addition & 1 deletion src/components/Page/Page.js
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,7 @@ export default function Page({ children, semaphoreUrl }) {
<div className="upper-container">
<Header />
<BroadcastBannerStack semaphoreUrl={semaphoreUrl} />
<MainContent>{children}</MainContent>
{children}
</div>
<div className="sticky-footer-container">
<Footer />
Expand Down
54 changes: 54 additions & 0 deletions src/components/TimesSquareViewer/TimesSquareViewer.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
/*
* The NotebookIframe controls the iframe with HTML content
* from Times Square with a notebook render.
*/

import styled from 'styled-components';

import useHtmlStatus from './useHtmlStatus';

const StyledIframe = styled.iframe`
--shadow-color: 0deg 0% 74%;
--shadow-elevation-medium: 0.1px 0.7px 0.9px hsl(var(--shadow-color) / 0.16),
0.4px 2.4px 3px -0.6px hsl(var(--shadow-color) / 0.2),
0.8px 5.3px 6.7px -1.1px hsl(var(--shadow-color) / 0.24),
1.9px 11.9px 15px -1.7px hsl(var(--shadow-color) / 0.28);
border: 0px solid black;
box-shadow: var(--shadow-elevation-medium);
width: 100%;
height: 100%;
`;

export default function NotebookIframe({
tsHtmlUrl,
tsHtmlStatusUrl,
parameters,
}) {
const htmlUrl = new URL(tsHtmlUrl);
parameters.forEach((item) => htmlUrl.searchParams.set(item[0], item[1]));

const htmlStatus = useHtmlStatus(tsHtmlStatusUrl, parameters);

if (htmlStatus.error) {
return (
<div>
<p>Error contacting API at {`${tsHtmlStatusUrl}`}</p>
</div>
);
}

if (htmlStatus.loading) {
return (
<div>
<p>Loading...</p>
</div>
);
}

return (
<StyledIframe
src={htmlStatus.htmlUrl || htmlUrl.toString()}
key={htmlStatus.iframeKey}
></StyledIframe>
);
}
2 changes: 2 additions & 0 deletions src/components/TimesSquareViewer/index.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
export * from './TimesSquareViewer';
export { default } from './TimesSquareViewer';
32 changes: 32 additions & 0 deletions src/components/TimesSquareViewer/useHtmlStatus.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
/*
* useHtmlStatus hook fetches data from the Times Square
* /v1/pages/:page/htmlstatus endpoint using the SWR hook to enable
* dynamic refreshing of data about a page's HTML rendering.
*/

import useSWR from 'swr';

const fetcher = (...args) => fetch(...args).then((res) => res.json());

function useHtmlStatus(htmlStatusUrl, parameters) {
const url = new URL(htmlStatusUrl);
parameters.forEach((item) => url.searchParams.set(item[0], item[1]));
const fullHtmlStatusUrl = url.toString();

const { data, error } = useSWR(fullHtmlStatusUrl, fetcher, {
// ping every 1 second while browser in focus.
// TODO back this off once HTML is loaded?
refreshInterval: 1000,
});

return {
error: error,
loading: !error && !data,
htmlAvailable: data ? data.available : false,
htmlHash: data ? data.html_hash : null,
htmlUrl: data ? data.html_url : null,
iframeKey: data && data.available ? data.html_hash : 'html-not-available',
};
}

export default useHtmlStatus;
6 changes: 5 additions & 1 deletion src/pages/_app.js
Original file line number Diff line number Diff line change
Expand Up @@ -24,11 +24,15 @@ import Page from '../components/Page';
library.add(faAngleDown);

function MyApp({ Component, pageProps, baseUrl, semaphoreUrl }) {
// Use the content layout defined by the page component, if avaialble.
// Otherwise, the page itself is used as the content area layout container.
const getLayout = Component.getLayout || ((page) => page);

/* eslint-disable react/jsx-props-no-spreading */
return (
<ThemeProvider defaultTheme="system">
<Page baseUrl={baseUrl} semaphoreUrl={semaphoreUrl}>
<Component {...pageProps} />
{getLayout(<Component {...pageProps} />)}
</Page>
</ThemeProvider>
);
Expand Down
6 changes: 6 additions & 0 deletions src/pages/api-aspect.js
Original file line number Diff line number Diff line change
@@ -1,7 +1,9 @@
import Head from 'next/head';
import getConfig from 'next/config';
import PropTypes from 'prop-types';

import { Lede } from '../components/Typography';
import MainContent from '../components/MainContent';

const pageDescription =
'Integrate Rubin data into your analysis tools with APIs.';
Expand Down Expand Up @@ -97,6 +99,10 @@ ApiAspectPage.propTypes = {
publicRuntimeConfig: PropTypes.object,
};

ApiAspectPage.getLayout = function getLayout(page) {
return <MainContent>{page}</MainContent>;
};

export async function getServerSideProps() {
const { serverRuntimeConfig, publicRuntimeConfig } = getConfig();
return {
Expand Down
27 changes: 27 additions & 0 deletions src/pages/api/dev/times-square/v1/pages.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
/*
* Mock Times Square API endpoint: /times-square/v1/pages
*
* This endpoint lists available pages.
*/

import getConfig from 'next/config';

export default function handler(req, res) {
const { publicRuntimeConfig } = getConfig();
const { timesSquareUrl } = publicRuntimeConfig;

const createPage = (name) => {
const pageBaseUrl = `${timesSquareUrl}/v1/pages/${name}`;
return {
name,
title: name,
self_url: pageBaseUrl,
};
};

const content = [createPage('mypage'), createPage('anotherpage')];

res.statusCode = 200;
res.setHeader('Content-Type', 'application/json');
res.end(JSON.stringify(content));
}
45 changes: 45 additions & 0 deletions src/pages/api/dev/times-square/v1/pages/[page].js
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
/*
* Mock Times Square API endpoint: /times-square/v1/pages/:page
*/
import getConfig from 'next/config';

export default function handler(req, res) {
const { page } = req.query;
const { publicRuntimeConfig } = getConfig();
const { timesSquareUrl } = publicRuntimeConfig;
const pageBaseUrl = `${timesSquareUrl}/v1/pages/${page}`;

if (page == 'not-found') {
// simulate a page that doesn't exist in the backend
res.statusCode = 404;
res.end();
return;
}

const content = {
name: page,
title: `Title for ${page}`,
description: '<p>This is the description.</p>',
self_url: pageBaseUrl,
source_url: `${pageBaseUrl}/source`,
rendered_url: `${pageBaseUrl}/rendered`,
html_url: `${pageBaseUrl}/html`,
html_status_url: `${pageBaseUrl}/htmlstatus`,
parameters: {
a: {
type: 'number',
default: 42,
description: 'A number.',
},
b: {
type: 'string',
default: 'Hello',
description: 'A string.',
},
},
};

res.statusCode = 200;
res.setHeader('Content-Type', 'application/json');
res.end(JSON.stringify(content));
}
28 changes: 28 additions & 0 deletions src/pages/api/dev/times-square/v1/pages/[page]/html.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
/*
* Mock Times Square API endpoint: /times-square/v1/pages/[page]/html
*/

const htmlContent = `
<!doctype html>
<html class="no-js" lang="">
<head>
<meta charset="utf-8">
<title>Test document</title>
</head>
<body>
<h1>Test content</h1>
<p>Hello world</p>
</body>
</html>
`;

export default function handler(req, res) {
const { page } = req.query;
console.log(req.url);

res.statusCode = 200;
res.setHeader('Content-Type', 'text/html');
res.end(htmlContent);
}
26 changes: 26 additions & 0 deletions src/pages/api/dev/times-square/v1/pages/[page]/htmlstatus.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
/*
* Mock Times Square API endpoint: /times-square/api/v1/pages/:page/htmlstatus
*/
import getConfig from 'next/config';

export default function handler(req, res) {
const { page, a } = req.query;
const { publicRuntimeConfig } = getConfig();
const { timesSquareUrl } = publicRuntimeConfig;

const pageBaseUrl = `${timesSquareUrl}/v1/pages/${page}`;

const content = {
available: a != '2', // magic value to toggle status modes
html_url: `${pageBaseUrl}/html?a={a}`,
html_hash: a != '2' ? '12345' : null,
};

console.log(content);

console.log('Pinged status');

res.statusCode = 200;
res.setHeader('Content-Type', 'application/json');
res.end(JSON.stringify(content));
}
5 changes: 5 additions & 0 deletions src/pages/docs.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import PropTypes from 'prop-types';
import styled from 'styled-components';
import Link from 'next/link';

import MainContent from '../components/MainContent';
import { Lede } from '../components/Typography';

const Section = styled.section`
Expand Down Expand Up @@ -199,6 +200,10 @@ DocsPage.propTypes = {
publicRuntimeConfig: PropTypes.object,
};

DocsPage.getLayout = function getLayout(page) {
return <MainContent>{page}</MainContent>;
};

export async function getServerSideProps() {
const { serverRuntimeConfig, publicRuntimeConfig } = getConfig();
return {
Expand Down
5 changes: 5 additions & 0 deletions src/pages/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import Head from 'next/head';
import getConfig from 'next/config';
import PropTypes from 'prop-types';

import MainContent from '../components/MainContent';
import HomepageHero from '../components/HomepageHero';

export default function Home({ publicRuntimeConfig }) {
Expand All @@ -20,6 +21,10 @@ Home.propTypes = {
publicRuntimeConfig: PropTypes.object,
};

Home.getLayout = function getLayout(page) {
return <MainContent>{page}</MainContent>;
};

export async function getServerSideProps() {
const { serverRuntimeConfig, publicRuntimeConfig } = getConfig();
return {
Expand Down
Loading

0 comments on commit 313f115

Please sign in to comment.