Skip to content

Commit 00da984

Browse files
committed
[spike]: Resizable docked mode exploration
1 parent 5771568 commit 00da984

30 files changed

+1463
-55
lines changed

workspaces/quickstart/packages/app/package.json

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -49,8 +49,10 @@
4949
"@material-ui/icons": "^4.9.1",
5050
"@mui/icons-material": "5.18.0",
5151
"@mui/material": "5.18.0",
52+
"@red-hat-developer-hub/backstage-plugin-application-drawer": "workspace:^",
5253
"@red-hat-developer-hub/backstage-plugin-global-header": "^1.17.1",
5354
"@red-hat-developer-hub/backstage-plugin-quickstart": "workspace:^",
55+
"@red-hat-developer-hub/backstage-plugin-test-drawer": "workspace:^",
5456
"@red-hat-developer-hub/backstage-plugin-theme": "^0.11.0",
5557
"react": "^18.0.2",
5658
"react-dom": "^18.0.2",
Lines changed: 62 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,62 @@
1+
/*
2+
* Copyright Red Hat, Inc.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
17+
import { ComponentType } from 'react';
18+
import {
19+
useApplicationDrawerContext,
20+
ResizableDrawer,
21+
} from '@red-hat-developer-hub/backstage-plugin-application-drawer';
22+
23+
type DrawerContentType = {
24+
id: string;
25+
Component: ComponentType<any>;
26+
priority?: number;
27+
resizable?: boolean;
28+
};
29+
30+
export const ApplicationDrawer = ({
31+
drawerContents,
32+
}: {
33+
drawerContents: DrawerContentType[];
34+
}) => {
35+
const { getDrawers } = useApplicationDrawerContext();
36+
37+
// Get active drawer - compute fresh each render since we use refs
38+
const drawers = getDrawers();
39+
const activeDrawer = drawers
40+
.filter(p => p.isDrawerOpen)
41+
.map(p => {
42+
const content = drawerContents.find(c => c.id === p.id);
43+
if (!content) return null;
44+
return { ...p, ...content };
45+
})
46+
.filter(Boolean)
47+
.sort((a, b) => (b?.priority ?? -1) - (a?.priority ?? -1))[0];
48+
49+
if (!activeDrawer) return null;
50+
51+
const { Component, resizable, drawerWidth, setDrawerWidth } = activeDrawer;
52+
return (
53+
<ResizableDrawer
54+
isDrawerOpen
55+
isResizable={resizable}
56+
drawerWidth={drawerWidth}
57+
onWidthChange={setDrawerWidth}
58+
>
59+
<Component />
60+
</ResizableDrawer>
61+
);
62+
};

workspaces/quickstart/packages/app/src/components/Root/Root.tsx

Lines changed: 73 additions & 39 deletions
Original file line numberDiff line numberDiff line change
@@ -50,6 +50,14 @@ import Box from '@mui/material/Box';
5050
import { QuickstartDrawerProvider } from '@red-hat-developer-hub/backstage-plugin-quickstart';
5151
import { QuickstartSidebarItem } from './QuickstartSidebarItem';
5252
import { Administration } from '@backstage-community/plugin-rbac';
53+
import { QuickstartDrawerContent } from '@red-hat-developer-hub/backstage-plugin-quickstart';
54+
import {
55+
TestDrawerContent,
56+
TestDrawerProvider,
57+
} from '@red-hat-developer-hub/backstage-plugin-test-drawer';
58+
import { ApplicationDrawerProvider } from '@red-hat-developer-hub/backstage-plugin-application-drawer';
59+
import { ApplicationDrawer } from './ApplicationDrawer';
60+
import { TestDrawerSidebarItem } from './TestDrawerSidebarItem';
5361

5462
const useSidebarLogoStyles = makeStyles({
5563
root: {
@@ -94,54 +102,80 @@ export const Root = ({ children }: PropsWithChildren<{}>) => {
94102
transition: 'margin-right 0.3s ease',
95103
},
96104
},
105+
'body.test-drawer-open #sidebar&': {
106+
"> div > main[class*='BackstagePage-root']": {
107+
marginRight: 'calc(var(--test-drawer-width, 500px) + 1.5em)',
108+
transition: 'margin-right 0.3s ease',
109+
},
110+
},
97111
}}
98112
>
99113
<SidebarPage>
100114
<GlobalHeaderComponent
101115
globalHeaderMountPoints={defaultGlobalHeaderComponentsMountPoints}
102116
/>
117+
<ApplicationDrawerProvider>
103118
<QuickstartDrawerProvider>
104-
<Sidebar>
105-
<SidebarLogo />
106-
<SidebarGroup label="Search" icon={<SearchIcon />} to="/search">
107-
<SidebarSearchModal />
108-
</SidebarGroup>
109-
<SidebarDivider />
110-
<SidebarGroup label="Menu" icon={<MenuIcon />}>
111-
{/* Global nav, not org-specific */}
112-
<SidebarItem icon={HomeIcon} to="catalog" text="Home" />
113-
<MyGroupsSidebarItem
114-
singularTitle="My Group"
115-
pluralTitle="My Groups"
116-
icon={GroupIcon}
117-
/>
118-
<SidebarItem icon={ExtensionIcon} to="api-docs" text="APIs" />
119-
<SidebarItem icon={LibraryBooks} to="docs" text="Docs" />
120-
<SidebarItem
121-
icon={CreateComponentIcon}
122-
to="create"
123-
text="Create..."
124-
/>
125-
{/* End global nav */}
119+
<TestDrawerProvider>
120+
<Sidebar>
121+
<SidebarLogo />
122+
<SidebarGroup label="Search" icon={<SearchIcon />} to="/search">
123+
<SidebarSearchModal />
124+
</SidebarGroup>
125+
<SidebarDivider />
126+
<SidebarGroup label="Menu" icon={<MenuIcon />}>
127+
{/* Global nav, not org-specific */}
128+
<SidebarItem icon={HomeIcon} to="catalog" text="Home" />
129+
<MyGroupsSidebarItem
130+
singularTitle="My Group"
131+
pluralTitle="My Groups"
132+
icon={GroupIcon}
133+
/>
134+
<SidebarItem icon={ExtensionIcon} to="api-docs" text="APIs" />
135+
<SidebarItem icon={LibraryBooks} to="docs" text="Docs" />
136+
<SidebarItem
137+
icon={CreateComponentIcon}
138+
to="create"
139+
text="Create..."
140+
/>
141+
{/* End global nav */}
142+
<SidebarDivider />
143+
<Administration />
144+
<SidebarScrollWrapper>
145+
{/* Items in this group will be scrollable if they run out of space */}
146+
</SidebarScrollWrapper>
147+
</SidebarGroup>
148+
<SidebarSpace />
149+
<QuickstartSidebarItem />
150+
<TestDrawerSidebarItem />
126151
<SidebarDivider />
127-
<Administration />
128-
<SidebarScrollWrapper>
129-
{/* Items in this group will be scrollable if they run out of space */}
130-
</SidebarScrollWrapper>
131-
</SidebarGroup>
132-
<SidebarSpace />
133-
<QuickstartSidebarItem />
134-
<SidebarDivider />
135-
<SidebarGroup
136-
label="Settings"
137-
icon={<UserSettingsSignInAvatar />}
138-
to="/settings"
139-
>
140-
<SidebarSettings />
141-
</SidebarGroup>
142-
</Sidebar>
143-
{children}
152+
<SidebarGroup
153+
label="Settings"
154+
icon={<UserSettingsSignInAvatar />}
155+
to="/settings"
156+
>
157+
<SidebarSettings />
158+
</SidebarGroup>
159+
</Sidebar>
160+
{children}
161+
<ApplicationDrawer
162+
drawerContents={[
163+
{
164+
Component: QuickstartDrawerContent,
165+
priority: 1,
166+
id: 'quickstart',
167+
},
168+
{
169+
Component: TestDrawerContent,
170+
priority: 100,
171+
id: 'test-drawer',
172+
resizable: true,
173+
},
174+
]}
175+
/>
176+
</TestDrawerProvider>
144177
</QuickstartDrawerProvider>
178+
</ApplicationDrawerProvider>
145179
</SidebarPage>
146180
</Box>
147181
);
Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
/*
2+
* Copyright Red Hat, Inc.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
17+
import { SidebarItem, StarIcon } from '@backstage/core-components';
18+
import { useTestDrawerContext } from '@red-hat-developer-hub/backstage-plugin-test-drawer';
19+
20+
export const TestDrawerSidebarItem = () => {
21+
const { toggleDrawer } = useTestDrawerContext();
22+
23+
return (
24+
<SidebarItem text="Test Drawer" icon={StarIcon} onClick={toggleDrawer} />
25+
);
26+
};
Lines changed: 58 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,58 @@
1+
{
2+
"name": "@red-hat-developer-hub/backstage-plugin-application-drawer",
3+
"version": "0.1.0",
4+
"license": "Apache-2.0",
5+
"main": "src/index.ts",
6+
"types": "src/index.ts",
7+
"publishConfig": {
8+
"access": "public"
9+
},
10+
"repository": {
11+
"type": "git",
12+
"url": "https://github.com/redhat-developer/rhdh-plugins",
13+
"directory": "workspaces/quickstart/plugins/application-drawer"
14+
},
15+
"backstage": {
16+
"role": "frontend-plugin",
17+
"pluginId": "application-drawer",
18+
"pluginPackages": [
19+
"@red-hat-developer-hub/backstage-plugin-application-drawer"
20+
]
21+
},
22+
"sideEffects": false,
23+
"scripts": {
24+
"start": "backstage-cli package start",
25+
"build": "backstage-cli package build",
26+
"lint": "backstage-cli package lint",
27+
"test": "backstage-cli package test",
28+
"clean": "backstage-cli package clean",
29+
"prepack": "backstage-cli package prepack",
30+
"postpack": "backstage-cli package postpack"
31+
},
32+
"dependencies": {
33+
"@backstage/core-components": "^0.18.3",
34+
"@backstage/core-plugin-api": "^1.12.0",
35+
"@backstage/theme": "^0.7.0",
36+
"@mui/icons-material": "5.18.0",
37+
"@mui/material": "5.18.0",
38+
"@red-hat-developer-hub/backstage-plugin-theme": "^0.11.0"
39+
},
40+
"peerDependencies": {
41+
"react": "^16.13.1 || ^17.0.0 || ^18.0.0"
42+
},
43+
"devDependencies": {
44+
"@backstage/cli": "^0.34.5",
45+
"@backstage/dev-utils": "^1.1.17",
46+
"@backstage/test-utils": "^1.7.13",
47+
"@testing-library/jest-dom": "^6.0.0",
48+
"@testing-library/react": "^14.0.0",
49+
"react": "^16.13.1 || ^17.0.0 || ^18.0.0"
50+
},
51+
"exports": {
52+
".": "./src/index.ts",
53+
"./package.json": "./package.json"
54+
},
55+
"files": [
56+
"dist"
57+
]
58+
}
Lines changed: 73 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,73 @@
1+
/*
2+
* Copyright Red Hat, Inc.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
17+
import { createContext, useContext } from 'react';
18+
19+
/**
20+
* Type for a drawer context
21+
*
22+
* @public
23+
*/
24+
export type DrawerContext = {
25+
id: string;
26+
isDrawerOpen: boolean;
27+
drawerWidth?: number;
28+
setDrawerWidth?: React.Dispatch<React.SetStateAction<number>>;
29+
};
30+
31+
/**
32+
* Type for ApplicationDrawerContext
33+
*
34+
* @public
35+
*/
36+
export type ApplicationDrawerContextType = {
37+
addDrawerContext: (
38+
id: string,
39+
context: {
40+
isDrawerOpen: boolean;
41+
drawerWidth?: number;
42+
setDrawerWidth?: React.Dispatch<React.SetStateAction<number>>;
43+
},
44+
) => void;
45+
getDrawers: () => DrawerContext[];
46+
};
47+
48+
/**
49+
* Context for the Application Drawer
50+
*
51+
* Allows drawer providers (Quickstart, Lightspeed, etc.) to register
52+
* themselves so ApplicationDrawer can render the active one.
53+
*
54+
* @public
55+
*/
56+
export const ApplicationDrawerContext = createContext<
57+
ApplicationDrawerContextType | undefined
58+
>(undefined);
59+
60+
/**
61+
* Hook to access the ApplicationDrawerContext
62+
*
63+
* @public
64+
*/
65+
export const useApplicationDrawerContext = (): ApplicationDrawerContextType => {
66+
const context = useContext(ApplicationDrawerContext);
67+
if (!context) {
68+
throw new Error(
69+
'useApplicationDrawerContext must be used within an ApplicationDrawerProvider',
70+
);
71+
}
72+
return context;
73+
};

0 commit comments

Comments
 (0)