Skip to content

Commit b0e855a

Browse files
committed
feat(SidebarTab): allow to define web component on first usage
Signed-off-by: Ferdinand Thiessen <opensource@fthiessen.de>
1 parent ff2cffd commit b0e855a

2 files changed

Lines changed: 43 additions & 14 deletions

File tree

__tests__/sidebar/sidebarTab.spec.ts

Lines changed: 11 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -77,7 +77,7 @@ describe('Sidebar tabs', () => {
7777

7878
it('fails with invalid tagName name', () => {
7979
expect(() => registerSidebarTab({ ...getExampleTab(), tagName: 'MyAppSidebarTab' }))
80-
.toThrowErrorMatchingInlineSnapshot('[Error: Sidebar tabs tagName name is invalid]')
80+
.toThrowErrorMatchingInlineSnapshot('[Error: Sidebar tab "tagName" is invalid]')
8181
})
8282

8383
it('fails with missing name', () => {
@@ -119,11 +119,18 @@ describe('Sidebar tabs', () => {
119119
}).toThrowErrorMatchingInlineSnapshot('[Error: Sidebar tabs need to have a numeric order set]')
120120
})
121121

122-
it('fails with missing "enabled" method', () => {
122+
it('fails with invalid "enabled" method', () => {
123123
expect(() => {
124124
// @ts-expect-error mocking for testing
125-
registerSidebarTab({ ...getExampleTab(), enabled: undefined })
126-
}).toThrowErrorMatchingInlineSnapshot('[Error: Sidebar tabs need to have an "enabled" method]')
125+
registerSidebarTab({ ...getExampleTab(), enabled: 'true' })
126+
}).toThrowErrorMatchingInlineSnapshot('[Error: Sidebar tab "enabled" is not a function]')
127+
})
128+
129+
it('fails with invalid "onInit" method', () => {
130+
expect(() => {
131+
// @ts-expect-error mocking for testing
132+
registerSidebarTab({ ...getExampleTab(), onInit: 'not a method' })
133+
}).toThrowErrorMatchingInlineSnapshot('[Error: Sidebar tab "onInit" is not a function]')
127134
})
128135
})
129136
})

lib/sidebar/SidebarTab.ts

Lines changed: 32 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -32,15 +32,19 @@ export interface ISidebarContext {
3232
* @see https://developer.mozilla.org/en-US/docs/Web/API/Web_components
3333
* @see https://vuejs.org/guide/extras/web-components#building-custom-elements-with-vue
3434
*/
35-
export interface SidebarComponent extends HTMLElement, ISidebarContext {
35+
export interface SidebarTabComponent extends ISidebarContext {
3636
/**
37-
* This method is called by the files app if the sidebar tab state changes.
38-
*
39-
* @param active - The new active state
37+
* The active state of the sidebar tab.
38+
* It will be set to true if this component is the currently active tab.
4039
*/
41-
setActive(active: boolean): Promise<void>
40+
active: boolean
4241
}
4342

43+
/**
44+
* The instance type of a sidebar tab web component.
45+
*/
46+
export type SidebarTabComponentInstance = SidebarTabComponent & HTMLElement
47+
4448
/**
4549
* Implementation of a custom sidebar tab within the files app.
4650
*/
@@ -69,7 +73,10 @@ export interface ISidebarTab {
6973

7074
/**
7175
* The tag name of the web component.
72-
* The web component must already be registered under that tag name with `CustomElementRegistry.define()`.
76+
*
77+
* The web component must be defined using this name with `CustomElementRegistry.define()`,
78+
* either on initialization or within the `onInit` callback (preferred).
79+
* When rendering the sidebar tab, the files app will wait for the component to be defined in the registry (`customElements.whenDefined()`).
7380
*
7481
* To avoid name clashes the name has to start with your appid (e.g. `your_app`).
7582
* So in addition with the web component naming rules a good name would be `your_app-files-sidebar-tab`.
@@ -79,9 +86,20 @@ export interface ISidebarTab {
7986
/**
8087
* Callback to check if the sidebar tab should be shown for the selected node.
8188
*
89+
* If not provided, the tab will always be shown.
90+
*
8291
* @param context - The current context of the files app
8392
*/
84-
enabled: (context: ISidebarContext) => boolean
93+
enabled?: (context: ISidebarContext) => boolean
94+
95+
/**
96+
* Called when the sidebar tab is active and rendered the first time in the sidebar.
97+
* This should be used to register the web componen (`CustomElementRegistry.define()`).
98+
*
99+
* The sidebar itself will anyways wait for the component to be defined in the registry (`customElements.whenDefined()`).
100+
* But also will wait for the promise returned by this method to resolve before rendering the tab.
101+
*/
102+
onInit?: () => Promise<void>
85103
}
86104

87105
/**
@@ -132,7 +150,7 @@ function validateSidebarTab(tab: ISidebarTab): void {
132150
}
133151

134152
if (!tab.tagName.match(/^[a-z][a-z0-9-_]+$/)) {
135-
throw new Error('Sidebar tabs tagName name is invalid')
153+
throw new Error('Sidebar tab "tagName" is invalid')
136154
}
137155

138156
if (!tab.displayName || typeof tab.displayName !== 'string') {
@@ -147,7 +165,11 @@ function validateSidebarTab(tab: ISidebarTab): void {
147165
throw new Error('Sidebar tabs need to have a numeric order set')
148166
}
149167

150-
if (typeof tab.enabled !== 'function') {
151-
throw new Error('Sidebar tabs need to have an "enabled" method')
168+
if (tab.enabled && typeof tab.enabled !== 'function') {
169+
throw new Error('Sidebar tab "enabled" is not a function')
170+
}
171+
172+
if (tab.onInit && typeof tab.onInit !== 'function') {
173+
throw new Error('Sidebar tab "onInit" is not a function')
152174
}
153175
}

0 commit comments

Comments
 (0)