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
4 changes: 2 additions & 2 deletions .storybook/manager.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
import { addons } from 'storybook/manager-api';
// eslint-disable-next-line import-x/extensions
import { create } from 'storybook/theming';
import { generateDocsHref } from '../src/lib/utils.js';
import { getDocUrl } from '../src/lib/dev-hub-url.js';
import { enhanceStoryName } from '../src/stories/lib/story-names.js';

// We could create an addon to provide a control that would switch between dark / light
Expand All @@ -12,7 +12,7 @@ const isDarkMode = window.matchMedia('(prefers-color-scheme: dark)').matches;
const cleverTheme = create({
base: isDarkMode ? 'dark' : 'light',
brandTitle: 'Clever Cloud components',
brandUrl: generateDocsHref(),
brandUrl: getDocUrl(),
brandImage: isDarkMode ? 'imgs/logo-clever-dark.svg' : 'imgs/logo-clever-light.svg',
});

Expand Down
130 changes: 130 additions & 0 deletions docs/contributing/urls.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,130 @@
---
kind: '👋 Contributing'
title: 'Documentation and asset URLs'
---

# Documentation and asset URLs

When working on components, you'll often need to link to documentation, developer hub pages, or static assets. This guide explains how to handle these URLs correctly.

## Documentation URLs

Use `getDocUrl()` to generate URLs for documentation pages on the Clever Cloud developer hub.

### Import the helper

```js
import { getDocUrl } from '../../lib/dev-hub-url.js';
```

<cc-notice intent="info" message="The path to `dev-hub-url.js` is relative to your component's file."></cc-notice>

### Use the helper

```js
const url = getDocUrl('/billing/unified-invoices');
// => 'https://www.clever.cloud/developers/doc/billing/unified-invoices'

// Empty argument defaults to doc root
const url = getDocUrl();
// => 'https://www.clever.cloud/developers/doc'
```

**_🧐 OBSERVATIONS:_**

* Always use a leading slash: `getDocUrl('/path/to/doc')`.
* You can use the helper directly in templates, as a constant, or in translation files.
* The helper preserves query parameters and URL fragments.

### Usage in translation files

You can use `getDocUrl()` in translation files to generate documentation links:

```js
import { getDocUrl } from '../lib/dev-hub-url.js';

export const translations = {
'my-component.doc-link': () => sanitize`
Read more in the <a href="${getDocUrl('/path/to/doc')}">documentation</a>.
`,
};
```

## Developer hub URLs

Use `getDevHubUrl()` to generate URLs for general developer hub pages (not under `/doc/`).

### Import the helper

```js
import { getDevHubUrl } from '../../lib/dev-hub-url.js';
```

### Use the helper

```js
const url = getDevHubUrl('/api/v4');
// => 'https://www.clever.cloud/developers/api/v4'

// Supports fragments and query params
const url = getDevHubUrl('/api/howto/#oauth1');
// => 'https://www.clever.cloud/developers/api/howto/#oauth1'
```

## Asset URLs

Use `getAssetUrl()` to generate URLs for static assets that are not part of the components project.

### Import the helper

```js
import { getAssetUrl } from '../../lib/assets-url.js';
```

### Use the helper

```js
const url = getAssetUrl('/logos/java-jar.svg');
// => 'https://assets.clever-cloud.com/logos/java-jar.svg'

// Preserves query parameters and fragments
const url = getAssetUrl('/images/logo.png?v=2');
// => 'https://assets.clever-cloud.com/images/logo.png?v=2'
```

### Helper functions for common assets

The `remote-assets.js` file provides convenient wrapper functions for frequently used assets:

```js
import { getFlagUrl, getInfraProviderLogoUrl } from '../../lib/remote-assets.js';

// Country flags
const flag = getFlagUrl('fr');
// => 'https://assets.clever-cloud.com/flags/fr.svg'

// Infrastructure provider logos
const logo = getInfraProviderLogoUrl('ovh');
// => 'https://assets.clever-cloud.com/infra/ovh.svg'
```

## Best practices

### Always use the helpers

**DON'T DO THIS:**

```js
// ❌ Hardcoded URLs
const url = 'https://www.clever.cloud/developers/doc/addons/postgresql';
const asset = 'https://assets.clever-cloud.com/logos/java-jar.svg';
```

**DO THIS:**

```js
// ✅ Use the helpers
const url = getDocUrl('/addons/postgresql');
const asset = getAssetUrl('/logos/java-jar.svg');
```

68 changes: 68 additions & 0 deletions docs/getting-started/custom-base-urls.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
---
kind: '🚀 Getting started'
title: 'Customizing base URLs'
---

# Customizing base URLs

By default, Clever Cloud components generate URLs that point to:
* `https://www.clever.cloud/developers` for documentation and developer hub links
* `https://assets.clever-cloud.com` for static assets

You can override these base URLs.

## Customizing the developer hub base URL

Use `setDevHubBaseUrl()` to change the base URL for documentation and developer hub links.

```js
import { setDevHubBaseUrl } from '@clevercloud/components/dist/dev-hub-url.js';

// Set a custom base URL
setDevHubBaseUrl('https://staging.clever-cloud.com/developers');

// Now import components
import '@clevercloud/components/dist/cc-addon-info.js';
```

After this configuration, all documentation links generated by components will use your custom base URL:

* `getDocUrl('/addons/postgresql')` → `https://staging.clever-cloud.com/developers/doc/addons/postgresql`
* `getDevHubUrl('/api/v4')` → `https://staging.clever-cloud.com/developers/api/v4`

## Customizing the assets base URL

Use `setAssetsBaseUrl()` to change the base URL for static assets like logos, flags, and icons.

```js
import { setAssetsBaseUrl } from '@clevercloud/components/dist/assets-url.js';

// Set a custom assets URL
setAssetsBaseUrl('https://cdn.example.com');

// Now import components
import '@clevercloud/components/dist/cc-addon-info.js';
```

After this configuration, all asset URLs generated by components will use your custom base URL:

* `getAssetUrl('/logos/java-jar.svg')` → `https://cdn.example.com/logos/java-jar.svg`
* `getFlagUrl('fr')` → `https://cdn.example.com/flags/fr.svg`

## Important: call before importing components

You **must** call `setDevHubBaseUrl()` and `setAssetsBaseUrl()` **before** importing any components. Otherwise, some URLs may still use the default base URLs.

```js
import { setDevHubBaseUrl } from '@clevercloud/components/dist/dev-hub-url.js';
import { setAssetsBaseUrl } from '@clevercloud/components/dist/assets-url.js';

// Configure base URLs first
setDevHubBaseUrl('https://staging.clever-cloud.com/developers');
setAssetsBaseUrl('https://cdn.example.com');

// Then import components
import '@clevercloud/components/dist/cc-addon-info.js';
import '@clevercloud/components/dist/cc-invoice-list.js';
```

Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { getDocUrl } from '../../lib/dev-hub-url.js';
import { notifyError, notifySuccess } from '../../lib/notifications.js';
import { defineSmartComponent } from '../../lib/smart/define-smart-component.js';
import { generateDocsHref } from '../../lib/utils.js';
import { i18n } from '../../translations/translation.js';
import '../cc-smart-container/cc-smart-container.js';
import { CcAddonCredentialsBetaClient } from './cc-addon-credentials-beta.client.js';
Expand Down Expand Up @@ -30,7 +30,7 @@ const LOADING_STATE = {
],
docLink: {
text: i18n('cc-addon-credentials-beta.doc-link.keycloak'),
href: generateDocsHref('/addons/keycloak/#secured-multi-instances'),
href: getDocUrl('/addons/keycloak/#secured-multi-instances'),
},
},
},
Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import { getDocUrl } from '../../lib/dev-hub-url.js';
import { fakeString } from '../../lib/fake-strings.js';
import { notifyError, notifySuccess } from '../../lib/notifications.js';
import { defineSmartComponent } from '../../lib/smart/define-smart-component.js';
import { generateDocsHref } from '../../lib/utils.js';
import { i18n } from '../../translations/translation.js';
import '../cc-smart-container/cc-smart-container.js';
import { CcAddonCredentialsBetaClient } from './cc-addon-credentials-beta.client.js';
Expand Down Expand Up @@ -29,7 +29,7 @@ const LOADING_STATE = {
],
docLink: {
text: i18n('cc-addon-credentials-beta.doc-link.otoroshi-ng'),
href: generateDocsHref('/addons/otoroshi/#use-otoroshi-in-a-network-group'),
href: getDocUrl('/addons/otoroshi/#use-otoroshi-in-a-network-group'),
},
},
api: {
Expand Down Expand Up @@ -58,7 +58,7 @@ const LOADING_STATE = {
],
docLink: {
text: i18n('cc-addon-credentials-beta.doc-link.otoroshi-api'),
href: generateDocsHref('/addons/otoroshi/#manage-otoroshi-from-its-api'),
href: getDocUrl('/addons/otoroshi/#manage-otoroshi-from-its-api'),
},
},
},
Expand Down
Original file line number Diff line number Diff line change
@@ -1,13 +1,13 @@
import { getDocUrl } from '../../lib/dev-hub-url.js';
import { fakeString } from '../../lib/fake-strings.js';
import { notify, notifyError } from '../../lib/notifications.js';
import { defineSmartComponent } from '../../lib/smart/define-smart-component.js';
import { generateDocsHref } from '../../lib/utils.js';
import { i18n } from '../../translations/translation.js';
import '../cc-smart-container/cc-smart-container.js';
import { CcAddonHeaderClient } from './cc-addon-header.client.js';
import './cc-addon-header.js';

const DOCS_URL = generateDocsHref(`/addons/keycloak`);
const DOCS_URL = getDocUrl(`/addons/keycloak`);
const PROVIDER_ID = 'keycloak';

/**
Expand Down
Original file line number Diff line number Diff line change
@@ -1,13 +1,13 @@
import { getDocUrl } from '../../lib/dev-hub-url.js';
import { fakeString } from '../../lib/fake-strings.js';
import { notify, notifyError } from '../../lib/notifications.js';
import { defineSmartComponent } from '../../lib/smart/define-smart-component.js';
import { generateDocsHref } from '../../lib/utils.js';
import { i18n } from '../../translations/translation.js';
import '../cc-smart-container/cc-smart-container.js';
import { CcAddonHeaderClient } from './cc-addon-header.client.js';
import './cc-addon-header.js';

const DOCS_URL = generateDocsHref(`/addons/matomo`);
const DOCS_URL = getDocUrl(`/addons/matomo`);
const PROVIDER_ID = 'matomo';

/**
Expand Down
Original file line number Diff line number Diff line change
@@ -1,13 +1,13 @@
import { getDocUrl } from '../../lib/dev-hub-url.js';
import { fakeString } from '../../lib/fake-strings.js';
import { notify, notifyError } from '../../lib/notifications.js';
import { defineSmartComponent } from '../../lib/smart/define-smart-component.js';
import { generateDocsHref } from '../../lib/utils.js';
import { i18n } from '../../translations/translation.js';
import '../cc-smart-container/cc-smart-container.js';
import { CcAddonHeaderClient } from './cc-addon-header.client.js';
import './cc-addon-header.js';

const DOCS_URL = generateDocsHref(`/addons/metabase`);
const DOCS_URL = getDocUrl(`/addons/metabase`);
const PROVIDER_ID = 'metabase';

/**
Expand Down
Original file line number Diff line number Diff line change
@@ -1,13 +1,13 @@
import { getDocUrl } from '../../lib/dev-hub-url.js';
import { fakeString } from '../../lib/fake-strings.js';
import { notify, notifyError } from '../../lib/notifications.js';
import { defineSmartComponent } from '../../lib/smart/define-smart-component.js';
import { generateDocsHref } from '../../lib/utils.js';
import { i18n } from '../../translations/translation.js';
import '../cc-smart-container/cc-smart-container.js';
import { CcAddonHeaderClient } from './cc-addon-header.client.js';
import './cc-addon-header.js';

const DOCS_URL = generateDocsHref(`/addons/otoroshi`);
const DOCS_URL = getDocUrl(`/addons/otoroshi`);
const PROVIDER_ID = 'otoroshi';

/**
Expand Down
4 changes: 2 additions & 2 deletions src/components/cc-addon-info/cc-addon-info.client.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,8 @@ import { get as getAddon } from '@clevercloud/client/esm/api/v2/addon.js';
import { getGrafanaOrganisation } from '@clevercloud/client/esm/api/v4/saas.js';
// @ts-expect-error FIXME: remove when clever-client exports types
import { ONE_SECOND } from '@clevercloud/client/esm/with-cache.js';
import { getDevHubUrl } from '../../lib/dev-hub-url.js';
import { sendToApi } from '../../lib/send-to-api.js';
import { generateDevHubHref } from '../../lib/utils.js';

/**
* @typedef {import('./cc-addon-info.types.js').AddonInfoStateLoaded} AddonInfoStateLoaded
Expand Down Expand Up @@ -178,7 +178,7 @@ export function formatVersionState(operatorVersionInfo) {
installed: operatorVersionInfo.installed,
available: operatorVersionInfo.available.filter((version) => version !== operatorVersionInfo.installed),
latest: operatorVersionInfo.latest,
changelogLink: `${generateDevHubHref('/changelog')}`,
changelogLink: `${getDevHubUrl('/changelog')}`,
};
}

Expand Down
4 changes: 2 additions & 2 deletions src/components/cc-addon-info/cc-addon-info.smart-keycloak.js
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import { getAssetUrl } from '../../lib/assets-url.js';
import { getDocUrl } from '../../lib/dev-hub-url.js';
import { notifyError, notifySuccess } from '../../lib/notifications.js';
import { defineSmartComponent } from '../../lib/smart/define-smart-component.js';
import { generateDocsHref } from '../../lib/utils.js';
import { i18n } from '../../translations/translation.js';
import '../cc-smart-container/cc-smart-container.js';
import { CcAddonInfoClient, formatVersionState } from './cc-addon-info.client.js';
Expand Down Expand Up @@ -72,7 +72,7 @@ defineSmartComponent({
updateComponent('state', LOADING_STATE);
updateComponent('docLink', {
text: i18n('cc-addon-info.doc-link.keycloak'),
href: generateDocsHref('/addons/keycloak'),
href: getDocUrl('/addons/keycloak'),
});

api
Expand Down
4 changes: 2 additions & 2 deletions src/components/cc-addon-info/cc-addon-info.smart-matomo.js
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import { getAssetUrl } from '../../lib/assets-url.js';
import { getDocUrl } from '../../lib/dev-hub-url.js';
import { sendToApi } from '../../lib/send-to-api.js';
import { defineSmartComponent } from '../../lib/smart/define-smart-component.js';
import { generateDocsHref } from '../../lib/utils.js';
import { i18n } from '../../translations/translation.js';
import '../cc-smart-container/cc-smart-container.js';
import { CcAddonInfoClient } from './cc-addon-info.client.js';
Expand Down Expand Up @@ -87,7 +87,7 @@ defineSmartComponent({
updateComponent('state', LOADING_STATE);
updateComponent('docLink', {
text: i18n('cc-addon-info.doc-link.matomo'),
href: generateDocsHref('/addons/matomo'),
href: getDocUrl('/addons/matomo'),
});

api
Expand Down
4 changes: 2 additions & 2 deletions src/components/cc-addon-info/cc-addon-info.smart-metabase.js
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import { getAssetUrl } from '../../lib/assets-url.js';
import { getDocUrl } from '../../lib/dev-hub-url.js';
import { notifyError, notifySuccess } from '../../lib/notifications.js';
import { defineSmartComponent } from '../../lib/smart/define-smart-component.js';
import { generateDocsHref } from '../../lib/utils.js';
import { i18n } from '../../translations/translation.js';
import '../cc-smart-container/cc-smart-container.js';
import { CcAddonInfoClient, formatVersionState } from './cc-addon-info.client.js';
Expand Down Expand Up @@ -82,7 +82,7 @@ defineSmartComponent({
updateComponent('state', LOADING_STATE);
updateComponent('docLink', {
text: i18n('cc-addon-info.doc-link.metabase'),
href: generateDocsHref('/addons/metabase'),
href: getDocUrl('/addons/metabase'),
});

api
Expand Down
Loading
Loading