Move <UserSettings/> to a user-settings plugin
This commit is contained in:
@@ -0,0 +1,3 @@
|
||||
module.exports = {
|
||||
extends: [require.resolve('@backstage/cli/config/eslint')],
|
||||
};
|
||||
@@ -0,0 +1,13 @@
|
||||
# user-settings
|
||||
|
||||
Welcome to the user-settings plugin!
|
||||
|
||||
_This plugin was created through the Backstage CLI_
|
||||
|
||||
## Getting started
|
||||
|
||||
Your plugin has been added to the example app in this repository, meaning you'll be able to access it by running `yarn start` in the root directory, and then navigating to [/user-settings](http://localhost:3000/user-settings).
|
||||
|
||||
You can also serve the plugin in isolation by running `yarn start` in the plugin directory.
|
||||
This method of serving the plugin provides quicker iteration speed and a faster startup and hot reloads.
|
||||
It is only meant for local development, and the setup for it can be found inside the [/dev](./dev) directory.
|
||||
@@ -0,0 +1,19 @@
|
||||
/*
|
||||
* Copyright 2020 Spotify AB
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
import { createDevApp } from '@backstage/dev-utils';
|
||||
import { plugin } from '../src/plugin';
|
||||
|
||||
createDevApp().registerPlugin(plugin).render();
|
||||
@@ -0,0 +1,46 @@
|
||||
{
|
||||
"name": "@backstage/plugin-user-settings",
|
||||
"version": "0.1.1-alpha.21",
|
||||
"main": "src/index.ts",
|
||||
"types": "src/index.ts",
|
||||
"license": "Apache-2.0",
|
||||
"private": true,
|
||||
"publishConfig": {
|
||||
"access": "public",
|
||||
"main": "dist/index.esm.js",
|
||||
"types": "dist/index.d.ts"
|
||||
},
|
||||
"scripts": {
|
||||
"build": "backstage-cli plugin:build",
|
||||
"start": "backstage-cli plugin:serve",
|
||||
"lint": "backstage-cli lint",
|
||||
"test": "backstage-cli test",
|
||||
"diff": "backstage-cli plugin:diff",
|
||||
"prepack": "backstage-cli prepack",
|
||||
"postpack": "backstage-cli postpack",
|
||||
"clean": "backstage-cli clean"
|
||||
},
|
||||
"dependencies": {
|
||||
"@backstage/core": "^0.1.1-alpha.21",
|
||||
"@backstage/theme": "^0.1.1-alpha.21",
|
||||
"@material-ui/core": "^4.11.0",
|
||||
"@material-ui/icons": "^4.9.1",
|
||||
"@material-ui/lab": "4.0.0-alpha.45",
|
||||
"react": "^16.13.1",
|
||||
"react-dom": "^16.13.1",
|
||||
"react-use": "^15.3.3"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@backstage/cli": "^0.1.1-alpha.21",
|
||||
"@backstage/dev-utils": "^0.1.1-alpha.21",
|
||||
"@testing-library/jest-dom": "^5.10.1",
|
||||
"@testing-library/react": "^10.4.1",
|
||||
"@testing-library/user-event": "^12.0.7",
|
||||
"@types/jest": "^26.0.7",
|
||||
"@types/node": "^12.0.0",
|
||||
"jest-fetch-mock": "^3.0.3"
|
||||
},
|
||||
"files": [
|
||||
"dist"
|
||||
]
|
||||
}
|
||||
@@ -0,0 +1,26 @@
|
||||
/*
|
||||
* Copyright 2020 Spotify AB
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
import React from 'react';
|
||||
import { List, ListSubheader } from '@material-ui/core';
|
||||
import { SidebarThemeToggle } from './ThemeToggle';
|
||||
import { SidebarPinButton } from './PinButton';
|
||||
|
||||
export const AppSettingsList = () => (
|
||||
<List dense subheader={<ListSubheader>App Settings</ListSubheader>}>
|
||||
<SidebarThemeToggle />
|
||||
<SidebarPinButton />
|
||||
</List>
|
||||
);
|
||||
@@ -0,0 +1,28 @@
|
||||
/*
|
||||
* Copyright 2020 Spotify AB
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import React from 'react';
|
||||
import { List, ListSubheader } from '@material-ui/core';
|
||||
|
||||
type Props = {
|
||||
providerSettings: React.ReactNode;
|
||||
};
|
||||
|
||||
export const AuthProvidersList = ({ providerSettings }: Props) => (
|
||||
<List subheader={<ListSubheader>Available Auth Providers</ListSubheader>}>
|
||||
{providerSettings}
|
||||
</List>
|
||||
);
|
||||
@@ -0,0 +1,90 @@
|
||||
/*
|
||||
* Copyright 2020 Spotify AB
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
import {
|
||||
configApiRef,
|
||||
githubAuthApiRef,
|
||||
gitlabAuthApiRef,
|
||||
googleAuthApiRef,
|
||||
oauth2ApiRef,
|
||||
oktaAuthApiRef,
|
||||
microsoftAuthApiRef,
|
||||
samlAuthApiRef,
|
||||
useApi,
|
||||
} from '@backstage/core';
|
||||
import Star from '@material-ui/icons/Star';
|
||||
import React from 'react';
|
||||
import { OAuthProviderSettings } from './OAuthProviderSettings';
|
||||
import { OIDCProviderSettings } from './OIDCProviderSettings';
|
||||
|
||||
export const DefaultProviderSettings = () => {
|
||||
const configApi = useApi(configApiRef);
|
||||
const providersConfig = configApi.getOptionalConfig('auth.providers');
|
||||
const providers = providersConfig?.keys() ?? [];
|
||||
|
||||
return (
|
||||
<>
|
||||
{providers.includes('google') && (
|
||||
<ProviderSettingsItem
|
||||
title="Google"
|
||||
apiRef={googleAuthApiRef}
|
||||
icon={Star}
|
||||
/>
|
||||
)}
|
||||
{providers.includes('microsoft') && (
|
||||
<ProviderSettingsItem
|
||||
title="Microsoft"
|
||||
apiRef={microsoftAuthApiRef}
|
||||
icon={Star}
|
||||
/>
|
||||
)}
|
||||
{providers.includes('github') && (
|
||||
<ProviderSettingsItem
|
||||
title="Github"
|
||||
apiRef={githubAuthApiRef}
|
||||
icon={Star}
|
||||
/>
|
||||
)}
|
||||
{providers.includes('gitlab') && (
|
||||
<ProviderSettingsItem
|
||||
title="Gitlab"
|
||||
apiRef={gitlabAuthApiRef}
|
||||
icon={Star}
|
||||
/>
|
||||
)}
|
||||
{providers.includes('okta') && (
|
||||
<ProviderSettingsItem
|
||||
title="Okta"
|
||||
apiRef={oktaAuthApiRef}
|
||||
icon={Star}
|
||||
/>
|
||||
)}
|
||||
{providers.includes('saml') && (
|
||||
<ProviderSettingsItem
|
||||
title="SAML"
|
||||
apiRef={samlAuthApiRef}
|
||||
icon={Star}
|
||||
/>
|
||||
)}
|
||||
{providers.includes('oauth2') && (
|
||||
<ProviderSettingsItem
|
||||
title="YourOrg"
|
||||
apiRef={oauth2ApiRef}
|
||||
icon={Star}
|
||||
/>
|
||||
)}
|
||||
</>
|
||||
);
|
||||
};
|
||||
@@ -0,0 +1,69 @@
|
||||
/*
|
||||
* Copyright 2020 Spotify AB
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import React from 'react';
|
||||
import { FeatureFlagName, useApi, featureFlagsApiRef } from '@backstage/core';
|
||||
import {
|
||||
ListItem,
|
||||
ListItemSecondaryAction,
|
||||
ListItemText,
|
||||
Tooltip,
|
||||
} from '@material-ui/core';
|
||||
import CheckIcon from '@material-ui/icons/CheckCircle';
|
||||
import { ToggleButton } from '@material-ui/lab';
|
||||
|
||||
export type Item = {
|
||||
name: FeatureFlagName;
|
||||
pluginId: string;
|
||||
};
|
||||
|
||||
type Props = {
|
||||
featureFlag: Item;
|
||||
};
|
||||
|
||||
export const FlagItem = ({ featureFlag }: Props) => {
|
||||
const api = useApi(featureFlagsApiRef);
|
||||
|
||||
const [enabled, setEnabled] = React.useState(
|
||||
Boolean(api.getFlags().get(featureFlag.name)),
|
||||
);
|
||||
|
||||
const toggleFlag = () => {
|
||||
const newState = api.getFlags().toggle(featureFlag.name);
|
||||
setEnabled(Boolean(newState));
|
||||
};
|
||||
|
||||
return (
|
||||
<ListItem>
|
||||
<ListItemText
|
||||
primary={featureFlag.name}
|
||||
secondary={`Registered in ${featureFlag.pluginId} plugin`}
|
||||
/>
|
||||
<ListItemSecondaryAction>
|
||||
<ToggleButton
|
||||
size="small"
|
||||
value="flag"
|
||||
selected={enabled}
|
||||
onChange={toggleFlag}
|
||||
>
|
||||
<Tooltip placement="top" arrow title={enabled ? 'Disable' : 'Enable'}>
|
||||
<CheckIcon />
|
||||
</Tooltip>
|
||||
</ToggleButton>
|
||||
</ListItemSecondaryAction>
|
||||
</ListItem>
|
||||
);
|
||||
};
|
||||
@@ -0,0 +1,32 @@
|
||||
/*
|
||||
* Copyright 2020 Spotify AB
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import React from 'react';
|
||||
import List from '@material-ui/core/List';
|
||||
import ListSubheader from '@material-ui/core/ListSubheader';
|
||||
import { FlagItem, Item } from './FeatureFlagsItem';
|
||||
|
||||
type Props = {
|
||||
featureFlags: Item[];
|
||||
};
|
||||
|
||||
export const FeatureFlagsList = ({ featureFlags }: Props) => (
|
||||
<List dense subheader={<ListSubheader>Feature Flags</ListSubheader>}>
|
||||
{featureFlags.map(featureFlag => (
|
||||
<FlagItem key={featureFlag.name} featureFlag={featureFlag} />
|
||||
))}
|
||||
</List>
|
||||
);
|
||||
@@ -0,0 +1,80 @@
|
||||
/*
|
||||
* Copyright 2020 Spotify AB
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import {
|
||||
ApiRef,
|
||||
OAuthApi,
|
||||
SessionStateApi,
|
||||
useApi,
|
||||
Subscription,
|
||||
IconComponent,
|
||||
SessionState,
|
||||
} from '@backstage/core';
|
||||
import React, { FC, useState, useEffect } from 'react';
|
||||
import { ProviderSettingsItem } from './ProviderSettingsItem';
|
||||
|
||||
type OAuthProviderSidebarProps = {
|
||||
title: string;
|
||||
icon: IconComponent;
|
||||
apiRef: ApiRef<OAuthApi & SessionStateApi>;
|
||||
};
|
||||
|
||||
export const OAuthProviderSettings: FC<OAuthProviderSidebarProps> = ({
|
||||
title,
|
||||
icon,
|
||||
apiRef,
|
||||
}) => {
|
||||
const api = useApi(apiRef);
|
||||
const [signedIn, setSignedIn] = useState(false);
|
||||
|
||||
useEffect(() => {
|
||||
let didCancel = false;
|
||||
|
||||
const checkSession = async () => {
|
||||
const session = await api.getAccessToken('', { optional: true });
|
||||
if (!didCancel) {
|
||||
setSignedIn(!!session);
|
||||
}
|
||||
};
|
||||
let subscription: Subscription;
|
||||
const observeSession = () => {
|
||||
subscription = api
|
||||
.sessionState$()
|
||||
.subscribe((sessionState: SessionState) => {
|
||||
if (!didCancel) {
|
||||
setSignedIn(sessionState === SessionState.SignedIn);
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
checkSession();
|
||||
observeSession();
|
||||
return () => {
|
||||
didCancel = true;
|
||||
subscription.unsubscribe();
|
||||
};
|
||||
}, [api]);
|
||||
|
||||
return (
|
||||
<ProviderSettingsItem
|
||||
title={title}
|
||||
icon={icon}
|
||||
signedIn={signedIn}
|
||||
api={api}
|
||||
signInHandler={() => api.getAccessToken()}
|
||||
/>
|
||||
);
|
||||
};
|
||||
@@ -0,0 +1,81 @@
|
||||
/*
|
||||
* Copyright 2020 Spotify AB
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import {
|
||||
ApiRef,
|
||||
OpenIdConnectApi,
|
||||
SessionStateApi,
|
||||
useApi,
|
||||
Subscription,
|
||||
IconComponent,
|
||||
SessionState,
|
||||
} from '@backstage/core';
|
||||
import React, { FC, useState, useEffect } from 'react';
|
||||
import { ProviderSettingsItem } from './ProviderSettingsItem';
|
||||
|
||||
export type OIDCProviderSidebarProps = {
|
||||
title: string;
|
||||
icon: IconComponent;
|
||||
apiRef: ApiRef<OpenIdConnectApi & SessionStateApi>;
|
||||
};
|
||||
|
||||
export const OIDCProviderSettings: FC<OIDCProviderSidebarProps> = ({
|
||||
title,
|
||||
icon,
|
||||
apiRef,
|
||||
}) => {
|
||||
const api = useApi(apiRef);
|
||||
const [signedIn, setSignedIn] = useState(false);
|
||||
|
||||
useEffect(() => {
|
||||
let didCancel = false;
|
||||
|
||||
const checkSession = async () => {
|
||||
const session = await api.getIdToken({ optional: true });
|
||||
if (!didCancel) {
|
||||
setSignedIn(!!session);
|
||||
}
|
||||
};
|
||||
|
||||
let subscription: Subscription;
|
||||
const observeSession = () => {
|
||||
subscription = api
|
||||
.sessionState$()
|
||||
.subscribe((sessionState: SessionState) => {
|
||||
if (!didCancel) {
|
||||
setSignedIn(sessionState === SessionState.SignedIn);
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
checkSession();
|
||||
observeSession();
|
||||
return () => {
|
||||
didCancel = true;
|
||||
subscription.unsubscribe();
|
||||
};
|
||||
}, [api]);
|
||||
|
||||
return (
|
||||
<ProviderSettingsItem
|
||||
title={title}
|
||||
icon={icon}
|
||||
signedIn={signedIn}
|
||||
api={api}
|
||||
signInHandler={() => api.getIdToken()}
|
||||
/>
|
||||
);
|
||||
};
|
||||
@@ -0,0 +1,64 @@
|
||||
/*
|
||||
* Copyright 2020 Spotify AB
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import React, { useContext } from 'react';
|
||||
import {
|
||||
ListItem,
|
||||
ListItemSecondaryAction,
|
||||
ListItemText,
|
||||
Tooltip,
|
||||
} from '@material-ui/core';
|
||||
import LockIcon from '@material-ui/icons/Lock';
|
||||
import LockOpenIcon from '@material-ui/icons/LockOpen';
|
||||
import { ToggleButton } from '@material-ui/lab';
|
||||
import { SidebarPinStateContext } from '@backstage/core';
|
||||
|
||||
export const SidebarPinButton = () => {
|
||||
const { isPinned, toggleSidebarPinState } = useContext(
|
||||
SidebarPinStateContext,
|
||||
);
|
||||
|
||||
const PinIcon = () => (
|
||||
<Tooltip
|
||||
placement="top"
|
||||
arrow
|
||||
title={`${isPinned ? 'Unpin' : 'Pin'} Sidebar`}
|
||||
>
|
||||
{isPinned ? <LockIcon /> : <LockOpenIcon />}
|
||||
</Tooltip>
|
||||
);
|
||||
|
||||
return (
|
||||
<ListItem>
|
||||
<ListItemText
|
||||
primary="Pin Sidebar"
|
||||
secondary="Prevent the sidebar from collapsing"
|
||||
/>
|
||||
<ListItemSecondaryAction>
|
||||
<ToggleButton
|
||||
size="small"
|
||||
value="pin"
|
||||
selected={isPinned}
|
||||
onChange={() => {
|
||||
toggleSidebarPinState();
|
||||
}}
|
||||
>
|
||||
<PinIcon />
|
||||
</ToggleButton>
|
||||
</ListItemSecondaryAction>
|
||||
</ListItem>
|
||||
);
|
||||
};
|
||||
@@ -0,0 +1,91 @@
|
||||
/*
|
||||
* Copyright 2020 Spotify AB
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import React from 'react';
|
||||
import { IconComponent, OAuthApi, OpenIdConnectApi } from '@backstage/core';
|
||||
import {
|
||||
ListItem,
|
||||
ListItemIcon,
|
||||
ListItemSecondaryAction,
|
||||
ListItemText,
|
||||
Tooltip,
|
||||
} from '@material-ui/core';
|
||||
import PowerButton from '@material-ui/icons/PowerSettingsNew';
|
||||
import { ToggleButton } from '@material-ui/lab';
|
||||
import {
|
||||
ApiRef,
|
||||
SessionApi,
|
||||
useApi,
|
||||
IconComponent,
|
||||
SessionState,
|
||||
} from '@backstage/core-api';
|
||||
|
||||
type OAuthProviderSidebarProps = {
|
||||
title: string;
|
||||
icon: IconComponent;
|
||||
apiRef: ApiRef<SessionApi>;
|
||||
};
|
||||
|
||||
export const ProviderSettingsItem: FC<OAuthProviderSidebarProps> = ({
|
||||
title,
|
||||
icon: Icon,
|
||||
apiRef,
|
||||
}) => {
|
||||
const api = useApi(apiRef);
|
||||
const [signedIn, setSignedIn] = useState(false);
|
||||
|
||||
useEffect(() => {
|
||||
let didCancel = false;
|
||||
|
||||
const subscription = api
|
||||
.sessionState$()
|
||||
.subscribe((sessionState: SessionState) => {
|
||||
if (!didCancel) {
|
||||
setSignedIn(sessionState === SessionState.SignedIn);
|
||||
}
|
||||
});
|
||||
|
||||
return () => {
|
||||
didCancel = true;
|
||||
subscription.unsubscribe();
|
||||
};
|
||||
}, [api]);
|
||||
|
||||
return (
|
||||
<ListItem>
|
||||
<ListItemIcon>
|
||||
<Icon />
|
||||
</ListItemIcon>
|
||||
<ListItemText primary={title} />
|
||||
<ListItemSecondaryAction>
|
||||
<ToggleButton
|
||||
size="small"
|
||||
value={title}
|
||||
selected={signedIn}
|
||||
onChange={() => (signedIn ? api.signOut() : api.signIn())}
|
||||
>
|
||||
<Tooltip
|
||||
placement="top"
|
||||
arrow
|
||||
title={signedIn ? `Sign out from ${title}` : `Sign in to ${title}`}
|
||||
>
|
||||
<PowerButton />
|
||||
</Tooltip>
|
||||
</ToggleButton>
|
||||
</ListItemSecondaryAction>
|
||||
</ListItem>
|
||||
);
|
||||
};
|
||||
@@ -0,0 +1,74 @@
|
||||
/*
|
||||
* Copyright 2020 Spotify AB
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import React from 'react';
|
||||
import {
|
||||
Card,
|
||||
CardContent,
|
||||
CardHeader,
|
||||
makeStyles,
|
||||
Divider,
|
||||
} from '@material-ui/core';
|
||||
import { AppSettingsList } from './AppSettingsList';
|
||||
import { AuthProvidersList } from './AuthProviderList';
|
||||
import { FeatureFlagsList } from './FeatureFlagsList';
|
||||
import { SignInAvatar } from './SignInAvatar';
|
||||
import { UserSettingsMenu } from './UserSettingsMenu';
|
||||
import { useUserProfile } from './useUserProfileInfo';
|
||||
import { useApi, featureFlagsApiRef } from '@backstage/core';
|
||||
|
||||
const useStyles = makeStyles({
|
||||
root: {
|
||||
minWidth: 400,
|
||||
},
|
||||
});
|
||||
|
||||
type Props = {
|
||||
providerSettings?: React.ReactNode;
|
||||
};
|
||||
|
||||
export const SettingsDialog = ({ providerSettings }: Props) => {
|
||||
const classes = useStyles();
|
||||
const { profile, displayName } = useUserProfile();
|
||||
const featureFlagsApi = useApi(featureFlagsApiRef);
|
||||
const featureFlags = featureFlagsApi.getRegisteredFlags();
|
||||
|
||||
return (
|
||||
<Card className={classes.root}>
|
||||
<CardHeader
|
||||
avatar={<SignInAvatar size={48} />}
|
||||
action={<UserSettingsMenu />}
|
||||
title={displayName}
|
||||
subheader={profile.email}
|
||||
/>
|
||||
<CardContent>
|
||||
<AppSettingsList />
|
||||
{providerSettings && (
|
||||
<>
|
||||
<Divider />
|
||||
<AuthProvidersList providerSettings={providerSettings} />
|
||||
</>
|
||||
)}
|
||||
{featureFlags.length > 0 && (
|
||||
<>
|
||||
<Divider />
|
||||
<FeatureFlagsList featureFlags={featureFlags} />
|
||||
</>
|
||||
)}
|
||||
</CardContent>
|
||||
</Card>
|
||||
);
|
||||
};
|
||||
@@ -0,0 +1,42 @@
|
||||
/*
|
||||
* Copyright 2020 Spotify AB
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import React from 'react';
|
||||
import { BackstageTheme } from '@backstage/theme';
|
||||
import { makeStyles, Avatar } from '@material-ui/core';
|
||||
import { useUserProfile } from './useUserProfileInfo';
|
||||
import { sidebarConfig } from '@backstage/core';
|
||||
|
||||
const useStyles = makeStyles<BackstageTheme, { size: number }>({
|
||||
avatar: {
|
||||
width: ({ size }) => size,
|
||||
height: ({ size }) => size,
|
||||
},
|
||||
});
|
||||
|
||||
type Props = { size?: number };
|
||||
|
||||
export const SignInAvatar = ({ size }: Props) => {
|
||||
const { iconSize } = sidebarConfig;
|
||||
const classes = useStyles(size ? { size } : { size: iconSize });
|
||||
const { profile, displayName } = useUserProfile();
|
||||
|
||||
return (
|
||||
<Avatar src={profile.picture} className={classes.avatar}>
|
||||
{displayName[0]}
|
||||
</Avatar>
|
||||
);
|
||||
};
|
||||
@@ -0,0 +1,87 @@
|
||||
/*
|
||||
* Copyright 2020 Spotify AB
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import React from 'react';
|
||||
import { useObservable } from 'react-use';
|
||||
import LightIcon from '@material-ui/icons/WbSunny';
|
||||
import DarkIcon from '@material-ui/icons/Brightness2';
|
||||
import AutoIcon from '@material-ui/icons/BrightnessAuto';
|
||||
import { appThemeApiRef, useApi } from '@backstage/core';
|
||||
import ToggleButton from '@material-ui/lab/ToggleButton';
|
||||
import ToggleButtonGroup from '@material-ui/lab/ToggleButtonGroup';
|
||||
import {
|
||||
ListItem,
|
||||
ListItemText,
|
||||
ListItemSecondaryAction,
|
||||
Tooltip,
|
||||
} from '@material-ui/core';
|
||||
|
||||
export const SidebarThemeToggle = () => {
|
||||
const appThemeApi = useApi(appThemeApiRef);
|
||||
const themeId = useObservable(
|
||||
appThemeApi.activeThemeId$(),
|
||||
appThemeApi.getActiveThemeId(),
|
||||
);
|
||||
|
||||
const themeIds = appThemeApi.getInstalledThemes();
|
||||
// TODO(marcuseide): can these be put on the theme itself?
|
||||
const themeIcons = {
|
||||
dark: <DarkIcon />,
|
||||
light: <LightIcon />,
|
||||
};
|
||||
|
||||
const handleSetTheme = (
|
||||
_event: React.MouseEvent<HTMLElement>,
|
||||
newThemeId: string | undefined,
|
||||
) => {
|
||||
if (themeIds.some(t => t.id === newThemeId)) {
|
||||
appThemeApi.setActiveThemeId(newThemeId);
|
||||
} else {
|
||||
appThemeApi.setActiveThemeId(undefined);
|
||||
}
|
||||
};
|
||||
|
||||
return (
|
||||
<ListItem>
|
||||
<ListItemText primary="Theme" secondary="Change the theme mode" />
|
||||
<ListItemSecondaryAction>
|
||||
<ToggleButtonGroup
|
||||
exclusive
|
||||
size="small"
|
||||
value={themeId ?? 'auto'}
|
||||
onChange={handleSetTheme}
|
||||
>
|
||||
{themeIds.map(theme => (
|
||||
<ToggleButton key={theme.id} value={theme.variant}>
|
||||
<Tooltip
|
||||
placement="top"
|
||||
arrow
|
||||
title={`Select ${theme.variant} theme`}
|
||||
>
|
||||
{themeIcons[theme.variant]}
|
||||
</Tooltip>
|
||||
</ToggleButton>
|
||||
))}
|
||||
<ToggleButton value="auto">
|
||||
<Tooltip placement="top" arrow title="Select auto theme">
|
||||
<AutoIcon />
|
||||
</Tooltip>
|
||||
</ToggleButton>
|
||||
</ToggleButtonGroup>
|
||||
</ListItemSecondaryAction>
|
||||
</ListItem>
|
||||
);
|
||||
};
|
||||
@@ -0,0 +1,76 @@
|
||||
/*
|
||||
* Copyright 2020 Spotify AB
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import React, { useEffect, useContext } from 'react';
|
||||
import { Popover } from '@material-ui/core';
|
||||
import { SignInAvatar } from './SignInAvatar';
|
||||
import { SettingsDialog } from './SettingsDialog';
|
||||
import { SidebarItem, SidebarContext } from '@backstage/core';
|
||||
import { useUserProfile } from './useUserProfileInfo';
|
||||
|
||||
type Props = {
|
||||
providerSettings?: React.ReactNode;
|
||||
};
|
||||
|
||||
export const UserSettings = ({ providerSettings }: Props) => {
|
||||
const { isOpen: sidebarOpen } = useContext(SidebarContext);
|
||||
const { displayName } = useUserProfile();
|
||||
const [open, setOpen] = React.useState(false);
|
||||
const [anchorEl, setAnchorEl] = React.useState<HTMLButtonElement | undefined>(
|
||||
undefined,
|
||||
);
|
||||
|
||||
const handleOpen = (event?: React.MouseEvent<HTMLButtonElement>) => {
|
||||
setAnchorEl(event?.currentTarget ?? undefined);
|
||||
setOpen(true);
|
||||
};
|
||||
|
||||
const handleClose = () => {
|
||||
setAnchorEl(undefined);
|
||||
setOpen(false);
|
||||
};
|
||||
|
||||
useEffect(() => {
|
||||
if (!sidebarOpen && open) setOpen(false);
|
||||
}, [open, sidebarOpen]);
|
||||
|
||||
const SidebarAvatar = () => <SignInAvatar />;
|
||||
|
||||
return (
|
||||
<>
|
||||
<SidebarItem
|
||||
text={displayName}
|
||||
onClick={handleOpen}
|
||||
icon={SidebarAvatar}
|
||||
/>
|
||||
<Popover
|
||||
open={open}
|
||||
anchorEl={anchorEl}
|
||||
onClose={handleClose}
|
||||
anchorOrigin={{
|
||||
vertical: 'center',
|
||||
horizontal: 'center',
|
||||
}}
|
||||
transformOrigin={{
|
||||
vertical: 'bottom',
|
||||
horizontal: 'left',
|
||||
}}
|
||||
>
|
||||
<SettingsDialog providerSettings={providerSettings} />
|
||||
</Popover>
|
||||
</>
|
||||
);
|
||||
};
|
||||
@@ -0,0 +1,55 @@
|
||||
/*
|
||||
* Copyright 2020 Spotify AB
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import React from 'react';
|
||||
import { identityApiRef, useApi } from '@backstage/core';
|
||||
import { IconButton, ListItemIcon, Menu, MenuItem } from '@material-ui/core';
|
||||
import SignOutIcon from '@material-ui/icons/MeetingRoom';
|
||||
import MoreVertIcon from '@material-ui/icons/MoreVert';
|
||||
|
||||
export const UserSettingsMenu = () => {
|
||||
const identityApi = useApi(identityApiRef);
|
||||
const [open, setOpen] = React.useState(false);
|
||||
const [anchorEl, setAnchorEl] = React.useState<undefined | HTMLElement>(
|
||||
undefined,
|
||||
);
|
||||
|
||||
const handleOpen = (event: React.MouseEvent<HTMLButtonElement>) => {
|
||||
setAnchorEl(event.currentTarget);
|
||||
setOpen(true);
|
||||
};
|
||||
|
||||
const handleClose = () => {
|
||||
setAnchorEl(undefined);
|
||||
setOpen(false);
|
||||
};
|
||||
|
||||
return (
|
||||
<>
|
||||
<IconButton onClick={handleOpen}>
|
||||
<MoreVertIcon />
|
||||
</IconButton>
|
||||
<Menu anchorEl={anchorEl} open={open} onClose={handleClose}>
|
||||
<MenuItem onClick={() => identityApi.signOut()}>
|
||||
<ListItemIcon>
|
||||
<SignOutIcon />
|
||||
</ListItemIcon>
|
||||
Sign Out
|
||||
</MenuItem>
|
||||
</Menu>
|
||||
</>
|
||||
);
|
||||
};
|
||||
@@ -0,0 +1,17 @@
|
||||
/*
|
||||
* Copyright 2020 Spotify AB
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
export { UserSettings } from './UserSettings';
|
||||
@@ -0,0 +1,26 @@
|
||||
/*
|
||||
* Copyright 2020 Spotify AB
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import { useApi, identityApiRef } from '@backstage/core';
|
||||
|
||||
export const useUserProfile = () => {
|
||||
const identityApi = useApi(identityApiRef);
|
||||
const userId = identityApi.getUserId();
|
||||
const profile = identityApi.getProfile();
|
||||
const displayName = profile.displayName ?? userId;
|
||||
|
||||
return { profile, displayName };
|
||||
};
|
||||
@@ -0,0 +1,17 @@
|
||||
/*
|
||||
* Copyright 2020 Spotify AB
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
export { plugin } from './plugin';
|
||||
export { UserSettings } from './components/UserSettings';
|
||||
@@ -0,0 +1,22 @@
|
||||
/*
|
||||
* Copyright 2020 Spotify AB
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
import { plugin } from './plugin';
|
||||
|
||||
describe('user-settings', () => {
|
||||
it('should export plugin', () => {
|
||||
expect(plugin).toBeDefined();
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,30 @@
|
||||
/*
|
||||
* Copyright 2020 Spotify AB
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
import { createPlugin } from '@backstage/core';
|
||||
// import ExampleComponent from './components/ExampleComponent';
|
||||
|
||||
// export const rootRouteRef = createRouteRef({
|
||||
// path: '/user-settings',
|
||||
// title: 'user-settings',
|
||||
// });
|
||||
|
||||
export const plugin = createPlugin({
|
||||
id: 'user-settings',
|
||||
// apis: [createApiFactory(GCPApiRef, new GCPClient())],
|
||||
// register({ router }) {
|
||||
// router.addRoute(rootRouteRef, ExampleComponent);
|
||||
// },
|
||||
});
|
||||
@@ -0,0 +1,18 @@
|
||||
/*
|
||||
* Copyright 2020 Spotify AB
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
import '@testing-library/jest-dom';
|
||||
|
||||
require('jest-fetch-mock').enableMocks();
|
||||
Reference in New Issue
Block a user