@tridion-docs/extensions v2.0.0
Docs Extensions
This project contains the helper function, components and hooks for working with Tridion Docs extensions.
License
License: https://www.rws.com/legal/terms-and-conditions/rws-technology/
Copyright (c) 2014-2023. All Rights Reserved by RWS Group for and on behalf of its affiliates and subsidiaries.
Compatibility
This version of the package is compatible with Tridion Docs 15.0
Documentation
Components - React components
Hooks - React hooks, helps you to show custom components, refresh content etc.
Extension Points - Allows to register custom components in one of the available areas.
Other documentation
Components
The extension library provides components to assist with extension development.
React component: Iframe
The Iframe
component allows you to embed an HTML page inside the extension area.
The width and height can be defined as seen in the example below, these can also be
percentages, e.g.: "50%"
, "100%"
, etc.
The security sandbox
is pretty relaxed: allow-forms
, allow-popups
, allow-same-origin
, allow-scripts
,
however the iframe content is not allowed to access data from the parent frame
(the Docs Organize Space application).
Example
import { Iframe } from '@tridion-docs/extensions';
export const CustomIframePage = () => {
return <Iframe src="https://www.wikipedia.org/" width="600" height="400" />;
};
Props
Name | Type | Description |
---|---|---|
src | string | Url to load inside the iframe |
width | number / string | Width of the iframe ("100%" by default) |
height | number / string | Height of the iframe ("100%" by default) |
className | string | Optional CSS classname |
React component: DataIndicator
The DataIndicator
component handles loading, error and empty content views.
Example
import { DataIndicator } from '@tridion-docs/extensions';
export const CustomPage = () => {
return (
<DataIndicator isLoading={false} noDataMessage="There is no information to show.">
<div>This content will be visible if isLoading set to false</div>
</DataIndicator>
);
};
Props
Name | Type | Description |
---|---|---|
children | ReactNode | Content that should be rendered when hasData is true AND isLoading is false AND isLoadingFailed is false |
errorMessage | string | Message that rendered when isLoadingFailed is true |
hasData | boolean | Indicates that content is ready to be rendered (children). When true children is rendered |
isLoading | boolean | Controls loading indicator. When true loading indicator is shown |
isLoadingFailed | boolean | Controls error message. When true error message is rendered |
noDataMessage | string | Rendered when hasData is false |
Hooks
The extension library provides hooks to assist with extension development.
useItemSelector
The useItemSelector
allows you to show dialog with a structured folder tree on the left side and the content of the folder on the right side. This dialog supposes to help you to select one of the multiple objects based on your configuration.
Example
import { useItemSelector, ItemSelectorObjectType } from '@tridion-docs/extensions';
export const useCustomHook = () => {
const itemSelectorProps = {
objectTypesToSelect: [
DocumentObjectType.ILLUSTRATION,
DocumentObjectType.LIBRARY,
DocumentObjectType.MAP,
DocumentObjectType.OTHER,
DocumentObjectType.TOPIC,
],
isMultiSelect: false,
onOk: async (values: Content[]) => {
console.log('selected', values);
},
okLabel: 'Ok',
cancelLabel: 'Cancel',
};
const { execute } = useItemSelector(itemSelectorProps);
return {
showItemSelector: execute,
};
}
Props
Name | Type | Description |
---|---|---|
isMultiSelect | boolean | Whether multiple items can be selected |
objectTypesToSelect | ItemSelectorObjectType | List of object types that can be selected. Default available types: undefined , illustration , library , map , other , topic , publication |
onOk | function | Function that will be called when a user clicks the OK button |
okLabel | string | Label of the 'Ok' button |
cancelLabel | string | Label of the 'Cancel' button |
Return
Name | Type | Description |
---|---|---|
execute | function | By calling this function you will show item selector on the screen. |
useFolderSelector
The useFolderSelector
allows you to show dialog with a structured folder tree. This dialog supposes to help you to select one folder.
Example
import { useFolderSelector, ContentState, Folder } from '@tridion-docs/extensions';
export const useCustomHook = () => {
const folderSelectorProps = {
onOk: async (folder: Folder) => {
console.log('selected', folder);
},
getDisabledFolderIds: (_hierarchy: Hierarchy<ContentState>, _sourceFolderId: string) => {
return [];
},
showConstraints: false,
constraintsComponent: () => <></>,
modalIcon: <MyIcon />,
modalTitle: 'The Modal Title',
okButtonLabel: 'Select',
cancelButtonLabel: 'Cancel',
};
const { execute } = useFolderSelector(folderSelectorProps);
return {
showFolderSelector: execute,
};
}
Props
Name | Type | Description |
---|---|---|
getDisabledFolderIds | function | Disable the ability to select a list of Folders |
showConstraints | boolean | Show or Hide the Show Constraints button |
constraintsComponent | boolean | Component to show when user clicks on Show Constraints button |
onOk | function | Function that will be called when a user clicks the OK button |
okButtonLabel | string | Label of the 'Ok' button |
cancelButtonLabel | string | Label of the 'Cancel' button |
modalTitle | string | Title of the Modal |
modalIcon | string | Icon of Modal Title |
cancelButtonLabel | string | Label of the 'Cancel' button |
Return
Name | Type | Description |
---|---|---|
execute | function | By calling this function you will show folder selector on the screen. |
Notifications
Hook for displaying a notification message.
Example
export const useCustomHook = (props: any) => {
const { execute: executeNotification, messageSeverity } = useNotification();
executeNotification({
message: 'Notification title',
description: 'Notification message',
severity: messageSeverity.SUCCESS,
});
}
Props
Name | Type | Description |
---|
none
Return
| Name | Type | Description |
| :-------------------- | :------------------ | :---------------------------------------- |
execute
| function(message, description, severity)
| By calling this function you will show a notification message. message
: Title of the notification, description
: Body or text of the notification, severity
: notification type
messageSeverity
| MessageSeverity
| Message type, based on the type design and behavior of notification may vary.
useRepositoryFolderRefresh
Hook to refresh content of the folder
Example
export const useCustomHook = (props: any) => {
const { execute: executeRepositoryFolderRefresh } = useRepositoryFolderRefresh({ item: contextFolder as Content });
executeRepositoryFolderRefresh().then(() => {
console.log('refresh complete');
})
}
Props
Name | Type | Description |
---|---|---|
item | Content | Folder to be refreshed |
Return
| Name | Type | Description |
| :-------------------- | :------------------ | :--------------------------------------- |
execute
| () => Promise<Content>
| By calling this function you will refresh folder content, promise will be resolved when the data fetching is completed.
useRepositoryObjectDetailsRefresh
Hook to refresh current details view
Example
export const useCustomHook = (props: any) => {
const { execute } = useRepositoryObjectDetailsRefresh();
execute().then(() => { console.log('refresh complete') })
}
Props
Name | Type | Description |
---|
none
Return
| Name | Type | Description |
| :-------------------- | :------------------ | :--------------------------------------- |
execute
| () => Promise<Content>
| By calling this function you will refresh details view, promise will be resolved when the data fetching is completed.
useRepositoryObjectRefresh
Hook to refresh content of the provided item
Example
export const useCustomHook = (props: any) => {
const { execute } = useRepositoryObjectRefresh();
execute().then(() => { console.log('refresh complete') })
}
Props
Name | Type | Description |
---|---|---|
item | Content | Content item to be refreshed |
Return
| Name | Type | Description |
| :-------------------- | :------------------ | :--------------------------------------- |
execute
| () => Promise<Content>
| By calling this function you will refresh content, promise will be resolved when the data fetching is completed.
useUserProfile
Hook to get current logged on user information
Example
import { useUserProfile } from '@tridion-docs/extensions';
export const CustomComponents = (props: any) => {
const { execute: getUserProfile } = useUserProfile();
const { displayName } = getUserProfile();
return (
<div>
Hi {displayName}!
</div>
)
}
Props
Name | Type | Description |
---|
none
Return
| Name | Type | Description |
| :-------------------- | :------------------ | :--------------------------------------- |
execute
| () => UserInfo
| By calling this function you will get current logged user information. userName
: User name, displayName
: Display name, uiLanguage
: Ui language of the user, workingLanguage
: Working language, privileges
: list of available privileges
Extension points
Organize Space of Tridion Docs supports several areas that can be extended and custom logic can be integrated into. Every extension needs to be initialized before it can be used. The initial registration of the extension areas can be done with the help of ExtensionModule
interface. If you're using @tridion-docs/extensions-cli
for generating your app then ExtensionModule interface will be already set in your index.ts file.
To register your extension you need to call a builder function inside initialize
method.
const extensionModule: ExtensionModule = {
runtimeInfo: packageJson,
initializeGlobals,
initialize: builder => {
builder.objectInsight.addObjectInsightItem(() => ({
//...
}));
builder.action.addExplorerAction(() => ({
//...
}));
builder.header.addMainNavigationLink(() => ({
//...
}));
builder.header.addMainNavigationItem(() => ({
//...
}));
},
};
Header
A header extension point is providing the ability to extend the main navigation and add a link button to the right side of the header
mainNavigationItem
Registration of new navigation items is required to call the builder function builder.header.addMainNavigationItem
builder.header.addMainNavigationItem(() => ({
id: 'id',
title: 'title',
path: 'what-ever',
position: {
item: 'settings',
positioningType: 'after',
},
isVisible: () => { return true}
component: CustomPage,
}));
Props
Name | Type | Description |
---|---|---|
id | string | Unique Id for menu item |
title | string | Menu item title |
path | string | Menu item path (should be unique) |
position | Position | Menu item relative position. |
isVisible | ({userProfile: UserInfo}) => boolean | Function to determine if the menu item should be visible, it receives the current user and should return a boolean value (show/hide) |
component | ComponentType | Component that will be rendered |
mainNavigationLink
Adds extra navigation link in the main application header. Registration of new navigation items is required to call the builder function builder.header.addMainNavigationLink
builder.header.addMainNavigationLink(() => ({
id: 'id',
title: 'title',
tooltip: '',
popupProperties: {
width: '600px',
height: '600px'
},
isVisible: () => {
return true;
},
icon: CustomIcon,
component: CustomPage,
}));
Props
Name | Type | Description |
---|---|---|
id | string | Unique Id for navigation link used by system to identify it |
title | string | Title of navigation link that will be visible on UI |
tooltip | string | Tooltip of navigation link that will be visible on mouse hover |
popupProperties | { width: string, height: string } | Configuration for popup |
isVisible | ({userProfile: UserInfo}) => boolean | Function to determine if the menu item should be visible, it receives the current user and should return a boolean value (show/hide) |
icon | ComponentType | Component that will be rendered in place of the icon |
component | ComponentType | Component that will be rendered after mouse click |
Action
Custom action button that might be used for executing custom logic for selected items.Registration of new navigation items is required to call the builder function builder.header.builder.action.addExplorerAction
builder.action.addExplorerAction(() => ({
id: 'id',
title: 'title',
tooltip: '',
isVisible: (props) => {
return true;
},
icon: CustomIcon,
hook: useCustomHook,
}));
Props
Name | Type | Description |
---|---|---|
id | string | Unique Id for action button used by system to identify it |
title | string | Title of navigation action button will be visible on UI |
tooltip | string | Tooltip of action button that will be visible on mouse hover |
isVisible | ({activeView: ViewType, userProfile: UserInfo}) => boolean | Function to determine if the action button should be visible, it receives the current user and should return a boolean value (show/hide) |
icon | ComponentType | Component that will be rendered in place of the icon |
hook | function | Hook that will be used for this action. |
Object Insight
The object right side panel is representing a custom data view for one or multiple selected items. Registration of new object insight is required to call the builder function builder.objectInsight.addObjectInsightItem
builder.objectInsight.addObjectInsightItem(() => ({
id: 'id',
tooltip: 'tooltip',
isVisible: (props) => {
return false;
},
icon: CustomIcon,
component: CustomComponent,
}));
Props
Name | Type | Description |
---|---|---|
id | string | Unique Id for insight panel used by system to identify it |
tooltip | string | Tooltip for insight panel that will be visible on mouse hover |
isVisible | ({activeView: ViewType, userProfile: UserInfo}) => boolean | Function to determine if the panel should be visible, it receives the current user and should return a boolean value (show/hide) |
icon | ComponentType | Component that will be rendered in place of the icon |
component | ComponentType | Component that will be rendered after mouse click |
Types Explanations
PositionType
{
item: 'settings',
positioningType: PositioningType.addAfter,
}
Name | Type | Description | |
---|---|---|---|
item | string | Id of the navigation item that will be used for positioning. Default navigation items ('content', 'events', 'settings'), also you can use a previously added custom navigation items id | |
PositioningType | PositioningType | string | Indicate where should be located navigation item relative to previously selected item (after or before ) |
Translations
Translation records can be registered per language by using the function builder.translations.addTranslation
builder.translations.addTranslation('en', {
key: 'EN Value',
});
builder.translations.addTranslation('es', {
key: 'ES Value',
});
//....
Usage
Import t
function returned by createExtensionGlobals
and pass it the translation key.
`Hello in, {t('key')}`
Supported language
Name | Code |
---|---|
English | en |
Deutsch | de |
Spanish | es |
French | fr |
Italian | it |
Japanese | ja |
Chinese (Simplified) | zh |
Generate open api typescript client from the swagger
- Put a swagger-generated file
spec.json
inside your project for examplesrc/oapi/spec
. You can skip this step if you want to use direct link to the swagger spec. - As an example, for the generation we are going to use
nswag
(https://www.npmjs.com/package/nswag). You can install it by running the commandnpm i nswag -D
(-D
indicates that it should be a dev dependency) - Add generate command into the
package.json
Add build client command into package.json file
You can put client build command in the package.json
file in the scripts
section.
example for a local swagger spec file
"scripts": {
// ...
"generate-client": "nswag openapi2tsclient /input:./src/oApi/spec/spec.json /output:src/oApi/client/api-client.ts"
},
example for a remote swagger spec file
"scripts": {
// ...
"generate-client": "nswag openapi2tsclient /input:https:/...../api-docs/v3/spec.json /output:src/oApi/client/api-client.ts"
},
openapi2tsclient
- Generates TypeScript client code from a Swagger/OpenAPI/input:
- path to the spec.json file. (direct url to the spec.json can be specified)/output:
- where to put generate file
Generating client
npm run "generate-client
will generate a typescript client in the folder specified in output
parameter.
Usage of generated client
import { Client } from 'oApi/client/api-client';
const client = new Client();
client
.getApplicationVersion()
.then(data => {
// logic is here
})
.catch(error => {
// error handling is here
});
How to get baseUrl
All endpoints added via backend extensions will be accessible by the following URL https://[domain]/[instance]/OrganizeSpace/Extensions. To pass correct baseUrl
while instantiating nswag client you can use following example.
import { Client } from 'oApi/client/api-client';
const getExtensionsBaseUrl = () => {
const re = new RegExp('/.*?(OrganizeSpace)', 'i');
const regExpResult = window.location.pathname.match(re);
const base = regExpResult ? `${regExpResult[0]}/Extensions` : '';
return base;
};
const baseUrl = getExtensionsBaseUrl();
const client = new Client(baseUrl);