Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions .changeset/vacation-calendar-nfs-support.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
'@axis-backstage/plugin-vacation-calendar': patch
---

Added new frontend system (NFS) support via a new `./alpha` entry point. The plugin exposes an `EntityContentBlueprint` that renders the Out of Office calendar as an entity content tab, and an `ApiBlueprint` for the vacation calendar API.
2 changes: 1 addition & 1 deletion packages/app-next/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@
"@axis-backstage/plugin-jira-dashboard": "workspace:^",
"@axis-backstage/plugin-jira-dashboard-common": "workspace:^",
"@axis-backstage/plugin-readme": "workspace:^",
"@axis-backstage/plugin-statuspage": "workspace:^",
"@axis-backstage/plugin-vacation-calendar": "workspace:^",
"@backstage/app-defaults": "^1.7.7",
"@backstage/catalog-model": "^1.8.0",
"@backstage/cli": "^0.36.1",
Expand Down
39 changes: 39 additions & 0 deletions plugins/vacation-calendar/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -100,6 +100,45 @@ const userPage = (
);
```

## New Frontend System

The plugin supports the [new Backstage frontend system](https://backstage.io/docs/frontend-system/) via the `./alpha` entry point.

1. Install the plugin into your app:

```bash
# From your Backstage root directory
yarn --cwd packages/app add @axis-backstage/plugin-vacation-calendar
```

2. Add the plugin to your app:

```ts
// packages/app/src/App.tsx (or wherever you configure your frontend features)
import vacationCalendarPlugin from '@axis-backstage/plugin-vacation-calendar/alpha';

export const app = createApp({
features: [
// ...
vacationCalendarPlugin,
],
});
```

The plugin will automatically add an "Out Of Office" tab to Group and User entity pages. No manual route or API setup is required — the plugin handles both the API factory and the entity content tab as extensions.

### Overriding the entity filter

By default the tab is shown on `Group` and `User` entities. You can override this in your `app-config.yaml`:

```yaml
app:
extensions:
- entity-content:vacation-calendar/entity:
config:
filter: 'kind: Component'
```

## Development

The 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 [/vacation-calendar](http://localhost:3000/vacation-calendar).
Expand Down
16 changes: 16 additions & 0 deletions plugins/vacation-calendar/package.json
Original file line number Diff line number Diff line change
@@ -1,8 +1,23 @@
{
"name": "@axis-backstage/plugin-vacation-calendar",
"version": "0.9.2",
"exports": {
".": "./src/index.ts",
"./alpha": "./src/alpha.ts",
"./package.json": "./package.json"
},
"main": "src/index.ts",
"types": "src/index.ts",
"typesVersions": {
"*": {
"alpha": [
"src/alpha.ts"
],
"package.json": [
"package.json"
]
}
},
"license": "Apache-2.0",
"publishConfig": {
"access": "public",
Expand Down Expand Up @@ -32,6 +47,7 @@
"@backstage/core-components": "^0.18.9",
"@backstage/core-plugin-api": "^1.12.5",
"@backstage/errors": "^1.3.0",
"@backstage/frontend-plugin-api": "^0.16.2",
"@backstage/plugin-catalog-react": "^2.1.4",
"@backstage/theme": "^0.7.3",
"@date-io/luxon": "3.0.0",
Expand Down
125 changes: 125 additions & 0 deletions plugins/vacation-calendar/report-alpha.api.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,125 @@
## API Report File for "@axis-backstage/plugin-vacation-calendar"

> Do not edit this file. It is a report generated by [API Extractor](https://api-extractor.com/).

```ts
import { AnyApiFactory } from '@backstage/frontend-plugin-api';
import { AnyRouteRefParams } from '@backstage/frontend-plugin-api';
import { ApiFactory } from '@backstage/frontend-plugin-api';
import { Entity } from '@backstage/catalog-model';
import { ExtensionBlueprintParams } from '@backstage/frontend-plugin-api';
import { ExtensionDataRef } from '@backstage/frontend-plugin-api';
import { FilterPredicate } from '@backstage/filter-predicates';
import { JSX as JSX_2 } from 'react';
import { JSXElementConstructor } from 'react';
import { OverridableExtensionDefinition } from '@backstage/frontend-plugin-api';
import { OverridableFrontendPlugin } from '@backstage/frontend-plugin-api';
import { ReactElement } from 'react';
import { RouteRef } from '@backstage/core-plugin-api';
import { RouteRef as RouteRef_2 } from '@backstage/frontend-plugin-api';

// @alpha
const _default: OverridableFrontendPlugin<
{
root: RouteRef<undefined>;
},
{},
{
'api:vacation-calendar': OverridableExtensionDefinition<{
kind: 'api';
name: undefined;
config: {};
configInput: {};
output: ExtensionDataRef<AnyApiFactory, 'core.api.factory', {}>;
inputs: {};
params: <
TApi,
TImpl extends TApi,
TDeps extends { [name in string]: unknown },
>(
params: ApiFactory<TApi, TImpl, TDeps>,
) => ExtensionBlueprintParams<AnyApiFactory>;
}>;
'entity-content:vacation-calendar/entity': OverridableExtensionDefinition<{
kind: 'entity-content';
name: 'entity';
config: {
path: string | undefined;
title: string | undefined;
filter: FilterPredicate | undefined;
group: string | false | undefined;
icon: string | undefined;
};
configInput: {
path?: string | undefined;
title?: string | undefined;
filter?: FilterPredicate | undefined;
group?: string | false | undefined;
icon?: string | undefined;
};
output:
| ExtensionDataRef<JSX_2.Element, 'core.reactElement', {}>
| ExtensionDataRef<string, 'core.routing.path', {}>
| ExtensionDataRef<
RouteRef_2<AnyRouteRefParams>,
'core.routing.ref',
{
optional: true;
}
>
| ExtensionDataRef<
(entity: Entity) => boolean,
'catalog.entity-filter-function',
{
optional: true;
}
>
| ExtensionDataRef<
string,
'catalog.entity-filter-expression',
{
optional: true;
}
>
| ExtensionDataRef<string, 'catalog.entity-content-title', {}>
| ExtensionDataRef<
string,
'catalog.entity-content-group',
{
optional: true;
}
>
| ExtensionDataRef<
string | ReactElement<any, string | JSXElementConstructor<any>>,
'catalog.entity-content-icon',
{
optional: true;
}
>;
inputs: {};
params: {
defaultPath?: [Error: `Use the 'path' param instead`];
path: string;
defaultTitle?: [Error: `Use the 'title' param instead`];
title: string;
defaultGroup?: [Error: `Use the 'group' param instead`];
group?:
| (
| 'development'
| 'deployment'
| 'overview'
| 'documentation'
| 'operation'
| 'observability'
)
| (string & {});
icon?: string | ReactElement;
loader: () => Promise<JSX.Element>;
routeRef?: RouteRef_2;
filter?: string | FilterPredicate | ((entity: Entity) => boolean);
};
}>;
}
>;
export default _default;
```
20 changes: 20 additions & 0 deletions plugins/vacation-calendar/src/alpha.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
import { createFrontendPlugin } from '@backstage/frontend-plugin-api';
import {
entityVacationCalendarContent,
vacationCalendarApi,
} from './alpha/index';
import { rootRouteRef } from './routes';

/**
* Frontend plugin that fetches and displays out of office events for entities.
*
* @alpha
* @packageDocumentation
*/
export default createFrontendPlugin({
pluginId: 'vacation-calendar',
extensions: [entityVacationCalendarContent, vacationCalendarApi],
routes: {
root: rootRouteRef,
},
});
17 changes: 17 additions & 0 deletions plugins/vacation-calendar/src/alpha/apis.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
import { ApiBlueprint, fetchApiRef } from '@backstage/frontend-plugin-api';
import { microsoftAuthApiRef } from '@backstage/core-plugin-api';
import { vacationCalendarApiRef } from '../api';
import { VacationCalendarApiClient } from '../api';

/**
* @alpha
*/
export const vacationCalendarApi = ApiBlueprint.make({
params: defineParams =>
defineParams({
api: vacationCalendarApiRef,
deps: { authApi: microsoftAuthApiRef, fetchApi: fetchApiRef },
factory: ({ authApi, fetchApi }) =>
new VacationCalendarApiClient({ authApi, fetchApi }),
}),
});
18 changes: 18 additions & 0 deletions plugins/vacation-calendar/src/alpha/entityContent.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
import { isGroupEntity, isUserEntity } from '@backstage/catalog-model';
import { EntityContentBlueprint } from '@backstage/plugin-catalog-react/alpha';
import { rootRouteRef } from '../routes';

/**
* @alpha
*/
export const entityVacationCalendarContent = EntityContentBlueprint.make({
name: 'entity',
params: {
path: '/vacation-calendar',
title: 'Out Of Office',
routeRef: rootRouteRef,
filter: entity => isGroupEntity(entity) || isUserEntity(entity),
loader: async () =>
import('../components').then(m => <m.VacationCalendar />),
},
});
2 changes: 2 additions & 0 deletions plugins/vacation-calendar/src/alpha/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
export * from './apis';
export * from './entityContent';
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import 'react-calendar-timeline/lib/Timeline.css';
import { ReactNode, useState } from 'react';
import useAsync from 'react-use/lib/useAsync';
import Timeline, {
Expand Down
3 changes: 2 additions & 1 deletion yarn.lock
Original file line number Diff line number Diff line change
Expand Up @@ -2200,6 +2200,7 @@ __metadata:
"@backstage/core-plugin-api": "npm:^1.12.5"
"@backstage/dev-utils": "npm:^1.1.22"
"@backstage/errors": "npm:^1.3.0"
"@backstage/frontend-plugin-api": "npm:^0.16.2"
"@backstage/plugin-catalog-react": "npm:^2.1.4"
"@backstage/test-utils": "npm:^1.7.17"
"@backstage/theme": "npm:^0.7.3"
Expand Down Expand Up @@ -15308,7 +15309,7 @@ __metadata:
"@axis-backstage/plugin-jira-dashboard": "workspace:^"
"@axis-backstage/plugin-jira-dashboard-common": "workspace:^"
"@axis-backstage/plugin-readme": "workspace:^"
"@axis-backstage/plugin-statuspage": "workspace:^"
"@axis-backstage/plugin-vacation-calendar": "workspace:^"
"@backstage/app-defaults": "npm:^1.7.7"
"@backstage/catalog-model": "npm:^1.8.0"
"@backstage/cli": "npm:^0.36.1"
Expand Down
Loading