Skip to content

Commit

Permalink
Merge pull request #38 from jbouder/public-sharing
Browse files Browse the repository at this point in the history
Update UI for Public Sharing
  • Loading branch information
aktech authored Jan 5, 2024
2 parents 06ca96e + 34e07a0 commit b6000f5
Show file tree
Hide file tree
Showing 18 changed files with 199 additions and 22 deletions.
2 changes: 1 addition & 1 deletion jhub_apps/static/css/index.css

Large diffs are not rendered by default.

36 changes: 18 additions & 18 deletions jhub_apps/static/js/index.js

Large diffs are not rendered by default.

4 changes: 4 additions & 0 deletions jhub_apps/templates/style.css
Original file line number Diff line number Diff line change
Expand Up @@ -101,6 +101,10 @@ label {
outline-color: var(--primary-color) !important;
}

.toggle-body-on {
background-color: var(--primary-color) !important;
}

a {
color: var(--link-text-color);
text-decoration: underline;
Expand Down
1 change: 1 addition & 0 deletions ui/src/components/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,3 +12,4 @@ export type { SelectOption } from './select/select';
export { default as Tag } from './tag/tag';
export { default as TextArea } from './text-area/text-area';
export { default as TextInput } from './text-input/text-input';
export { default as Toggle } from './toggle/toggle';
30 changes: 30 additions & 0 deletions ui/src/components/toggle/toggle.test.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
import '@testing-library/jest-dom';
import { act, fireEvent, render } from '@testing-library/react';
import { Toggle } from './toggle';

describe('Toggle', () => {
test('renders a default toggle successfully', () => {
const { baseElement } = render(<Toggle id="default-toggle" />);
const input = baseElement.querySelector('input');

expect(input).toBeInTheDocument();
});

test('renders a toggle with label', () => {
const { baseElement } = render(<Toggle id="label-toggle" label="label" />);
const label = baseElement.querySelector('label');

expect(label).toBeInTheDocument();
});

test('fires event callback when changed', async () => {
const { baseElement } = render(<Toggle id="clickable-toggle" />);
const input = baseElement.querySelector('input') as HTMLInputElement;
const body = baseElement.querySelector('.toggle-body') as HTMLInputElement;
expect(body).not.toHaveClass('btn-primary');

await act(async () => {
fireEvent.click(input);
});
});
});
74 changes: 74 additions & 0 deletions ui/src/components/toggle/toggle.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,74 @@
import { ChangeEvent, ChangeEventHandler, useEffect, useState } from 'react';

export interface ToggleProps {
/**
* The unique identifier for this component
*/
id: string;
/**
* The name of the text input
*/
name?: string;
/**
* Whether the toggle is checked or not
*/
checked?: boolean;
/**
* A label to display with the toggle
*/
label?: string;
/**
* Custom callback for when input is changed
*/
onChange?: ChangeEventHandler<HTMLInputElement>;
}

export const Toggle = ({
id,
name,
checked = false,
label,
onChange,
}: ToggleProps) => {
const [isChecked, setIsChecked] = useState(false);
const toggleHandler = (event: ChangeEvent<HTMLInputElement>) => {
setIsChecked(!isChecked);
if (onChange) {
onChange(event);
}
};

useEffect(() => {
setIsChecked(checked);
}, [checked]);

return (
<div className="toggle flex items-center">
<label htmlFor={id} className="flex items-center cursor-pointer">
<div className="relative">
<input
type="checkbox"
id={id}
name={name}
className="sr-only"
checked={isChecked}
onChange={toggleHandler}
/>
<div
className={`toggle-body w-12 h-6 rounded-full shadow-inner ${
isChecked ? 'toggle-body-on' : ''
}`}
></div>
<div
className={`toggle-dot absolute w-6 h-6 rounded-full shadow inset-y-0 left-0 ${
isChecked ? 'ml-6' : 'ml-0'
}`}
></div>
</div>
{label && <span className="toggle-label ml-3">{label}</span>}
</label>
</div>
);
};

export default Toggle;
1 change: 1 addition & 0 deletions ui/src/data/api.ts
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,7 @@ export const app: AppQueryGetProps = {
custom_command: '',
conda_env: '',
profile: '',
public: false,
},
progress_url: '',
state: {},
Expand Down
6 changes: 6 additions & 0 deletions ui/src/data/jupyterhub.ts
Original file line number Diff line number Diff line change
Expand Up @@ -90,6 +90,7 @@ export const apps: JhApp[] = [
url: '/hub/app1',
shared: false,
ready: true,
public: true,
},
{
id: '2',
Expand All @@ -102,6 +103,7 @@ export const apps: JhApp[] = [
url: '/hub/app2',
shared: false,
ready: false,
public: false,
},
{
id: '3',
Expand All @@ -112,6 +114,7 @@ export const apps: JhApp[] = [
url: '/hub/app3',
shared: false,
ready: false,
public: false,
},
{
id: '4',
Expand All @@ -122,6 +125,7 @@ export const apps: JhApp[] = [
url: '/hub/app4',
shared: false,
ready: false,
public: false,
},
{
id: '5',
Expand All @@ -132,6 +136,7 @@ export const apps: JhApp[] = [
url: '/hub/app5',
shared: false,
ready: false,
public: false,
},
{
id: '6',
Expand All @@ -142,6 +147,7 @@ export const apps: JhApp[] = [
url: '/hub/app6',
shared: true,
ready: false,
public: false,
},
];

Expand Down
21 changes: 21 additions & 0 deletions ui/src/index.css
Original file line number Diff line number Diff line change
Expand Up @@ -119,6 +119,27 @@
min-height: 6rem;
}

.toggle {
margin-top: 0.5rem;
}

.toggle-dot {
background-color: theme('colors.white');
border: 1px solid theme('colors.gray-lighter');
}

.toggle-body {
background-color: theme('colors.gray-lighter');
}

.toggle-body-on {
background-color: #c316e9;
}

.toggle-label {
color: theme('colors.text-dark');
}

.text-red {
color: theme('colors.error');
}
Expand Down
7 changes: 7 additions & 0 deletions ui/src/pages/home/app-card/app-card.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ interface AppCardProps {
thumbnail?: string;
url: string;
ready?: boolean;
isPublic?: boolean;
}

export const AppCard = ({
Expand All @@ -30,6 +31,7 @@ export const AppCard = ({
thumbnail,
framework,
url,
isPublic = false,
}: AppCardProps): React.ReactElement => {
const queryClient = useQueryClient();
const [submitting, setSubmitting] = useState(false);
Expand Down Expand Up @@ -149,6 +151,11 @@ export const AppCard = ({
</div>
<div className="card-footer">
<Tag id={`tag-${id}`}>{framework}</Tag>
{isPublic ? (
<Tag id={`tag-${id}`} className="ml-2 bg-warning-light">
Public
</Tag>
) : undefined}
</div>
</div>
);
Expand Down
23 changes: 23 additions & 0 deletions ui/src/pages/home/app-form/app-form.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ import {
Select,
TextArea,
TextInput,
Toggle,
} from '../../../components';
import { currentNotification } from '../../../store';

Expand All @@ -43,6 +44,7 @@ export const AppForm = ({
);
const [name, setName] = useState('');
const [currentFile, setCurrentFile] = useState<File>();
const [isPublic, setIsPublic] = useState(false);
// Get the app data if we're editing an existing app
const { data: formData, error: formError } = useQuery<
AppQueryGetProps,
Expand Down Expand Up @@ -99,6 +101,7 @@ export const AppForm = ({
conda_env: '',
custom_command: '',
profile: '',
is_public: false,
},
});
const currentFramework = watch('framework');
Expand Down Expand Up @@ -126,6 +129,7 @@ export const AppForm = ({
conda_env: conda_env || '',
custom_command: custom_command || '',
profile: profile || '',
public: isPublic,
},
};

Expand Down Expand Up @@ -216,6 +220,7 @@ export const AppForm = ({
if (formData?.name && formData?.user_options) {
setName(formData.name);
reset({ ...formData.user_options });
setIsPublic(formData.user_options.public);
}
}, [formData?.name, formData?.user_options, reset]);

Expand Down Expand Up @@ -424,6 +429,24 @@ export const AppForm = ({
) : (
<></>
)}
<FormGroup>
<Label htmlFor="is_public">Allow Public Access</Label>
<Controller
name="is_public"
control={control}
// eslint-disable-next-line @typescript-eslint/no-unused-vars
render={({ field: { ref: _, value, onChange, ...field } }) => (
<Toggle
{...field}
id="is_public"
checked={isPublic}
onChange={() => {
setIsPublic(!isPublic);
}}
/>
)}
/>
</FormGroup>
<ButtonGroup>
<Button
id="cancel-btn"
Expand Down
1 change: 1 addition & 0 deletions ui/src/pages/home/apps-grid/apps-grid.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -88,6 +88,7 @@ export const AppsGrid = ({
framework={app.framework}
url={app.url}
ready={app.ready}
isPublic={app.public}
/>
))}
</div>
Expand Down
1 change: 1 addition & 0 deletions ui/src/types/api.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ export interface UserOptions {
custom_command: string;
conda_env: string;
profile: string;
public: boolean;
}

export interface AppQueryUpdateProps {
Expand Down
1 change: 1 addition & 0 deletions ui/src/types/form.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,4 +13,5 @@ export interface AppFormInput {
conda_env?: string;
custom_command?: string;
profile?: string;
is_public: boolean;
}
1 change: 1 addition & 0 deletions ui/src/types/jupyterhub.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ export interface JhApp {
thumbnail?: string;
shared: boolean;
ready: boolean;
public: boolean;
}

export interface JhService {
Expand Down
1 change: 1 addition & 0 deletions ui/src/utils/jupyterhub.ts
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,7 @@ export const getApps = (servers: any, appType: string) => {
thumbnail: app.thumbnail,
shared: false,
ready: app.ready,
public: app.public,
});
}
}
Expand Down
2 changes: 1 addition & 1 deletion ui/tailwind.config.js
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ export default {
success: '#00a91c',
'success-light': '#ecf3ec',
warning: '#ffbe2e',
'warning-light': '#faf3d1',
'warning-light': '#fde68a',
error: '#d54309',
'error-light': '#f4e3db',
info: '#00bde3',
Expand Down
9 changes: 7 additions & 2 deletions ui/vite.config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -81,8 +81,9 @@ export default defineConfig({
filepath: '',
framework: 'panel',
custom_command: '',
conda_env: '',
profile: '',
conda_env: 'env1',
profile: 'Small Instance',
public: true,
},
progress_url: '',
state: {},
Expand All @@ -109,6 +110,7 @@ export default defineConfig({
'https://designsystem.digital.gov/img/introducing-uswds-2-0/built-to-grow--alt.jpg',
framework: 'Panel',
ready: true,
public: true,
},
},
'test-app-2': {
Expand All @@ -124,6 +126,7 @@ export default defineConfig({
'https://designsystem.digital.gov/img/introducing-uswds-2-0/built-to-grow--alt.jpg',
framework: 'Streamlit',
ready: false,
public: false,
},
},
'test-app-3': {
Expand All @@ -138,6 +141,7 @@ export default defineConfig({
imgUrl: null,
framework: 'JupyterLab',
ready: false,
public: false,
},
},
'test-app-4': {
Expand All @@ -151,6 +155,7 @@ export default defineConfig({
imgUrl: null,
framework: 'Streamlit',
ready: false,
public: false,
},
},
}),
Expand Down

0 comments on commit b6000f5

Please sign in to comment.