@backstage/plugin-user-settings 0.5.1 → 0.6.0-next.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.esm.js CHANGED
@@ -4,7 +4,7 @@ import ObservableImpl from 'zen-observable';
4
4
  import { createRouteRef, createPlugin, createRoutableExtension, useRouteRef, useApi, errorApiRef, SessionState, googleAuthApiRef, microsoftAuthApiRef, githubAuthApiRef, gitlabAuthApiRef, oktaAuthApiRef, bitbucketAuthApiRef, oneloginAuthApiRef, atlassianAuthApiRef, configApiRef, featureFlagsApiRef, FeatureFlagState, identityApiRef, alertApiRef, appThemeApiRef, attachComponentData, useElementFilter } from '@backstage/core-plugin-api';
5
5
  import React, { useState, useEffect, useCallback, cloneElement } from 'react';
6
6
  import SettingsIcon from '@material-ui/icons/Settings';
7
- import { SidebarItem, EmptyState, CodeSnippet, sidebarConfig, InfoCard, useSidebarPinState, Page, Header, TabbedLayout } from '@backstage/core-components';
7
+ import { SidebarItem, EmptyState, CodeSnippet, sidebarConfig, InfoCard, useSidebarPinState, Page, Header, RoutedTabs } from '@backstage/core-components';
8
8
  import { useOutlet } from 'react-router';
9
9
  import { Typography, Button, makeStyles, Avatar, ListItem, ListItemIcon, ListItemText, Tooltip, Grid, ListItemSecondaryAction, List, Switch, TextField, IconButton, Menu, MenuItem } from '@material-ui/core';
10
10
  import Star from '@material-ui/icons/Star';
@@ -174,7 +174,7 @@ const userSettingsPlugin = createPlugin({
174
174
  const UserSettingsPage = userSettingsPlugin.provide(
175
175
  createRoutableExtension({
176
176
  name: "UserSettingsPage",
177
- component: () => Promise.resolve().then(function () { return SettingsPage$1; }).then((m) => m.SettingsPage),
177
+ component: () => import('./esm/index-24ed3bf4.esm.js').then((m) => m.SettingsPage),
178
178
  mountPoint: settingsRouteRef
179
179
  })
180
180
  );
@@ -182,11 +182,7 @@ const UserSettingsPage = userSettingsPlugin.provide(
182
182
  const Settings = (props) => {
183
183
  const routePath = useRouteRef(settingsRouteRef);
184
184
  const Icon = props.icon ? props.icon : SettingsIcon;
185
- return /* @__PURE__ */ React.createElement(SidebarItem, {
186
- text: "Settings",
187
- to: routePath(),
188
- icon: Icon
189
- });
185
+ return /* @__PURE__ */ React.createElement(SidebarItem, { text: "Settings", to: routePath(), icon: Icon });
190
186
  };
191
187
 
192
188
  const EXAMPLE$1 = `auth:
@@ -196,24 +192,32 @@ const EXAMPLE$1 = `auth:
196
192
  clientId: \${AUTH_GOOGLE_CLIENT_ID}
197
193
  clientSecret: \${AUTH_GOOGLE_CLIENT_SECRET}
198
194
  `;
199
- const EmptyProviders = () => /* @__PURE__ */ React.createElement(EmptyState, {
200
- missing: "content",
201
- title: "No Authentication Providers",
202
- description: "You can add Authentication Providers to Backstage which allows you to use these providers to authenticate yourself.",
203
- action: /* @__PURE__ */ React.createElement(React.Fragment, null, /* @__PURE__ */ React.createElement(Typography, {
204
- variant: "body1"
205
- }, "Open ", /* @__PURE__ */ React.createElement("code", null, "app-config.yaml"), " and make the changes as highlighted below:"), /* @__PURE__ */ React.createElement(CodeSnippet, {
206
- text: EXAMPLE$1,
207
- language: "yaml",
208
- showLineNumbers: true,
209
- highlightedNumbers: [3, 4, 5, 6, 7, 8],
210
- customStyle: { background: "inherit", fontSize: "115%" }
211
- }), /* @__PURE__ */ React.createElement(Button, {
212
- variant: "contained",
213
- color: "primary",
214
- href: "https://backstage.io/docs/auth/add-auth-provider"
215
- }, "Read More"))
216
- });
195
+ const EmptyProviders = () => /* @__PURE__ */ React.createElement(
196
+ EmptyState,
197
+ {
198
+ missing: "content",
199
+ title: "No Authentication Providers",
200
+ description: "You can add Authentication Providers to Backstage which allows you to use these providers to authenticate yourself.",
201
+ action: /* @__PURE__ */ React.createElement(React.Fragment, null, /* @__PURE__ */ React.createElement(Typography, { variant: "body1" }, "Open ", /* @__PURE__ */ React.createElement("code", null, "app-config.yaml"), " and make the changes as highlighted below:"), /* @__PURE__ */ React.createElement(
202
+ CodeSnippet,
203
+ {
204
+ text: EXAMPLE$1,
205
+ language: "yaml",
206
+ showLineNumbers: true,
207
+ highlightedNumbers: [3, 4, 5, 6, 7, 8],
208
+ customStyle: { background: "inherit", fontSize: "115%" }
209
+ }
210
+ ), /* @__PURE__ */ React.createElement(
211
+ Button,
212
+ {
213
+ variant: "contained",
214
+ color: "primary",
215
+ href: "https://backstage.io/docs/auth/add-auth-provider"
216
+ },
217
+ "Read More"
218
+ ))
219
+ }
220
+ );
217
221
 
218
222
  const useStyles$2 = makeStyles((theme) => ({
219
223
  avatar: {
@@ -226,10 +230,7 @@ const useStyles$2 = makeStyles((theme) => ({
226
230
  const ProviderSettingsAvatar = ({ size, picture }) => {
227
231
  const { iconSize } = sidebarConfig;
228
232
  const classes = useStyles$2(size ? { size } : { size: iconSize });
229
- return /* @__PURE__ */ React.createElement(Avatar, {
230
- src: picture,
231
- className: classes.avatar
232
- });
233
+ return /* @__PURE__ */ React.createElement(Avatar, { src: picture, className: classes.avatar });
233
234
  };
234
235
 
235
236
  const ProviderSettingsItem = (props) => {
@@ -260,103 +261,110 @@ const ProviderSettingsItem = (props) => {
260
261
  subscription.unsubscribe();
261
262
  };
262
263
  }, [api]);
263
- return /* @__PURE__ */ React.createElement(ListItem, null, /* @__PURE__ */ React.createElement(ListItemIcon, null, /* @__PURE__ */ React.createElement(Icon, null)), /* @__PURE__ */ React.createElement(ListItemText, {
264
- primary: title,
265
- secondary: /* @__PURE__ */ React.createElement(Tooltip, {
264
+ return /* @__PURE__ */ React.createElement(ListItem, null, /* @__PURE__ */ React.createElement(ListItemIcon, null, /* @__PURE__ */ React.createElement(Icon, null)), /* @__PURE__ */ React.createElement(
265
+ ListItemText,
266
+ {
267
+ primary: title,
268
+ secondary: /* @__PURE__ */ React.createElement(Tooltip, { placement: "top", arrow: true, title: description }, /* @__PURE__ */ React.createElement("span", null, /* @__PURE__ */ React.createElement(Grid, { container: true, spacing: 6 }, /* @__PURE__ */ React.createElement(Grid, { item: true }, /* @__PURE__ */ React.createElement(ProviderSettingsAvatar, { size: 48, picture: profile.picture })), /* @__PURE__ */ React.createElement(Grid, { item: true, xs: 12, sm: true, container: true }, /* @__PURE__ */ React.createElement(Grid, { item: true, xs: true, container: true, direction: "column", spacing: 2 }, /* @__PURE__ */ React.createElement(Grid, { item: true, xs: true }, /* @__PURE__ */ React.createElement(
269
+ Typography,
270
+ {
271
+ variant: "subtitle1",
272
+ color: "textPrimary",
273
+ gutterBottom: true
274
+ },
275
+ profile.displayName
276
+ ), /* @__PURE__ */ React.createElement(Typography, { variant: "body2", color: "textSecondary" }, profile.email), /* @__PURE__ */ React.createElement(Typography, { variant: "body2", color: "textSecondary" }, description))))))),
277
+ secondaryTypographyProps: { noWrap: true, style: { width: "80%" } }
278
+ }
279
+ ), /* @__PURE__ */ React.createElement(ListItemSecondaryAction, null, /* @__PURE__ */ React.createElement(
280
+ Tooltip,
281
+ {
266
282
  placement: "top",
267
283
  arrow: true,
268
- title: description
269
- }, /* @__PURE__ */ React.createElement("span", null, /* @__PURE__ */ React.createElement(Grid, {
270
- container: true,
271
- spacing: 6
272
- }, /* @__PURE__ */ React.createElement(Grid, {
273
- item: true
274
- }, /* @__PURE__ */ React.createElement(ProviderSettingsAvatar, {
275
- size: 48,
276
- picture: profile.picture
277
- })), /* @__PURE__ */ React.createElement(Grid, {
278
- item: true,
279
- xs: 12,
280
- sm: true,
281
- container: true
282
- }, /* @__PURE__ */ React.createElement(Grid, {
283
- item: true,
284
- xs: true,
285
- container: true,
286
- direction: "column",
287
- spacing: 2
288
- }, /* @__PURE__ */ React.createElement(Grid, {
289
- item: true,
290
- xs: true
291
- }, /* @__PURE__ */ React.createElement(Typography, {
292
- variant: "subtitle1",
293
- color: "textPrimary",
294
- gutterBottom: true
295
- }, profile.displayName), /* @__PURE__ */ React.createElement(Typography, {
296
- variant: "body2",
297
- color: "textSecondary"
298
- }, profile.email), /* @__PURE__ */ React.createElement(Typography, {
299
- variant: "body2",
300
- color: "textSecondary"
301
- }, description))))))),
302
- secondaryTypographyProps: { noWrap: true, style: { width: "80%" } }
303
- }), /* @__PURE__ */ React.createElement(ListItemSecondaryAction, null, /* @__PURE__ */ React.createElement(Tooltip, {
304
- placement: "top",
305
- arrow: true,
306
- title: signedIn ? `Sign out from ${title}` : `Sign in to ${title}`
307
- }, /* @__PURE__ */ React.createElement(Button, {
308
- variant: "outlined",
309
- color: "primary",
310
- onClick: () => {
311
- const action = signedIn ? api.signOut() : api.signIn();
312
- action.catch((error) => errorApi.post(error));
313
- }
314
- }, signedIn ? `Sign out` : `Sign in`))));
284
+ title: signedIn ? `Sign out from ${title}` : `Sign in to ${title}`
285
+ },
286
+ /* @__PURE__ */ React.createElement(
287
+ Button,
288
+ {
289
+ variant: "outlined",
290
+ color: "primary",
291
+ onClick: () => {
292
+ const action = signedIn ? api.signOut() : api.signIn();
293
+ action.catch((error) => errorApi.post(error));
294
+ }
295
+ },
296
+ signedIn ? `Sign out` : `Sign in`
297
+ )
298
+ )));
315
299
  };
316
300
 
317
301
  const DefaultProviderSettings = (props) => {
318
302
  const { configuredProviders } = props;
319
- return /* @__PURE__ */ React.createElement(React.Fragment, null, configuredProviders.includes("google") && /* @__PURE__ */ React.createElement(ProviderSettingsItem, {
320
- title: "Google",
321
- description: "Provides authentication towards Google APIs and identities",
322
- apiRef: googleAuthApiRef,
323
- icon: Star
324
- }), configuredProviders.includes("microsoft") && /* @__PURE__ */ React.createElement(ProviderSettingsItem, {
325
- title: "Microsoft",
326
- description: "Provides authentication towards Microsoft APIs and identities",
327
- apiRef: microsoftAuthApiRef,
328
- icon: Star
329
- }), configuredProviders.includes("github") && /* @__PURE__ */ React.createElement(ProviderSettingsItem, {
330
- title: "GitHub",
331
- description: "Provides authentication towards GitHub APIs",
332
- apiRef: githubAuthApiRef,
333
- icon: Star
334
- }), configuredProviders.includes("gitlab") && /* @__PURE__ */ React.createElement(ProviderSettingsItem, {
335
- title: "GitLab",
336
- description: "Provides authentication towards GitLab APIs",
337
- apiRef: gitlabAuthApiRef,
338
- icon: Star
339
- }), configuredProviders.includes("okta") && /* @__PURE__ */ React.createElement(ProviderSettingsItem, {
340
- title: "Okta",
341
- description: "Provides authentication towards Okta APIs",
342
- apiRef: oktaAuthApiRef,
343
- icon: Star
344
- }), configuredProviders.includes("bitbucket") && /* @__PURE__ */ React.createElement(ProviderSettingsItem, {
345
- title: "Bitbucket",
346
- description: "Provides authentication towards Bitbucket APIs",
347
- apiRef: bitbucketAuthApiRef,
348
- icon: Star
349
- }), configuredProviders.includes("onelogin") && /* @__PURE__ */ React.createElement(ProviderSettingsItem, {
350
- title: "OneLogin",
351
- description: "Provides authentication towards OneLogin APIs",
352
- apiRef: oneloginAuthApiRef,
353
- icon: Star
354
- }), configuredProviders.includes("atlassian") && /* @__PURE__ */ React.createElement(ProviderSettingsItem, {
355
- title: "Atlassian",
356
- description: "Provides authentication towards Atlassian APIs",
357
- apiRef: atlassianAuthApiRef,
358
- icon: Star
359
- }));
303
+ return /* @__PURE__ */ React.createElement(React.Fragment, null, configuredProviders.includes("google") && /* @__PURE__ */ React.createElement(
304
+ ProviderSettingsItem,
305
+ {
306
+ title: "Google",
307
+ description: "Provides authentication towards Google APIs and identities",
308
+ apiRef: googleAuthApiRef,
309
+ icon: Star
310
+ }
311
+ ), configuredProviders.includes("microsoft") && /* @__PURE__ */ React.createElement(
312
+ ProviderSettingsItem,
313
+ {
314
+ title: "Microsoft",
315
+ description: "Provides authentication towards Microsoft APIs and identities",
316
+ apiRef: microsoftAuthApiRef,
317
+ icon: Star
318
+ }
319
+ ), configuredProviders.includes("github") && /* @__PURE__ */ React.createElement(
320
+ ProviderSettingsItem,
321
+ {
322
+ title: "GitHub",
323
+ description: "Provides authentication towards GitHub APIs",
324
+ apiRef: githubAuthApiRef,
325
+ icon: Star
326
+ }
327
+ ), configuredProviders.includes("gitlab") && /* @__PURE__ */ React.createElement(
328
+ ProviderSettingsItem,
329
+ {
330
+ title: "GitLab",
331
+ description: "Provides authentication towards GitLab APIs",
332
+ apiRef: gitlabAuthApiRef,
333
+ icon: Star
334
+ }
335
+ ), configuredProviders.includes("okta") && /* @__PURE__ */ React.createElement(
336
+ ProviderSettingsItem,
337
+ {
338
+ title: "Okta",
339
+ description: "Provides authentication towards Okta APIs",
340
+ apiRef: oktaAuthApiRef,
341
+ icon: Star
342
+ }
343
+ ), configuredProviders.includes("bitbucket") && /* @__PURE__ */ React.createElement(
344
+ ProviderSettingsItem,
345
+ {
346
+ title: "Bitbucket",
347
+ description: "Provides authentication towards Bitbucket APIs",
348
+ apiRef: bitbucketAuthApiRef,
349
+ icon: Star
350
+ }
351
+ ), configuredProviders.includes("onelogin") && /* @__PURE__ */ React.createElement(
352
+ ProviderSettingsItem,
353
+ {
354
+ title: "OneLogin",
355
+ description: "Provides authentication towards OneLogin APIs",
356
+ apiRef: oneloginAuthApiRef,
357
+ icon: Star
358
+ }
359
+ ), configuredProviders.includes("atlassian") && /* @__PURE__ */ React.createElement(
360
+ ProviderSettingsItem,
361
+ {
362
+ title: "Atlassian",
363
+ description: "Provides authentication towards Atlassian APIs",
364
+ apiRef: atlassianAuthApiRef,
365
+ icon: Star
366
+ }
367
+ ));
360
368
  };
361
369
 
362
370
  const UserSettingsAuthProviders = (props) => {
@@ -364,17 +372,11 @@ const UserSettingsAuthProviders = (props) => {
364
372
  const configApi = useApi(configApiRef);
365
373
  const providersConfig = configApi.getOptionalConfig("auth.providers");
366
374
  const configuredProviders = (providersConfig == null ? void 0 : providersConfig.keys()) || [];
367
- const providers = providerSettings != null ? providerSettings : /* @__PURE__ */ React.createElement(DefaultProviderSettings, {
368
- configuredProviders
369
- });
375
+ const providers = providerSettings != null ? providerSettings : /* @__PURE__ */ React.createElement(DefaultProviderSettings, { configuredProviders });
370
376
  if (!providerSettings && !(configuredProviders == null ? void 0 : configuredProviders.length)) {
371
377
  return /* @__PURE__ */ React.createElement(EmptyProviders, null);
372
378
  }
373
- return /* @__PURE__ */ React.createElement(InfoCard, {
374
- title: "Available Providers"
375
- }, /* @__PURE__ */ React.createElement(List, {
376
- dense: true
377
- }, providers));
379
+ return /* @__PURE__ */ React.createElement(InfoCard, { title: "Available Providers" }, /* @__PURE__ */ React.createElement(List, { dense: true }, providers));
378
380
  };
379
381
 
380
382
  const EXAMPLE = `import { createPlugin } from '@backstage/core-plugin-api';
@@ -384,41 +386,40 @@ export default createPlugin({
384
386
  featureFlags: [{ name: 'enable-example-feature' }],
385
387
  });
386
388
  `;
387
- const EmptyFlags = () => /* @__PURE__ */ React.createElement(EmptyState, {
388
- missing: "content",
389
- title: "No Feature Flags",
390
- description: "Feature Flags make it possible for plugins to register features in Backstage for users to opt into. You can use this to split out logic in your code for manual A/B testing, etc.",
391
- action: /* @__PURE__ */ React.createElement(React.Fragment, null, /* @__PURE__ */ React.createElement(Typography, {
392
- variant: "body1"
393
- }, "An example for how to add a feature flag is highlighted below:"), /* @__PURE__ */ React.createElement(CodeSnippet, {
394
- text: EXAMPLE,
395
- language: "typescript",
396
- showLineNumbers: true,
397
- highlightedNumbers: [6],
398
- customStyle: { background: "inherit", fontSize: "115%" }
399
- }), /* @__PURE__ */ React.createElement(Button, {
400
- variant: "contained",
401
- color: "primary",
402
- href: "https://backstage.io/docs/api/utility-apis"
403
- }, "Read More"))
404
- });
389
+ const EmptyFlags = () => /* @__PURE__ */ React.createElement(
390
+ EmptyState,
391
+ {
392
+ missing: "content",
393
+ title: "No Feature Flags",
394
+ description: "Feature Flags make it possible for plugins to register features in Backstage for users to opt into. You can use this to split out logic in your code for manual A/B testing, etc.",
395
+ action: /* @__PURE__ */ React.createElement(React.Fragment, null, /* @__PURE__ */ React.createElement(Typography, { variant: "body1" }, "An example for how to add a feature flag is highlighted below:"), /* @__PURE__ */ React.createElement(
396
+ CodeSnippet,
397
+ {
398
+ text: EXAMPLE,
399
+ language: "typescript",
400
+ showLineNumbers: true,
401
+ highlightedNumbers: [6],
402
+ customStyle: { background: "inherit", fontSize: "115%" }
403
+ }
404
+ ), /* @__PURE__ */ React.createElement(
405
+ Button,
406
+ {
407
+ variant: "contained",
408
+ color: "primary",
409
+ href: "https://backstage.io/docs/api/utility-apis"
410
+ },
411
+ "Read More"
412
+ ))
413
+ }
414
+ );
405
415
 
406
- const FlagItem = ({ flag, enabled, toggleHandler }) => /* @__PURE__ */ React.createElement(ListItem, {
407
- divider: true,
408
- button: true,
409
- onClick: () => toggleHandler(flag.name)
410
- }, /* @__PURE__ */ React.createElement(ListItemIcon, null, /* @__PURE__ */ React.createElement(Tooltip, {
411
- placement: "top",
412
- arrow: true,
413
- title: enabled ? "Disable" : "Enable"
414
- }, /* @__PURE__ */ React.createElement(Switch, {
415
- color: "primary",
416
- checked: enabled,
417
- name: flag.name
418
- }))), /* @__PURE__ */ React.createElement(ListItemText, {
419
- primary: flag.name,
420
- secondary: `Registered in ${flag.pluginId} plugin`
421
- }));
416
+ const FlagItem = ({ flag, enabled, toggleHandler }) => /* @__PURE__ */ React.createElement(ListItem, { divider: true, button: true, onClick: () => toggleHandler(flag.name) }, /* @__PURE__ */ React.createElement(ListItemIcon, null, /* @__PURE__ */ React.createElement(Tooltip, { placement: "top", arrow: true, title: enabled ? "Disable" : "Enable" }, /* @__PURE__ */ React.createElement(Switch, { color: "primary", checked: enabled, name: flag.name }))), /* @__PURE__ */ React.createElement(
417
+ ListItemText,
418
+ {
419
+ primary: flag.name,
420
+ secondary: `Registered in ${flag.pluginId} plugin`
421
+ }
422
+ ));
422
423
 
423
424
  const UserSettingsFeatureFlags = () => {
424
425
  const featureFlagsApi = useApi(featureFlagsApiRef);
@@ -458,47 +459,40 @@ const UserSettingsFeatureFlags = () => {
458
459
  (featureFlag) => featureFlag.name.toLocaleLowerCase("en-US").includes(part)
459
460
  )
460
461
  );
461
- const Header = () => /* @__PURE__ */ React.createElement(Grid, {
462
- container: true,
463
- style: { justifyContent: "space-between" }
464
- }, /* @__PURE__ */ React.createElement(Grid, {
465
- item: true,
466
- xs: 6,
467
- md: 8
468
- }, /* @__PURE__ */ React.createElement(Typography, {
469
- variant: "h5"
470
- }, "Feature Flags")), featureFlags.length >= 10 && /* @__PURE__ */ React.createElement(Grid, {
471
- item: true,
472
- xs: 6,
473
- md: 4
474
- }, /* @__PURE__ */ React.createElement(TextField, {
475
- label: "Filter",
476
- style: { display: "flex", justifyContent: "flex-end" },
477
- inputRef: (ref) => ref && ref.focus(),
478
- InputProps: {
479
- ...filterInput.length && {
480
- endAdornment: /* @__PURE__ */ React.createElement(IconButton, {
481
- "aria-label": "Clear filter",
482
- onClick: clearFilterInput,
483
- edge: "end"
484
- }, /* @__PURE__ */ React.createElement(ClearIcon, null))
485
- }
486
- },
487
- onChange: (e) => setFilterInput(e.target.value),
488
- value: filterInput
489
- })));
490
- return /* @__PURE__ */ React.createElement(InfoCard, {
491
- title: /* @__PURE__ */ React.createElement(Header, null)
492
- }, /* @__PURE__ */ React.createElement(List, {
493
- dense: true
494
- }, filteredFeatureFlags.map((featureFlag) => {
462
+ const Header = () => /* @__PURE__ */ React.createElement(Grid, { container: true, style: { justifyContent: "space-between" } }, /* @__PURE__ */ React.createElement(Grid, { item: true, xs: 6, md: 8 }, /* @__PURE__ */ React.createElement(Typography, { variant: "h5" }, "Feature Flags")), featureFlags.length >= 10 && /* @__PURE__ */ React.createElement(Grid, { item: true, xs: 6, md: 4 }, /* @__PURE__ */ React.createElement(
463
+ TextField,
464
+ {
465
+ label: "Filter",
466
+ style: { display: "flex", justifyContent: "flex-end" },
467
+ inputRef: (ref) => ref && ref.focus(),
468
+ InputProps: {
469
+ ...filterInput.length && {
470
+ endAdornment: /* @__PURE__ */ React.createElement(
471
+ IconButton,
472
+ {
473
+ "aria-label": "Clear filter",
474
+ onClick: clearFilterInput,
475
+ edge: "end"
476
+ },
477
+ /* @__PURE__ */ React.createElement(ClearIcon, null)
478
+ )
479
+ }
480
+ },
481
+ onChange: (e) => setFilterInput(e.target.value),
482
+ value: filterInput
483
+ }
484
+ )));
485
+ return /* @__PURE__ */ React.createElement(InfoCard, { title: /* @__PURE__ */ React.createElement(Header, null) }, /* @__PURE__ */ React.createElement(List, { dense: true }, filteredFeatureFlags.map((featureFlag) => {
495
486
  const enabled = Boolean(state[featureFlag.name]);
496
- return /* @__PURE__ */ React.createElement(FlagItem, {
497
- key: featureFlag.name,
498
- flag: featureFlag,
499
- enabled,
500
- toggleHandler: toggleFlag
501
- });
487
+ return /* @__PURE__ */ React.createElement(
488
+ FlagItem,
489
+ {
490
+ key: featureFlag.name,
491
+ flag: featureFlag,
492
+ enabled,
493
+ toggleHandler: toggleFlag
494
+ }
495
+ );
502
496
  })));
503
497
  };
504
498
 
@@ -548,11 +542,14 @@ const UserSettingsSignInAvatar = (props) => {
548
542
  const { iconSize } = sidebarConfig;
549
543
  const classes = useStyles$1(size ? { size } : { size: iconSize });
550
544
  const { profile } = useUserProfile();
551
- return /* @__PURE__ */ React.createElement(Avatar, {
552
- src: profile.picture,
553
- className: classes.avatar,
554
- alt: "Profile picture"
555
- });
545
+ return /* @__PURE__ */ React.createElement(
546
+ Avatar,
547
+ {
548
+ src: profile.picture,
549
+ className: classes.avatar,
550
+ alt: "Profile picture"
551
+ }
552
+ );
556
553
  };
557
554
 
558
555
  const UserSettingsMenu = () => {
@@ -570,80 +567,61 @@ const UserSettingsMenu = () => {
570
567
  setAnchorEl(void 0);
571
568
  setOpen(false);
572
569
  };
573
- return /* @__PURE__ */ React.createElement(React.Fragment, null, /* @__PURE__ */ React.createElement(IconButton, {
574
- "data-testid": "user-settings-menu",
575
- "aria-label": "more",
576
- onClick: handleOpen
577
- }, /* @__PURE__ */ React.createElement(MoreVertIcon, null)), /* @__PURE__ */ React.createElement(Menu, {
578
- anchorEl,
579
- open,
580
- onClose: handleClose
581
- }, /* @__PURE__ */ React.createElement(MenuItem, {
582
- "data-testid": "sign-out",
583
- onClick: () => identityApi.signOut().catch((error) => errorApi.post(error))
584
- }, /* @__PURE__ */ React.createElement(ListItemIcon, null, /* @__PURE__ */ React.createElement(SignOutIcon, null)), "Sign Out")));
570
+ return /* @__PURE__ */ React.createElement(React.Fragment, null, /* @__PURE__ */ React.createElement(
571
+ IconButton,
572
+ {
573
+ "data-testid": "user-settings-menu",
574
+ "aria-label": "more",
575
+ onClick: handleOpen
576
+ },
577
+ /* @__PURE__ */ React.createElement(MoreVertIcon, null)
578
+ ), /* @__PURE__ */ React.createElement(Menu, { anchorEl, open, onClose: handleClose }, /* @__PURE__ */ React.createElement(
579
+ MenuItem,
580
+ {
581
+ "data-testid": "sign-out",
582
+ onClick: () => identityApi.signOut().catch((error) => errorApi.post(error))
583
+ },
584
+ /* @__PURE__ */ React.createElement(ListItemIcon, null, /* @__PURE__ */ React.createElement(SignOutIcon, null)),
585
+ "Sign Out"
586
+ )));
585
587
  };
586
588
 
587
589
  const UserSettingsProfileCard = () => {
588
590
  const { profile, displayName } = useUserProfile();
589
- return /* @__PURE__ */ React.createElement(InfoCard, {
590
- title: "Profile",
591
- variant: "gridItem"
592
- }, /* @__PURE__ */ React.createElement(Grid, {
593
- container: true,
594
- spacing: 6
595
- }, /* @__PURE__ */ React.createElement(Grid, {
596
- item: true
597
- }, /* @__PURE__ */ React.createElement(UserSettingsSignInAvatar, {
598
- size: 96
599
- })), /* @__PURE__ */ React.createElement(Grid, {
600
- item: true,
601
- xs: 12,
602
- sm: true,
603
- container: true
604
- }, /* @__PURE__ */ React.createElement(Grid, {
605
- item: true,
606
- xs: true,
607
- container: true,
608
- direction: "column",
609
- spacing: 2
610
- }, /* @__PURE__ */ React.createElement(Grid, {
611
- item: true,
612
- xs: true
613
- }, /* @__PURE__ */ React.createElement(Typography, {
614
- variant: "subtitle1",
615
- gutterBottom: true
616
- }, displayName), /* @__PURE__ */ React.createElement(Typography, {
617
- variant: "body2",
618
- color: "textSecondary"
619
- }, profile.email))), /* @__PURE__ */ React.createElement(Grid, {
620
- item: true
621
- }, /* @__PURE__ */ React.createElement(UserSettingsMenu, null)))));
591
+ return /* @__PURE__ */ React.createElement(InfoCard, { title: "Profile", variant: "gridItem" }, /* @__PURE__ */ React.createElement(Grid, { container: true, spacing: 6 }, /* @__PURE__ */ React.createElement(Grid, { item: true }, /* @__PURE__ */ React.createElement(UserSettingsSignInAvatar, { size: 96 })), /* @__PURE__ */ React.createElement(Grid, { item: true, xs: 12, sm: true, container: true }, /* @__PURE__ */ React.createElement(Grid, { item: true, xs: true, container: true, direction: "column", spacing: 2 }, /* @__PURE__ */ React.createElement(Grid, { item: true, xs: true }, /* @__PURE__ */ React.createElement(Typography, { variant: "subtitle1", gutterBottom: true }, displayName), /* @__PURE__ */ React.createElement(Typography, { variant: "body2", color: "textSecondary" }, profile.email))), /* @__PURE__ */ React.createElement(Grid, { item: true }, /* @__PURE__ */ React.createElement(UserSettingsMenu, null)))));
622
592
  };
623
593
 
624
594
  const UserSettingsPinToggle = () => {
625
595
  const { isPinned, toggleSidebarPinState } = useSidebarPinState();
626
- return /* @__PURE__ */ React.createElement(ListItem, null, /* @__PURE__ */ React.createElement(ListItemText, {
627
- primary: "Pin Sidebar",
628
- secondary: "Prevent the sidebar from collapsing"
629
- }), /* @__PURE__ */ React.createElement(ListItemSecondaryAction, null, /* @__PURE__ */ React.createElement(Tooltip, {
630
- placement: "top",
631
- arrow: true,
632
- title: `${isPinned ? "Unpin" : "Pin"} Sidebar`
633
- }, /* @__PURE__ */ React.createElement(Switch, {
634
- color: "primary",
635
- checked: isPinned,
636
- onChange: () => toggleSidebarPinState(),
637
- name: "pin",
638
- inputProps: { "aria-label": "Pin Sidebar Switch" }
639
- }))));
596
+ return /* @__PURE__ */ React.createElement(ListItem, null, /* @__PURE__ */ React.createElement(
597
+ ListItemText,
598
+ {
599
+ primary: "Pin Sidebar",
600
+ secondary: "Prevent the sidebar from collapsing"
601
+ }
602
+ ), /* @__PURE__ */ React.createElement(ListItemSecondaryAction, null, /* @__PURE__ */ React.createElement(
603
+ Tooltip,
604
+ {
605
+ placement: "top",
606
+ arrow: true,
607
+ title: `${isPinned ? "Unpin" : "Pin"} Sidebar`
608
+ },
609
+ /* @__PURE__ */ React.createElement(
610
+ Switch,
611
+ {
612
+ color: "primary",
613
+ checked: isPinned,
614
+ onChange: () => toggleSidebarPinState(),
615
+ name: "pin",
616
+ inputProps: { "aria-label": "Pin Sidebar Switch" }
617
+ }
618
+ )
619
+ )));
640
620
  };
641
621
 
642
622
  const ThemeIcon = ({ id, activeId, icon }) => icon ? cloneElement(icon, {
643
623
  color: activeId === id ? "primary" : void 0
644
- }) : /* @__PURE__ */ React.createElement(AutoIcon, {
645
- color: activeId === id ? "primary" : void 0
646
- });
624
+ }) : /* @__PURE__ */ React.createElement(AutoIcon, { color: activeId === id ? "primary" : void 0 });
647
625
  const useStyles = makeStyles((theme) => ({
648
626
  container: {
649
627
  display: "flex",
@@ -681,14 +659,7 @@ const TooltipToggleButton = ({
681
659
  title,
682
660
  value,
683
661
  ...props
684
- }) => /* @__PURE__ */ React.createElement(Tooltip, {
685
- placement: "top",
686
- arrow: true,
687
- title
688
- }, /* @__PURE__ */ React.createElement(ToggleButton, {
689
- value,
690
- ...props
691
- }, children));
662
+ }) => /* @__PURE__ */ React.createElement(Tooltip, { placement: "top", arrow: true, title }, /* @__PURE__ */ React.createElement(ToggleButton, { value, ...props }, children));
692
663
  const UserSettingsThemeToggle = () => {
693
664
  const classes = useStyles();
694
665
  const appThemeApi = useApi(appThemeApiRef);
@@ -704,152 +675,127 @@ const UserSettingsThemeToggle = () => {
704
675
  appThemeApi.setActiveThemeId(void 0);
705
676
  }
706
677
  };
707
- return /* @__PURE__ */ React.createElement(ListItem, {
708
- className: classes.list,
709
- classes: { container: classes.container }
710
- }, /* @__PURE__ */ React.createElement(ListItemText, {
711
- className: classes.listItemText,
712
- primary: "Theme",
713
- secondary: "Change the theme mode"
714
- }), /* @__PURE__ */ React.createElement(ListItemSecondaryAction, {
715
- className: classes.listItemSecondaryAction
716
- }, /* @__PURE__ */ React.createElement(ToggleButtonGroup, {
717
- exclusive: true,
718
- size: "small",
719
- value: themeId != null ? themeId : "auto",
720
- onChange: handleSetTheme
721
- }, themeIds.map((theme) => {
722
- var _a;
723
- const themeIcon = (_a = themeIds.find((t) => t.id === theme.id)) == null ? void 0 : _a.icon;
724
- return /* @__PURE__ */ React.createElement(TooltipToggleButton, {
725
- key: theme.id,
726
- title: `Select ${theme.title}`,
727
- value: theme.id
728
- }, /* @__PURE__ */ React.createElement(React.Fragment, null, theme.title, "\xA0", /* @__PURE__ */ React.createElement(ThemeIcon, {
729
- id: theme.id,
730
- icon: themeIcon,
731
- activeId: themeId
732
- })));
733
- }), /* @__PURE__ */ React.createElement(Tooltip, {
734
- placement: "top",
735
- arrow: true,
736
- title: "Select auto theme"
737
- }, /* @__PURE__ */ React.createElement(ToggleButton, {
738
- value: "auto",
739
- selected: themeId === void 0
740
- }, "Auto\xA0", /* @__PURE__ */ React.createElement(AutoIcon, {
741
- color: themeId === void 0 ? "primary" : void 0
742
- }))))));
678
+ return /* @__PURE__ */ React.createElement(
679
+ ListItem,
680
+ {
681
+ className: classes.list,
682
+ classes: { container: classes.container }
683
+ },
684
+ /* @__PURE__ */ React.createElement(
685
+ ListItemText,
686
+ {
687
+ className: classes.listItemText,
688
+ primary: "Theme",
689
+ secondary: "Change the theme mode"
690
+ }
691
+ ),
692
+ /* @__PURE__ */ React.createElement(ListItemSecondaryAction, { className: classes.listItemSecondaryAction }, /* @__PURE__ */ React.createElement(
693
+ ToggleButtonGroup,
694
+ {
695
+ exclusive: true,
696
+ size: "small",
697
+ value: themeId != null ? themeId : "auto",
698
+ onChange: handleSetTheme
699
+ },
700
+ themeIds.map((theme) => {
701
+ var _a;
702
+ const themeIcon = (_a = themeIds.find((t) => t.id === theme.id)) == null ? void 0 : _a.icon;
703
+ return /* @__PURE__ */ React.createElement(
704
+ TooltipToggleButton,
705
+ {
706
+ key: theme.id,
707
+ title: `Select ${theme.title}`,
708
+ value: theme.id
709
+ },
710
+ /* @__PURE__ */ React.createElement(React.Fragment, null, theme.title, "\xA0", /* @__PURE__ */ React.createElement(
711
+ ThemeIcon,
712
+ {
713
+ id: theme.id,
714
+ icon: themeIcon,
715
+ activeId: themeId
716
+ }
717
+ ))
718
+ );
719
+ }),
720
+ /* @__PURE__ */ React.createElement(Tooltip, { placement: "top", arrow: true, title: "Select auto theme" }, /* @__PURE__ */ React.createElement(ToggleButton, { value: "auto", selected: themeId === void 0 }, "Auto\xA0", /* @__PURE__ */ React.createElement(AutoIcon, { color: themeId === void 0 ? "primary" : void 0 })))
721
+ ))
722
+ );
743
723
  };
744
724
 
745
725
  const UserSettingsAppearanceCard = () => {
746
726
  const { isMobile } = useSidebarPinState();
747
- return /* @__PURE__ */ React.createElement(InfoCard, {
748
- title: "Appearance",
749
- variant: "gridItem"
750
- }, /* @__PURE__ */ React.createElement(List, {
751
- dense: true
752
- }, /* @__PURE__ */ React.createElement(UserSettingsThemeToggle, null), !isMobile && /* @__PURE__ */ React.createElement(UserSettingsPinToggle, null)));
727
+ return /* @__PURE__ */ React.createElement(InfoCard, { title: "Appearance", variant: "gridItem" }, /* @__PURE__ */ React.createElement(List, { dense: true }, /* @__PURE__ */ React.createElement(UserSettingsThemeToggle, null), !isMobile && /* @__PURE__ */ React.createElement(UserSettingsPinToggle, null)));
753
728
  };
754
729
 
755
730
  const UserSettingsIdentityCard = () => {
756
731
  const { backstageIdentity } = useUserProfile();
757
- return /* @__PURE__ */ React.createElement(InfoCard, {
758
- title: "Backstage Identity"
759
- }, /* @__PURE__ */ React.createElement(Grid$1, {
760
- container: true,
761
- spacing: 6
762
- }, /* @__PURE__ */ React.createElement(Grid$1, {
763
- item: true,
764
- xs: 12,
765
- sm: true,
766
- container: true
767
- }, /* @__PURE__ */ React.createElement(Grid$1, {
768
- item: true,
769
- xs: true,
770
- container: true,
771
- direction: "column",
772
- spacing: 2
773
- }, /* @__PURE__ */ React.createElement(Grid$1, {
774
- item: true,
775
- xs: true
776
- }, /* @__PURE__ */ React.createElement(Typography$1, {
777
- variant: "subtitle1",
778
- gutterBottom: true
779
- }, "User Entity:", " ", /* @__PURE__ */ React.createElement(Chip, {
780
- label: backstageIdentity == null ? void 0 : backstageIdentity.userEntityRef,
781
- variant: "outlined",
782
- size: "small"
783
- })), /* @__PURE__ */ React.createElement(Typography$1, {
784
- variant: "subtitle1"
785
- }, "Ownership Entities:", " ", backstageIdentity == null ? void 0 : backstageIdentity.ownershipEntityRefs.map((it) => /* @__PURE__ */ React.createElement(Chip, {
786
- label: it,
787
- variant: "outlined",
788
- size: "small"
789
- }))))))));
732
+ return /* @__PURE__ */ React.createElement(InfoCard, { title: "Backstage Identity" }, /* @__PURE__ */ React.createElement(Grid$1, { container: true, spacing: 6 }, /* @__PURE__ */ React.createElement(Grid$1, { item: true, xs: 12, sm: true, container: true }, /* @__PURE__ */ React.createElement(Grid$1, { item: true, xs: true, container: true, direction: "column", spacing: 2 }, /* @__PURE__ */ React.createElement(Grid$1, { item: true, xs: true }, /* @__PURE__ */ React.createElement(Typography$1, { variant: "subtitle1", gutterBottom: true }, "User Entity:", " ", /* @__PURE__ */ React.createElement(
733
+ Chip,
734
+ {
735
+ label: backstageIdentity == null ? void 0 : backstageIdentity.userEntityRef,
736
+ variant: "outlined",
737
+ size: "small"
738
+ }
739
+ )), /* @__PURE__ */ React.createElement(Typography$1, { variant: "subtitle1" }, "Ownership Entities:", " ", backstageIdentity == null ? void 0 : backstageIdentity.ownershipEntityRefs.map((it) => /* @__PURE__ */ React.createElement(Chip, { label: it, variant: "outlined", size: "small" }))))))));
790
740
  };
791
741
 
792
742
  const UserSettingsGeneral = () => {
793
- return /* @__PURE__ */ React.createElement(Grid, {
794
- container: true,
795
- direction: "row",
796
- spacing: 3
797
- }, /* @__PURE__ */ React.createElement(Grid, {
798
- item: true,
799
- xs: 12,
800
- md: 6
801
- }, /* @__PURE__ */ React.createElement(UserSettingsProfileCard, null)), /* @__PURE__ */ React.createElement(Grid, {
802
- item: true,
803
- xs: 12,
804
- md: 6
805
- }, /* @__PURE__ */ React.createElement(UserSettingsAppearanceCard, null)), /* @__PURE__ */ React.createElement(Grid, {
806
- item: true,
807
- xs: 12,
808
- md: 6
809
- }, /* @__PURE__ */ React.createElement(UserSettingsIdentityCard, null)));
743
+ return /* @__PURE__ */ React.createElement(Grid, { container: true, direction: "row", spacing: 3 }, /* @__PURE__ */ React.createElement(Grid, { item: true, xs: 12, md: 6 }, /* @__PURE__ */ React.createElement(UserSettingsProfileCard, null)), /* @__PURE__ */ React.createElement(Grid, { item: true, xs: 12, md: 6 }, /* @__PURE__ */ React.createElement(UserSettingsAppearanceCard, null)), /* @__PURE__ */ React.createElement(Grid, { item: true, xs: 12, md: 6 }, /* @__PURE__ */ React.createElement(UserSettingsIdentityCard, null)));
810
744
  };
811
745
 
812
- const USER_SETTINGS_TAB_KEY = "user-settings.tab";
813
- const UserSettingsTab = (props) => {
814
- return /* @__PURE__ */ React.createElement(React.Fragment, null, props.children);
746
+ const LAYOUT_DATA_KEY = "plugin.user-settings.settingsLayout";
747
+ const LAYOUT_ROUTE_DATA_KEY = "plugin.user-settings.settingsLayoutRoute";
748
+ const Route = () => null;
749
+ attachComponentData(Route, LAYOUT_ROUTE_DATA_KEY, true);
750
+ attachComponentData(Route, "core.gatherMountPoints", true);
751
+ const SettingsLayout = (props) => {
752
+ const { title, children } = props;
753
+ const { isMobile } = useSidebarPinState();
754
+ const routes = useElementFilter(
755
+ children,
756
+ (elements) => elements.selectByComponentData({
757
+ key: LAYOUT_ROUTE_DATA_KEY,
758
+ withStrictError: "Child of SettingsLayout must be an SettingsLayout.Route"
759
+ }).getElements().map((child) => child.props)
760
+ );
761
+ return /* @__PURE__ */ React.createElement(Page, { themeId: "home" }, !isMobile && /* @__PURE__ */ React.createElement(Header, { title: title != null ? title : "Settings" }), /* @__PURE__ */ React.createElement(RoutedTabs, { routes }));
762
+ };
763
+ attachComponentData(SettingsLayout, LAYOUT_DATA_KEY, true);
764
+ SettingsLayout.Route = Route;
765
+
766
+ const DefaultSettingsPage = (props) => {
767
+ const { providerSettings, tabs } = props;
768
+ return /* @__PURE__ */ React.createElement(SettingsLayout, null, /* @__PURE__ */ React.createElement(SettingsLayout.Route, { path: "general", title: "General" }, /* @__PURE__ */ React.createElement(UserSettingsGeneral, null)), /* @__PURE__ */ React.createElement(
769
+ SettingsLayout.Route,
770
+ {
771
+ path: "auth-providers",
772
+ title: "Authentication Providers"
773
+ },
774
+ /* @__PURE__ */ React.createElement(UserSettingsAuthProviders, { providerSettings })
775
+ ), /* @__PURE__ */ React.createElement(SettingsLayout.Route, { path: "feature-flags", title: "Feature Flags" }, /* @__PURE__ */ React.createElement(UserSettingsFeatureFlags, null)), tabs);
815
776
  };
816
- attachComponentData(UserSettingsTab, USER_SETTINGS_TAB_KEY, "UserSettingsTab");
817
777
 
818
778
  const SettingsPage = (props) => {
819
779
  const { providerSettings } = props;
820
- const { isMobile } = useSidebarPinState();
821
780
  const outlet = useOutlet();
781
+ const layout = useElementFilter(
782
+ outlet,
783
+ (elements) => elements.selectByComponentData({
784
+ key: LAYOUT_DATA_KEY
785
+ }).getElements()
786
+ );
822
787
  const tabs = useElementFilter(
823
788
  outlet,
824
789
  (elements) => elements.selectByComponentData({
825
- key: USER_SETTINGS_TAB_KEY
790
+ key: LAYOUT_ROUTE_DATA_KEY
826
791
  }).getElements()
827
792
  );
828
- return /* @__PURE__ */ React.createElement(Page, {
829
- themeId: "home"
830
- }, !isMobile && /* @__PURE__ */ React.createElement(Header, {
831
- title: "Settings"
832
- }), /* @__PURE__ */ React.createElement(TabbedLayout, null, /* @__PURE__ */ React.createElement(TabbedLayout.Route, {
833
- path: "general",
834
- title: "General"
835
- }, /* @__PURE__ */ React.createElement(UserSettingsGeneral, null)), /* @__PURE__ */ React.createElement(TabbedLayout.Route, {
836
- path: "auth-providers",
837
- title: "Authentication Providers"
838
- }, /* @__PURE__ */ React.createElement(UserSettingsAuthProviders, {
839
- providerSettings
840
- })), /* @__PURE__ */ React.createElement(TabbedLayout.Route, {
841
- path: "feature-flags",
842
- title: "Feature Flags"
843
- }, /* @__PURE__ */ React.createElement(UserSettingsFeatureFlags, null)), tabs.map((child, i) => /* @__PURE__ */ React.createElement(TabbedLayout.Route, {
844
- key: i,
845
- ...child.props
846
- }, child))));
793
+ return /* @__PURE__ */ React.createElement(React.Fragment, null, layout.length !== 0 && layout || /* @__PURE__ */ React.createElement(DefaultSettingsPage, { tabs, providerSettings }));
847
794
  };
848
795
 
849
- var SettingsPage$1 = /*#__PURE__*/Object.freeze({
850
- __proto__: null,
851
- SettingsPage: SettingsPage
852
- });
796
+ const USER_SETTINGS_TAB_KEY = LAYOUT_ROUTE_DATA_KEY;
797
+ const UserSettingsTab = (props) => /* @__PURE__ */ React.createElement(SettingsLayout.Route, { path: props.path, title: props.title }, /* @__PURE__ */ React.createElement(React.Fragment, null, "props.children"));
798
+ attachComponentData(UserSettingsTab, USER_SETTINGS_TAB_KEY, "UserSettingsTab");
853
799
 
854
- export { DefaultProviderSettings, ProviderSettingsItem, SettingsPage as Router, Settings, USER_SETTINGS_TAB_KEY, UserSettingsAppearanceCard, UserSettingsAuthProviders, UserSettingsFeatureFlags, UserSettingsGeneral, UserSettingsIdentityCard, UserSettingsMenu, UserSettingsPage, UserSettingsPinToggle, UserSettingsProfileCard, UserSettingsSignInAvatar, UserSettingsStorage, UserSettingsTab, UserSettingsThemeToggle, userSettingsPlugin as plugin, useUserProfile, userSettingsPlugin };
800
+ export { DefaultProviderSettings, ProviderSettingsItem, SettingsPage as Router, Settings, SettingsLayout, USER_SETTINGS_TAB_KEY, UserSettingsAppearanceCard, UserSettingsAuthProviders, UserSettingsFeatureFlags, UserSettingsGeneral, UserSettingsIdentityCard, UserSettingsMenu, UserSettingsPage, UserSettingsPinToggle, UserSettingsProfileCard, UserSettingsSignInAvatar, UserSettingsStorage, UserSettingsTab, UserSettingsThemeToggle, userSettingsPlugin as plugin, useUserProfile, userSettingsPlugin };
855
801
  //# sourceMappingURL=index.esm.js.map