Merge branch 'master' into pbarnett/update-enterprise-config-options

pull/6130/head
Jason Stirnaman 2025-06-10 09:15:27 -05:00 committed by GitHub
commit 8478d7bbc9
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
68 changed files with 19749 additions and 731 deletions

View File

@ -1,4 +1,4 @@
version: 2
version: 2.1
jobs:
build:
docker:
@ -41,7 +41,7 @@ jobs:
- /home/circleci/bin
- run:
name: Hugo Build
command: npx hugo --logLevel info --minify --destination workspace/public
command: yarn hugo --environment production --logLevel info --gc --destination workspace/public
- persist_to_workspace:
root: workspace
paths:
@ -68,7 +68,6 @@ jobs:
when: on_success
workflows:
version: 2
build:
jobs:
- build

View File

@ -1679,7 +1679,7 @@ The shortcode takes a regular expression for matching placeholder names.
Use the `code-placeholder-key` shortcode to format the placeholder names in
text that describes the placeholder--for example:
```
```markdown
{{% code-placeholders "DATABASE_NAME|USERNAME|PASSWORD_OR_TOKEN|API_TOKEN|exampleuser@influxdata.com" %}}
```sh
curl --request POST http://localhost:8086/write?db=DATABASE_NAME \
@ -1703,3 +1703,83 @@ InfluxDB API documentation when documentation is deployed.
Redoc generates HTML documentation using the InfluxDB `swagger.yml`.
For more information about generating InfluxDB API documentation, see the
[API Documentation README](https://github.com/influxdata/docs-v2/tree/master/api-docs#readme).
## JavaScript in the documentation UI
The InfluxData documentation UI uses JavaScript with ES6+ syntax and
`assets/js/main.js` as the entry point to import modules from
`assets/js`.
Only `assets/js/main.js` should be imported in HTML files.
`assets/js/main.js` registers components and initializes them on page load.
If you're adding UI functionality that requires JavaScript, follow these steps:
1. In your HTML file, add a `data-component` attribute to the element that
should be initialized by your JavaScript code. For example:
```html
<div data-component="my-component"></div>
```
2. Following the component pattern, create a single-purpose JavaScript module
(`assets/js/components/my-component.js`)
that exports a single function that receives the component element and initializes it.
3. In `assets/js/main.js`, import the module and register the component to ensure
the component is initialized on page load.
### Debugging JavaScript
To debug JavaScript code used in the InfluxData documentation UI, choose one of the following methods:
- Use source maps and the Chrome DevTools debugger.
- Use debug helpers that provide breakpoints and console logging as a workaround or alternative for using source maps and the Chrome DevTools debugger.
#### Using source maps and Chrome DevTools debugger
1. In VS Code, select Run > Start Debugging.
2. Select the "Debug Docs (source maps)" configuration.
3. Click the play button to start the debugger.
5. Set breakpoints in the JavaScript source files--files in the
`assets/js/ns-hugo-imp:` namespace-- in the
VS Code editor or in the Chrome Developer Tools Sources panel:
- In the VS Code Debugger panel > "Loaded Scripts" section, find the
`assets/js/ns-hugo-imp:` namespace.
- In the Chrome Developer Tools Sources panel, expand
`js/ns-hugo-imp:/<YOUR_WORKSPACE_ROOT>/assets/js/`.
#### Using debug helpers
1. In your JavaScript module, import debug helpers from `assets/js/utils/debug-helpers.js`.
These helpers provide breakpoints and console logging as a workaround or alternative for
using source maps and the Chrome DevTools debugger.
2. Insert debug statements by calling the helper functions in your code--for example:
```js
import { debugLog, debugBreak, debugInspect } from './utils/debug-helpers.js';
const data = debugInspect(someData, 'Data');
debugLog('Processing data', 'myFunction');
function processData() {
// Add a breakpoint that works with DevTools
debugBreak();
// Your existing code...
}
```
3. Start Hugo in development mode--for example:
```bash
yarn hugo server
```
4. In VS Code, go to Run > Start Debugging, and select the "Debug JS (debug-helpers)" configuration.
Your system uses the configuration in `launch.json` to launch the site in Chrome
and attach the debugger to the Developer Tools console.
Make sure to remove the debug statements before merging your changes.
The debug helpers are designed to be used in development and should not be used in production.

1
.gitignore vendored
View File

@ -21,6 +21,7 @@ node_modules
test-results.xml
/influxdb3cli-build-scripts/content
.vscode/*
!.vscode/launch.json
.idea
**/config.toml
package-lock.json

View File

@ -3,3 +3,4 @@
**/.svn
**/.hg
**/node_modules
assets/jsconfig.json

47
.vscode/launch.json vendored Normal file
View File

@ -0,0 +1,47 @@
{
"version": "0.2.0",
"configurations": [
{
"name": "Debug JS (debug-helpers)",
"type": "chrome",
"request": "launch",
"url": "http://localhost:1313",
"webRoot": "${workspaceFolder}",
"skipFiles": [
"<node_internals>/**"
],
"sourceMaps": false,
"trace": true,
"smartStep": false
},
{
"name": "Debug JS (source maps)",
"type": "chrome",
"request": "launch",
"url": "http://localhost:1313",
"webRoot": "${workspaceFolder}",
"sourceMaps": true,
"sourceMapPathOverrides": {
"*": "${webRoot}/assets/js/*",
"main.js": "${webRoot}/assets/js/main.js",
"page-context.js": "${webRoot}/assets/js/page-context.js",
"ask-ai-trigger.js": "${webRoot}/assets/js/ask-ai-trigger.js",
"ask-ai.js": "${webRoot}/assets/js/ask-ai.js",
"utils/*": "${webRoot}/assets/js/utils/*",
"services/*": "${webRoot}/assets/js/services/*"
},
"skipFiles": [
"<node_internals>/**",
"node_modules/**",
"chrome-extension://**"
],
"trace": true,
"smartStep": true,
"disableNetworkCache": true,
"userDataDir": "${workspaceFolder}/.vscode/chrome-user-data",
"runtimeArgs": [
"--disable-features=VizDisplayCompositor"
]
},
]
}

View File

@ -1667,7 +1667,7 @@ The shortcode takes a regular expression for matching placeholder names.
Use the `code-placeholder-key` shortcode to format the placeholder names in
text that describes the placeholder--for example:
```
```markdown
{{% code-placeholders "DATABASE_NAME|USERNAME|PASSWORD_OR_TOKEN|API_TOKEN|exampleuser@influxdata.com" %}}
```sh
curl --request POST http://localhost:8086/write?db=DATABASE_NAME \
@ -1691,3 +1691,83 @@ InfluxDB API documentation when documentation is deployed.
Redoc generates HTML documentation using the InfluxDB `swagger.yml`.
For more information about generating InfluxDB API documentation, see the
[API Documentation README](https://github.com/influxdata/docs-v2/tree/master/api-docs#readme).
## JavaScript in the documentation UI
The InfluxData documentation UI uses JavaScript with ES6+ syntax and
`assets/js/main.js` as the entry point to import modules from
`assets/js`.
Only `assets/js/main.js` should be imported in HTML files.
`assets/js/main.js` registers components and initializes them on page load.
If you're adding UI functionality that requires JavaScript, follow these steps:
1. In your HTML file, add a `data-component` attribute to the element that
should be initialized by your JavaScript code. For example:
```html
<div data-component="my-component"></div>
```
2. Following the component pattern, create a single-purpose JavaScript module
(`assets/js/components/my-component.js`)
that exports a single function that receives the component element and initializes it.
3. In `assets/js/main.js`, import the module and register the component to ensure
the component is initialized on page load.
### Debugging JavaScript
To debug JavaScript code used in the InfluxData documentation UI, choose one of the following methods:
- Use source maps and the Chrome DevTools debugger.
- Use debug helpers that provide breakpoints and console logging as a workaround or alternative for using source maps and the Chrome DevTools debugger.
#### Using source maps and Chrome DevTools debugger
1. In VS Code, select Run > Start Debugging.
2. Select the "Debug Docs (source maps)" configuration.
3. Click the play button to start the debugger.
5. Set breakpoints in the JavaScript source files--files in the
`assets/js/ns-hugo-imp:` namespace-- in the
VS Code editor or in the Chrome Developer Tools Sources panel:
- In the VS Code Debugger panel > "Loaded Scripts" section, find the
`assets/js/ns-hugo-imp:` namespace.
- In the Chrome Developer Tools Sources panel, expand
`js/ns-hugo-imp:/<YOUR_WORKSPACE_ROOT>/assets/js/`.
#### Using debug helpers
1. In your JavaScript module, import debug helpers from `assets/js/utils/debug-helpers.js`.
These helpers provide breakpoints and console logging as a workaround or alternative for
using source maps and the Chrome DevTools debugger.
2. Insert debug statements by calling the helper functions in your code--for example:
```js
import { debugLog, debugBreak, debugInspect } from './utils/debug-helpers.js';
const data = debugInspect(someData, 'Data');
debugLog('Processing data', 'myFunction');
function processData() {
// Add a breakpoint that works with DevTools
debugBreak();
// Your existing code...
}
```
3. Start Hugo in development mode--for example:
```bash
yarn hugo server
```
4. In VS Code, go to Run > Start Debugging, and select the "Debug JS (debug-helpers)" configuration.
Your system uses the configuration in `launch.json` to launch the site in Chrome
and attach the debugger to the Developer Tools console.
Make sure to remove the debug statements before merging your changes.
The debug helpers are designed to be used in development and should not be used in production.

View File

@ -2,7 +2,7 @@
///////////////// Preferred Client Library programming language ///////////////
////////////////////////////////////////////////////////////////////////////////
import { activateTabs, updateBtnURLs } from './tabbed-content.js';
import { getPreference, setPreference } from './local-storage.js';
import { getPreference, setPreference } from './services/local-storage.js';
function getVisitedApiLib() {
const path = window.location.pathname.match(

View File

@ -8,29 +8,31 @@ function setUser(userid, email) {
window[NAMESPACE] = {
user: {
uniqueClientId: userid,
email: email,
}
}
email: email,
},
};
}
// Initialize the chat widget
function initializeChat({onChatLoad, chatAttributes}) {
/* See https://docs.kapa.ai/integrations/website-widget/configuration for
function initializeChat({ onChatLoad, chatAttributes }) {
/* See https://docs.kapa.ai/integrations/website-widget/configuration for
* available configuration options.
* All values are strings.
*/
// If you make changes to data attributes here, you also need to port the changes to the api-docs/template.hbs API reference template.
// If you make changes to data attributes here, you also need to
// port the changes to the api-docs/template.hbs API reference template.
const requiredAttributes = {
websiteId: 'a02bca75-1dd3-411e-95c0-79ee1139be4d',
projectName: 'InfluxDB',
projectColor: '#020a47',
projectLogo: '/img/influx-logo-cubo-white.png',
}
};
const optionalAttributes = {
modalDisclaimer: 'This AI can access [documentation for InfluxDB, clients, and related tools](https://docs.influxdata.com). Information you submit is used in accordance with our [Privacy Policy](https://www.influxdata.com/legal/privacy-policy/).',
modalExampleQuestions: 'Use Python to write data to InfluxDB 3,How do I query using SQL?,How do I use MQTT with Telegraf?',
modalDisclaimer:
'This AI can access [documentation for InfluxDB, clients, and related tools](https://docs.influxdata.com). Information you submit is used in accordance with our [Privacy Policy](https://www.influxdata.com/legal/privacy-policy/).',
modalExampleQuestions:
'Use Python to write data to InfluxDB 3,How do I query using SQL?,How do I use MQTT with Telegraf?',
buttonHide: 'true',
exampleQuestionButtonWidth: 'auto',
modalOpenOnCommandK: 'true',
@ -52,28 +54,32 @@ function initializeChat({onChatLoad, chatAttributes}) {
modalHeaderBorderBottom: 'none',
modalTitleColor: '#fff',
modalTitleFontSize: '1.25rem',
}
};
const scriptUrl = 'https://widget.kapa.ai/kapa-widget.bundle.js';
const script = document.createElement('script');
script.async = true;
script.src = scriptUrl;
script.onload = function() {
script.onload = function () {
onChatLoad();
window.influxdatadocs.AskAI = AskAI;
};
script.onerror = function() {
script.onerror = function () {
console.error('Error loading AI chat widget script');
};
const dataset = {...requiredAttributes, ...optionalAttributes, ...chatAttributes};
Object.keys(dataset).forEach(key => {
// Assign dataset attributes from the object
const dataset = {
...requiredAttributes,
...optionalAttributes,
...chatAttributes,
};
Object.keys(dataset).forEach((key) => {
// Assign dataset attributes from the object
script.dataset[key] = dataset[key];
});
// Check for an existing script element to remove
const oldScript= document.querySelector(`script[src="${scriptUrl}"]`);
const oldScript = document.querySelector(`script[src="${scriptUrl}"]`);
if (oldScript) {
oldScript.remove();
}
@ -82,22 +88,21 @@ function initializeChat({onChatLoad, chatAttributes}) {
function getProductExampleQuestions() {
const questions = productData?.product?.ai_sample_questions;
return questions?.join(',') || '';
return questions?.join(',') || '';
}
/**
/**
* chatParams: specify custom (for example, page-specific) attribute values for the chat, pass the dataset key-values (collected in ...chatParams). See https://docs.kapa.ai/integrations/website-widget/configuration for available configuration options.
* onChatLoad: function to call when the chat widget has loaded
* userid: optional, a unique user ID for the user (not currently used for public docs)
*/
*/
export default function AskAI({ userid, email, onChatLoad, ...chatParams }) {
const modalExampleQuestions = getProductExampleQuestions();
const chatAttributes = {
...(modalExampleQuestions && { modalExampleQuestions }),
...chatParams,
}
initializeChat({onChatLoad, chatAttributes});
};
initializeChat({ onChatLoad, chatAttributes });
if (userid) {
setUser(userid, email);

View File

@ -2,7 +2,7 @@ import $ from 'jquery';
function initialize() {
var codeBlockSelector = '.article--content pre';
var codeBlocks = $(codeBlockSelector);
var $codeBlocks = $(codeBlockSelector);
var appendHTML = `
<div class="code-controls">
@ -15,7 +15,7 @@ function initialize() {
`;
// Wrap all codeblocks with a new 'codeblock' div
$(codeBlocks).each(function () {
$codeBlocks.each(function () {
$(this).wrap("<div class='codeblock'></div>");
});
@ -68,7 +68,9 @@ function initialize() {
// Trigger copy failure state lifecycle
$('.copy-code').click(function () {
let text = $(this).closest('.code-controls').prevAll('pre:has(code)')[0].innerText;
let text = $(this)
.closest('.code-controls')
.prevAll('pre:has(code)')[0].innerText;
const copyContent = async () => {
try {
@ -90,7 +92,10 @@ Disable scrolling on the body.
Disable user selection on everything but the fullscreen codeblock.
*/
$('.fullscreen-toggle').click(function () {
var code = $(this).closest('.code-controls').prevAll('pre:has(code)').clone();
var code = $(this)
.closest('.code-controls')
.prevAll('pre:has(code)')
.clone();
$('#fullscreen-code-placeholder').replaceWith(code[0]);
$('body').css('overflow', 'hidden');

View File

@ -0,0 +1,78 @@
// Memoize the mermaid module import
let mermaidPromise = null;
export default function Diagram({ component }) {
// Import mermaid.js module (memoized)
if (!mermaidPromise) {
mermaidPromise = import('mermaid');
}
mermaidPromise
.then(({ default: mermaid }) => {
// Configure mermaid with InfluxData theming
mermaid.initialize({
startOnLoad: false, // We'll manually call run()
theme: document.body.classList.contains('dark-theme')
? 'dark'
: 'default',
themeVariables: {
fontFamily: 'Proxima Nova',
fontSize: '16px',
lineColor: '#22ADF6',
primaryColor: '#22ADF6',
primaryTextColor: '#545454',
secondaryColor: '#05CE78',
tertiaryColor: '#f4f5f5',
},
securityLevel: 'loose', // Required for interactive diagrams
logLevel: 'error',
});
// Process the specific diagram component
try {
mermaid.run({ nodes: [component] });
} catch (error) {
console.error('Mermaid diagram rendering error:', error);
}
// Store reference to mermaid for theme switching
if (!window.mermaidInstances) {
window.mermaidInstances = new Map();
}
window.mermaidInstances.set(component, mermaid);
})
.catch((error) => {
console.error('Failed to load Mermaid library:', error);
});
// Listen for theme changes to refresh diagrams
const observer = new MutationObserver((mutations) => {
mutations.forEach((mutation) => {
if (
mutation.attributeName === 'class' &&
document.body.classList.contains('dark-theme') !== window.isDarkTheme
) {
window.isDarkTheme = document.body.classList.contains('dark-theme');
// Reload this specific diagram with new theme
if (window.mermaidInstances?.has(component)) {
const mermaid = window.mermaidInstances.get(component);
mermaid.initialize({
theme: window.isDarkTheme ? 'dark' : 'default',
});
mermaid.run({ nodes: [component] });
}
}
});
});
// Watch for theme changes on body element
observer.observe(document.body, { attributes: true });
// Return cleanup function to be called when component is destroyed
return () => {
observer.disconnect();
if (window.mermaidInstances?.has(component)) {
window.mermaidInstances.delete(component);
}
};
}

View File

@ -0,0 +1,180 @@
/**
* DocSearch component for InfluxData documentation
* Handles asynchronous loading and initialization of Algolia DocSearch
*/
const debug = false; // Set to true for debugging output
export default function DocSearch({ component }) {
// Store configuration from component data attributes
const config = {
apiKey: component.getAttribute('data-api-key'),
appId: component.getAttribute('data-app-id'),
indexName: component.getAttribute('data-index-name'),
inputSelector: component.getAttribute('data-input-selector'),
searchTag: component.getAttribute('data-search-tag'),
includeFlux: component.getAttribute('data-include-flux') === 'true',
includeResources:
component.getAttribute('data-include-resources') === 'true',
debug: component.getAttribute('data-debug') === 'true',
};
// Initialize global object to track DocSearch state
window.InfluxDocs = window.InfluxDocs || {};
window.InfluxDocs.search = {
initialized: false,
options: config,
};
// Load DocSearch asynchronously
function loadDocSearch() {
if (debug) {
console.log('Loading DocSearch script...');
}
const script = document.createElement('script');
script.src =
'https://cdn.jsdelivr.net/npm/docsearch.js@2/dist/cdn/docsearch.min.js';
script.async = true;
script.onload = initializeDocSearch;
document.body.appendChild(script);
}
// Initialize DocSearch after script loads
function initializeDocSearch() {
if (debug) {
console.log('Initializing DocSearch...');
}
const multiVersion = ['influxdb'];
// Use object-based lookups instead of conditionals for version and product names
// These can be replaced with data from productData in the future
// Version display name mappings
const versionDisplayNames = {
cloud: 'Cloud (TSM)',
core: 'Core',
enterprise: 'Enterprise',
'cloud-serverless': 'Cloud Serverless',
'cloud-dedicated': 'Cloud Dedicated',
clustered: 'Clustered',
explorer: 'Explorer',
};
// Product display name mappings
const productDisplayNames = {
influxdb: 'InfluxDB',
influxdb3: 'InfluxDB 3',
explorer: 'InfluxDB 3 Explorer',
enterprise_influxdb: 'InfluxDB Enterprise',
flux: 'Flux',
telegraf: 'Telegraf',
chronograf: 'Chronograf',
kapacitor: 'Kapacitor',
platform: 'InfluxData Platform',
resources: 'Additional Resources',
};
// Initialize DocSearch with configuration
window.docsearch({
apiKey: config.apiKey,
appId: config.appId,
indexName: config.indexName,
inputSelector: config.inputSelector,
debug: config.debug,
transformData: function (hits) {
// Format version using object lookup instead of if-else chain
function fmtVersion(version, productKey) {
if (version == null) {
return '';
} else if (versionDisplayNames[version]) {
return versionDisplayNames[version];
} else if (multiVersion.includes(productKey)) {
return version;
} else {
return '';
}
}
hits.map((hit) => {
const pathData = new URL(hit.url).pathname
.split('/')
.filter((n) => n);
const product = productDisplayNames[pathData[0]] || pathData[0];
const version = fmtVersion(pathData[1], pathData[0]);
hit.product = product;
hit.version = version;
hit.hierarchy.lvl0 =
hit.hierarchy.lvl0 +
` <span class=\"search-product-version\">${product} ${version}</span>`;
hit._highlightResult.hierarchy.lvl0.value =
hit._highlightResult.hierarchy.lvl0.value +
` <span class=\"search-product-version\">${product} ${version}</span>`;
});
return hits;
},
algoliaOptions: {
hitsPerPage: 10,
facetFilters: buildFacetFilters(config),
},
autocompleteOptions: {
templates: {
header:
'<div class="search-all-content"><a href="https:\/\/support.influxdata.com" target="_blank">Search all InfluxData content <span class="icon-arrow-up-right"></span></a>',
empty:
'<div class="search-no-results"><p>Not finding what you\'re looking for?</p> <a href="https:\/\/support.influxdata.com" target="_blank">Search all InfluxData content <span class="icon-arrow-up-right"></span></a></div>',
},
},
});
// Mark DocSearch as initialized
window.InfluxDocs.search.initialized = true;
// Dispatch event for other components to know DocSearch is ready
window.dispatchEvent(new CustomEvent('docsearch-initialized'));
}
/**
* Helper function to build facet filters based on config
* - Uses nested arrays for AND conditions
* - Includes space after colon in filter expressions
*/
function buildFacetFilters(config) {
if (!config.searchTag) {
return ['latest:true'];
} else if (config.includeFlux) {
// Return a nested array to match original template structure
// Note the space after each colon
return [
[
'searchTag: ' + config.searchTag,
'flux:true',
'resources: ' + config.includeResources,
],
];
} else {
// Return a nested array to match original template structure
// Note the space after each colon
return [
[
'searchTag: ' + config.searchTag,
'resources: ' + config.includeResources,
],
];
}
}
// Load DocSearch when page is idle or after a slight delay
if ('requestIdleCallback' in window) {
requestIdleCallback(loadDocSearch);
} else {
setTimeout(loadDocSearch, 500);
}
// Return cleanup function
return function cleanup() {
// Clean up any event listeners if needed
if (debug) {
console.log('DocSearch component cleanup');
}
};
}

View File

@ -0,0 +1,6 @@
import SearchInteractions from '../utils/search-interactions.js';
export default function SidebarSearch({ component }) {
const searchInput = component.querySelector('.sidebar--search-field');
SearchInteractions({ searchInput });
}

View File

@ -1,7 +1,7 @@
import $ from 'jquery';
import { Datepicker } from 'vanillajs-datepicker';
import { toggleModal } from './modals.js';
import * as localStorage from './local-storage.js';
import * as localStorage from './services/local-storage.js';
// Placeholder start date used in InfluxDB custom timestamps
const defaultStartDate = '2022-01-01';
@ -53,65 +53,65 @@ function timeToUnixSeconds(time) {
return unixSeconds;
}
// Default time values in getting started sample data
const defaultTimes = [
{
rfc3339: `${defaultStartDate}T08:00:00Z`,
unix: timeToUnixSeconds(`${defaultStartDate}T08:00:00Z`),
}, // 1641024000
{
rfc3339: `${defaultStartDate}T09:00:00Z`,
unix: timeToUnixSeconds(`${defaultStartDate}T09:00:00Z`),
}, // 1641027600
{
rfc3339: `${defaultStartDate}T10:00:00Z`,
unix: timeToUnixSeconds(`${defaultStartDate}T10:00:00Z`),
}, // 1641031200
{
rfc3339: `${defaultStartDate}T11:00:00Z`,
unix: timeToUnixSeconds(`${defaultStartDate}T11:00:00Z`),
}, // 1641034800
{
rfc3339: `${defaultStartDate}T12:00:00Z`,
unix: timeToUnixSeconds(`${defaultStartDate}T12:00:00Z`),
}, // 1641038400
{
rfc3339: `${defaultStartDate}T13:00:00Z`,
unix: timeToUnixSeconds(`${defaultStartDate}T13:00:00Z`),
}, // 1641042000
{
rfc3339: `${defaultStartDate}T14:00:00Z`,
unix: timeToUnixSeconds(`${defaultStartDate}T14:00:00Z`),
}, // 1641045600
{
rfc3339: `${defaultStartDate}T15:00:00Z`,
unix: timeToUnixSeconds(`${defaultStartDate}T15:00:00Z`),
}, // 1641049200
{
rfc3339: `${defaultStartDate}T16:00:00Z`,
unix: timeToUnixSeconds(`${defaultStartDate}T16:00:00Z`),
}, // 1641052800
{
rfc3339: `${defaultStartDate}T17:00:00Z`,
unix: timeToUnixSeconds(`${defaultStartDate}T17:00:00Z`),
}, // 1641056400
{
rfc3339: `${defaultStartDate}T18:00:00Z`,
unix: timeToUnixSeconds(`${defaultStartDate}T18:00:00Z`),
}, // 1641060000
{
rfc3339: `${defaultStartDate}T19:00:00Z`,
unix: timeToUnixSeconds(`${defaultStartDate}T19:00:00Z`),
}, // 1641063600
{
rfc3339: `${defaultStartDate}T20:00:00Z`,
unix: timeToUnixSeconds(`${defaultStartDate}T20:00:00Z`),
}, // 1641067200
];
// Default time values in getting started sample data
const defaultTimes = [
{
rfc3339: `${defaultStartDate}T08:00:00Z`,
unix: timeToUnixSeconds(`${defaultStartDate}T08:00:00Z`),
}, // 1641024000
{
rfc3339: `${defaultStartDate}T09:00:00Z`,
unix: timeToUnixSeconds(`${defaultStartDate}T09:00:00Z`),
}, // 1641027600
{
rfc3339: `${defaultStartDate}T10:00:00Z`,
unix: timeToUnixSeconds(`${defaultStartDate}T10:00:00Z`),
}, // 1641031200
{
rfc3339: `${defaultStartDate}T11:00:00Z`,
unix: timeToUnixSeconds(`${defaultStartDate}T11:00:00Z`),
}, // 1641034800
{
rfc3339: `${defaultStartDate}T12:00:00Z`,
unix: timeToUnixSeconds(`${defaultStartDate}T12:00:00Z`),
}, // 1641038400
{
rfc3339: `${defaultStartDate}T13:00:00Z`,
unix: timeToUnixSeconds(`${defaultStartDate}T13:00:00Z`),
}, // 1641042000
{
rfc3339: `${defaultStartDate}T14:00:00Z`,
unix: timeToUnixSeconds(`${defaultStartDate}T14:00:00Z`),
}, // 1641045600
{
rfc3339: `${defaultStartDate}T15:00:00Z`,
unix: timeToUnixSeconds(`${defaultStartDate}T15:00:00Z`),
}, // 1641049200
{
rfc3339: `${defaultStartDate}T16:00:00Z`,
unix: timeToUnixSeconds(`${defaultStartDate}T16:00:00Z`),
}, // 1641052800
{
rfc3339: `${defaultStartDate}T17:00:00Z`,
unix: timeToUnixSeconds(`${defaultStartDate}T17:00:00Z`),
}, // 1641056400
{
rfc3339: `${defaultStartDate}T18:00:00Z`,
unix: timeToUnixSeconds(`${defaultStartDate}T18:00:00Z`),
}, // 1641060000
{
rfc3339: `${defaultStartDate}T19:00:00Z`,
unix: timeToUnixSeconds(`${defaultStartDate}T19:00:00Z`),
}, // 1641063600
{
rfc3339: `${defaultStartDate}T20:00:00Z`,
unix: timeToUnixSeconds(`${defaultStartDate}T20:00:00Z`),
}, // 1641067200
];
function updateTimestamps (newStartDate, seedTimes=defaultTimes) {
function updateTimestamps(newStartDate, seedTimes = defaultTimes) {
// Update the times array with replacement times
const times = seedTimes.map(x => {
const times = seedTimes.map((x) => {
var newStartTimestamp = x.rfc3339.replace(/^.*T/, newStartDate + 'T');
return {
@ -178,7 +178,7 @@ function updateTimestamps (newStartDate, seedTimes=defaultTimes) {
/////////////////////// MODAL INTERACTIONS / DATE PICKER ///////////////////////
function CustomTimeTrigger({component}) {
function CustomTimeTrigger({ component }) {
const $component = $(component);
$component
.find('a[data-action="open"]:first')
@ -212,7 +212,7 @@ function CustomTimeTrigger({component}) {
if (newDate != undefined) {
newDate = formatDate(newDate);
// Update the last updated timestamps with the new date
// and reassign the updated times.
updatedTimes = updateTimestamps(newDate, updatedTimes);

View File

@ -1,30 +1,54 @@
const monthNames = ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"];
var date = new Date()
var currentTimestamp = date.toISOString().replace(/^(.*)(\.\d+)(Z)/, '$1$3') // 2023-01-01T12:34:56Z
var currentTime = date.toISOString().replace(/(^.*T)(.*)(Z)/, '$2') + '084216' // 12:34:56.000084216
import $ from 'jquery';
function currentDate(offset=0, trimTime=false) {
outputDate = new Date(date)
outputDate.setDate(outputDate.getDate() + offset)
var date = new Date();
var currentTimestamp = date.toISOString().replace(/^(.*)(\.\d+)(Z)/, '$1$3'); // 2023-01-01T12:34:56Z
// Microsecond offset appended to the current time string for formatting purposes
const MICROSECOND_OFFSET = '084216';
var currentTime =
date.toISOString().replace(/(^.*T)(.*)(Z)/, '$2') + MICROSECOND_OFFSET; // 12:34:56.000084216
function currentDate(offset = 0, trimTime = false) {
let outputDate = new Date(date);
outputDate.setDate(outputDate.getDate() + offset);
if (trimTime) {
return outputDate.toISOString().replace(/T.*$/, '') // 2023-01-01
return outputDate.toISOString().replace(/T.*$/, ''); // 2023-01-01
} else {
return outputDate.toISOString().replace(/T.*$/, 'T00:00:00Z') // 2023-01-01T00:00:00Z
return outputDate.toISOString().replace(/T.*$/, 'T00:00:00Z'); // 2023-01-01T00:00:00Z
}
}
function enterpriseEOLDate() {
var inTwoYears = date.setFullYear(date.getFullYear() + 2)
earliestEOL = new Date(inTwoYears)
return `${monthNames[earliestEOL.getMonth()]} ${earliestEOL.getDate()}, ${earliestEOL.getFullYear()}`
const monthNames = [
'January',
'February',
'March',
'April',
'May',
'June',
'July',
'August',
'September',
'October',
'November',
'December',
];
var inTwoYears = new Date(date);
inTwoYears.setFullYear(inTwoYears.getFullYear() + 2);
let earliestEOL = new Date(inTwoYears);
return `${monthNames[earliestEOL.getMonth()]} ${earliestEOL.getDate()}, ${earliestEOL.getFullYear()}`;
}
$('span.current-timestamp').text(currentTimestamp)
$('span.current-time').text(currentTime)
$('span.enterprise-eol-date').text(enterpriseEOLDate)
$('span.current-date').each(function() {
var dayOffset = parseInt($(this).attr("offset"))
var trimTime = $(this).attr("trim-time") === "true"
$(this).text(currentDate(dayOffset, trimTime))
})
function initialize() {
$('span.current-timestamp').text(currentTimestamp);
$('span.current-time').text(currentTime);
$('span.enterprise-eol-date').text(enterpriseEOLDate());
$('span.current-date').each(function () {
var dayOffset = parseInt($(this).attr('offset'));
var trimTime = $(this).attr('trim-time') === 'true';
$(this).text(currentDate(dayOffset, trimTime));
});
}
export { initialize };

View File

@ -2,37 +2,24 @@
This feature is designed to callout new features added to the documentation
CSS is required for the callout bubble to determine look and position, but the
element must have the `callout` class and a unique id.
Callouts are treated as notifications and use the notification cookie API in
assets/js/cookies.js.
Callouts are treated as notifications and use the LocalStorage notification API.
*/
import $ from 'jquery';
import * as LocalStorageAPI from './services/local-storage.js';
// Get notification ID
function getCalloutID (el) {
function getCalloutID(el) {
return $(el).attr('id');
}
// Hide a callout and update the cookie with the viewed callout
function hideCallout (calloutID) {
if (!window.LocalStorageAPI.notificationIsRead(calloutID)) {
window.LocalStorageAPI.setNotificationAsRead(calloutID, 'callout');
$(`#${calloutID}`).fadeOut(200);
// Show the url feature callouts on page load
export default function FeatureCallout({ component }) {
const calloutID = getCalloutID($(component));
if (!LocalStorageAPI.notificationIsRead(calloutID, 'callout')) {
$(`#${calloutID}.feature-callout`)
.fadeIn(300)
.removeClass('start-position');
}
}
// Show the url feature callouts on page load
$(document).ready(function () {
$('.feature-callout').each(function () {
const calloutID = getCalloutID($(this));
if (!window.LocalStorageAPI.notificationIsRead(calloutID, 'callout')) {
$(`#${calloutID}.feature-callout`)
.fadeIn(300)
.removeClass('start-position');
}
});
});
// Hide the InfluxDB URL selector callout
// $('button.url-trigger, #influxdb-url-selector .close').click(function () {
// hideCallout('influxdb-url-selector');
// });

View File

@ -1,49 +1,148 @@
var tablesElement = $("#flux-group-keys-demo #grouped-tables")
import $ from 'jquery';
// Sample data
let data = [
[
{ _time: "2021-01-01T00:00:00Z", _measurement: "example", loc: "rm1", sensorID: "A123", _field: "temp", _value: 110.3 },
{ _time: "2021-01-01T00:01:00Z", _measurement: "example", loc: "rm1", sensorID: "A123", _field: "temp", _value: 112.5 },
{ _time: "2021-01-01T00:02:00Z", _measurement: "example", loc: "rm1", sensorID: "A123", _field: "temp", _value: 111.9 }
{
_time: '2021-01-01T00:00:00Z',
_measurement: 'example',
loc: 'rm1',
sensorID: 'A123',
_field: 'temp',
_value: 110.3,
},
{
_time: '2021-01-01T00:01:00Z',
_measurement: 'example',
loc: 'rm1',
sensorID: 'A123',
_field: 'temp',
_value: 112.5,
},
{
_time: '2021-01-01T00:02:00Z',
_measurement: 'example',
loc: 'rm1',
sensorID: 'A123',
_field: 'temp',
_value: 111.9,
},
],
[
{ _time: "2021-01-01T00:00:00Z", _measurement: "example", loc: "rm1", sensorID: "A123", _field: "hum", _value: 73.4 },
{ _time: "2021-01-01T00:01:00Z", _measurement: "example", loc: "rm1", sensorID: "A123", _field: "hum", _value: 73.7 },
{ _time: "2021-01-01T00:02:00Z", _measurement: "example", loc: "rm1", sensorID: "A123", _field: "hum", _value: 75.1 }
{
_time: '2021-01-01T00:00:00Z',
_measurement: 'example',
loc: 'rm1',
sensorID: 'A123',
_field: 'hum',
_value: 73.4,
},
{
_time: '2021-01-01T00:01:00Z',
_measurement: 'example',
loc: 'rm1',
sensorID: 'A123',
_field: 'hum',
_value: 73.7,
},
{
_time: '2021-01-01T00:02:00Z',
_measurement: 'example',
loc: 'rm1',
sensorID: 'A123',
_field: 'hum',
_value: 75.1,
},
],
[
{ _time: "2021-01-01T00:00:00Z", _measurement: "example", loc: "rm2", sensorID: "B456", _field: "temp", _value: 108.2 },
{ _time: "2021-01-01T00:01:00Z", _measurement: "example", loc: "rm2", sensorID: "B456", _field: "temp", _value: 108.5 },
{ _time: "2021-01-01T00:02:00Z", _measurement: "example", loc: "rm2", sensorID: "B456", _field: "temp", _value: 109.6 }
{
_time: '2021-01-01T00:00:00Z',
_measurement: 'example',
loc: 'rm2',
sensorID: 'B456',
_field: 'temp',
_value: 108.2,
},
{
_time: '2021-01-01T00:01:00Z',
_measurement: 'example',
loc: 'rm2',
sensorID: 'B456',
_field: 'temp',
_value: 108.5,
},
{
_time: '2021-01-01T00:02:00Z',
_measurement: 'example',
loc: 'rm2',
sensorID: 'B456',
_field: 'temp',
_value: 109.6,
},
],
[
{ _time: "2021-01-01T00:00:00Z", _measurement: "example", loc: "rm2", sensorID: "B456", _field: "hum", _value: 71.8 },
{ _time: "2021-01-01T00:01:00Z", _measurement: "example", loc: "rm2", sensorID: "B456", _field: "hum", _value: 72.3 },
{ _time: "2021-01-01T00:02:00Z", _measurement: "example", loc: "rm2", sensorID: "B456", _field: "hum", _value: 72.1 }
]
]
{
_time: '2021-01-01T00:00:00Z',
_measurement: 'example',
loc: 'rm2',
sensorID: 'B456',
_field: 'hum',
_value: 71.8,
},
{
_time: '2021-01-01T00:01:00Z',
_measurement: 'example',
loc: 'rm2',
sensorID: 'B456',
_field: 'hum',
_value: 72.3,
},
{
_time: '2021-01-01T00:02:00Z',
_measurement: 'example',
loc: 'rm2',
sensorID: 'B456',
_field: 'hum',
_value: 72.1,
},
],
];
// Default group key
let groupKey = ["_measurement", "loc", "sensorID", "_field"]
let groupKey = ['_measurement', 'loc', 'sensorID', '_field'];
export default function FluxGroupKeysDemo({ component }) {
$('.column-list label').click(function () {
toggleCheckbox($(this));
groupKey = getChecked(component);
groupData();
buildGroupExample(component);
});
// Group and render tables on load
groupData();
}
// Build a table group (group key and table) using an array of objects
function buildTable(inputData) {
// Build the group key string
function wrapString(column, value) {
var stringColumns = ["_measurement", "loc", "sensorID", "_field"]
var stringColumns = ['_measurement', 'loc', 'sensorID', '_field'];
if (stringColumns.includes(column)) {
return '"' + value + '"'
return '"' + value + '"';
} else {
return value
return value;
}
}
var groupKeyString = "Group key instance = [" + (groupKey.map(column => column + ": " + wrapString(column, (inputData[0])[column])) ).join(", ") + "]";
var groupKeyLabel = document.createElement("p");
groupKeyLabel.className = "table-group-key"
groupKeyLabel.innerHTML = groupKeyString
var groupKeyString =
'Group key instance = [' +
groupKey
.map((column) => column + ': ' + wrapString(column, inputData[0][column]))
.join(', ') +
']';
var groupKeyLabel = document.createElement('p');
groupKeyLabel.className = 'table-group-key';
groupKeyLabel.innerHTML = groupKeyString;
// Extract column headers
var columns = [];
@ -54,56 +153,57 @@ function buildTable(inputData) {
}
}
}
// Create the table element
var table = document.createElement("table");
const table = document.createElement('table');
// Create the table header
for (let i = 0; i < columns.length; i++) {
var header = table.createTHead();
var th = document.createElement("th");
var th = document.createElement('th');
th.innerHTML = columns[i];
if (groupKey.includes(columns[i])) {
th.className = "grouped-by";
th.className = 'grouped-by';
}
header.appendChild(th);
}
// Add inputData to the HTML table
for (let i = 0; i < inputData.length; i++) {
tr = table.insertRow(-1);
let tr = table.insertRow(-1);
for (let j = 0; j < columns.length; j++) {
var td = tr.insertCell(-1);
td.innerHTML = inputData[i][columns[j]];
// Highlight the value if column is part of the group key
if (groupKey.includes(columns[j])) {
td.className = "grouped-by";
td.className = 'grouped-by';
}
}
}
// Create a table group with group key and table
var tableGroup = document.createElement("div");
tableGroup.innerHTML += groupKeyLabel.outerHTML + table.outerHTML
var tableGroup = document.createElement('div');
tableGroup.innerHTML += groupKeyLabel.outerHTML + table.outerHTML;
return tableGroup
return tableGroup;
}
// Clear and rebuild all HTML tables
function buildTables(data) {
existingTables = tablesElement[0]
let tablesElement = $('#flux-group-keys-demo #grouped-tables');
let existingTables = tablesElement[0];
while (existingTables.firstChild) {
existingTables.removeChild(existingTables.firstChild);
}
for (let i = 0; i < data.length; i++) {
var table = buildTable(data[i])
var table = buildTable(data[i]);
tablesElement.append(table);
}
}
// Group data based on the group key and output new tables
function groupData() {
let groupedData = data.flat()
let groupedData = data.flat();
function groupBy(array, f) {
var groups = {};
@ -114,20 +214,19 @@ function groupData() {
});
return Object.keys(groups).map(function (group) {
return groups[group];
})
});
}
groupedData = groupBy(groupedData, function (r) {
return groupKey.map(v => r[v]);
return groupKey.map((v) => r[v]);
});
buildTables(groupedData);
}
// Get selected column names
var checkboxes = $("input[type=checkbox]");
function getChecked() {
function getChecked(component) {
// Get selected column names
var checkboxes = $(component).find('input[type=checkbox]');
var checked = [];
for (var i = 0; i < checkboxes.length; i++) {
var checkbox = checkboxes[i];
@ -141,17 +240,12 @@ function toggleCheckbox(element) {
}
// Build example group function
function buildGroupExample() {
var columnCollection = getChecked().map(i => '<span class=\"s2\">"' + i + '"</span>').join(", ")
$("pre#group-by-example")[0].innerHTML = "data\n <span class='nx'>|></span> group(columns<span class='nx'>:</span> [" + columnCollection + "])";
function buildGroupExample(component) {
var columnCollection = getChecked(component)
.map((i) => '<span class=\"s2\">"' + i + '"</span>')
.join(', ');
$('pre#group-by-example')[0].innerHTML =
"data\n <span class='nx'>|></span> group(columns<span class='nx'>:</span> [" +
columnCollection +
'])';
}
$(".column-list label").click(function () {
toggleCheckbox($(this))
groupKey = getChecked();
groupData();
buildGroupExample();
});
// Group and render tables on load
groupData()

View File

@ -1,22 +0,0 @@
$('.exp-btn').click(function() {
var targetBtnElement = $(this).parent()
$('.exp-btn > p', targetBtnElement).fadeOut(100);
setTimeout(function() {
$('.exp-btn-links', targetBtnElement).fadeIn(200)
$('.exp-btn', targetBtnElement).addClass('open');
$('.close-btn', targetBtnElement).fadeIn(200);
}, 100);
})
$('.close-btn').click(function() {
var targetBtnElement = $(this).parent().parent()
$('.exp-btn-links', targetBtnElement).fadeOut(100)
$('.exp-btn', targetBtnElement).removeClass('open');
$(this).fadeOut(100);
setTimeout(function() {
$('p', targetBtnElement).fadeIn(100);
}, 100);
})
/////////////////////////////// EXPANDING BUTTONS //////////////////////////////

View File

@ -1 +0,0 @@
export * from './main.js';

View File

@ -3,7 +3,6 @@
///////////////////////// INFLUXDB URL PREFERENCE /////////////////////////////
////////////////////////////////////////////////////////////////////////////////
*/
import * as pageParams from '@params';
import {
DEFAULT_STORAGE_URLS,
getPreference,
@ -12,15 +11,18 @@ import {
removeInfluxDBUrl,
getInfluxDBUrl,
getInfluxDBUrls,
} from './local-storage.js';
} from './services/local-storage.js';
import $ from 'jquery';
import { context as PRODUCT_CONTEXT, referrerHost } from './page-context.js';
import { influxdbUrls } from './services/influxdb-urls.js';
import { delay } from './helpers.js';
import { toggleModal } from './modals.js';
let CLOUD_URLS = [];
if (pageParams && pageParams.influxdb_urls) {
CLOUD_URLS = Object.values(pageParams.influxdb_urls.cloud.providers).flatMap((provider) => provider.regions?.map((region) => region.url));
if (influxdbUrls?.cloud) {
CLOUD_URLS = Object.values(influxdbUrls.cloud.providers).flatMap((provider) =>
provider.regions?.map((region) => region.url)
);
}
export { CLOUD_URLS };
@ -28,7 +30,7 @@ export function InfluxDBUrl() {
const UNIQUE_URL_PRODUCTS = ['dedicated', 'clustered'];
const IS_UNIQUE_URL_PRODUCT = UNIQUE_URL_PRODUCTS.includes(PRODUCT_CONTEXT);
// Add actual cloud URLs as needed
// Add actual cloud URLs as needed
const elementSelector = '.article--content pre:not(.preserve)';
///////////////////// Stored preference management ///////////////////////
@ -118,11 +120,12 @@ export function InfluxDBUrl() {
});
}
// Retrieve the currently selected URLs from the urls local storage object.
function getUrls() {
const { cloud, oss, core, enterprise, serverless, dedicated, clustered } = getInfluxDBUrls();
return { oss, cloud, core, enterprise, serverless, dedicated, clustered };
}
// Retrieve the currently selected URLs from the urls local storage object.
function getUrls() {
const { cloud, oss, core, enterprise, serverless, dedicated, clustered } =
getInfluxDBUrls();
return { oss, cloud, core, enterprise, serverless, dedicated, clustered };
}
// Retrieve the previously selected URLs from the from the urls local storage object.
// This is used to update URLs whenever you switch between browser tabs.
@ -289,15 +292,17 @@ export function InfluxDBUrl() {
}
// Append the URL selector button to each codeblock containing a placeholder URL
function appendUrlSelector(urls={
cloud: '',
oss: '',
core: '',
enterprise: '',
serverless: '',
dedicated: '',
clustered: '',
}) {
function appendUrlSelector(
urls = {
cloud: '',
oss: '',
core: '',
enterprise: '',
serverless: '',
dedicated: '',
clustered: '',
}
) {
const appendToUrls = Object.values(urls);
const getBtnText = (context) => {
@ -315,7 +320,7 @@ export function InfluxDBUrl() {
return contextText[context];
};
appendToUrls.forEach(function (url) {
appendToUrls.forEach(function (url) {
$(elementSelector).each(function () {
var code = $(this).html();
if (code.includes(url)) {
@ -330,20 +335,32 @@ export function InfluxDBUrl() {
});
}
////////////////////////////////////////////////////////////////////////////
////////////////// Initialize InfluxDB URL interactions ////////////////////
////////////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////////
////////////////// Initialize InfluxDB URL interactions ////////////////////
////////////////////////////////////////////////////////////////////////////
// Add the preserve tag to code blocks that shouldn't be updated
addPreserve();
const { cloud, oss, core, enterprise, serverless, dedicated, clustered } = DEFAULT_STORAGE_URLS;
const { cloud, oss, core, enterprise, serverless, dedicated, clustered } =
DEFAULT_STORAGE_URLS;
// Append URL selector buttons to code blocks
appendUrlSelector({ cloud, oss, core, enterprise, serverless, dedicated, clustered });
appendUrlSelector({
cloud,
oss,
core,
enterprise,
serverless,
dedicated,
clustered,
});
// Update URLs on load
updateUrls({ cloud, oss, core, enterprise, serverless, dedicated, clustered }, getUrls());
updateUrls(
{ cloud, oss, core, enterprise, serverless, dedicated, clustered },
getUrls()
);
// Set active radio button on page load
setRadioButtons(getUrls());

View File

@ -1,41 +1,58 @@
// Dynamically update keybindings or hotkeys
function getPlatform() {
if (/Mac/.test(navigator.platform)) {
return "osx"
} else if (/Win/.test(navigator.platform)) {
return "win"
} else if (/Linux/.test(navigator.platform)) {
return "linux"
} else {
return "other"
}
import { getPlatform } from './utils/user-agent-platform.js';
import $ from 'jquery';
/**
* Adds OS-specific class to component
* @param {string} osClass - OS-specific class to add
* @param {Object} options - Component options
* @param {jQuery} options.$component - jQuery element reference
*/
function addOSClass(osClass, { $component }) {
$component.addClass(osClass);
}
const platform = getPlatform()
/**
* Updates keybinding display based on detected platform
* @param {Object} options - Component options
* @param {jQuery} options.$component - jQuery element reference
* @param {string} options.platform - Detected platform
*/
function updateKeyBindings({ $component, platform }) {
const osx = $component.data('osx');
const linux = $component.data('linux');
const win = $component.data('win');
function addOSClass(osClass) {
$('.keybinding').addClass(osClass)
}
let keybind;
function updateKeyBindings() {
$('.keybinding').each(function() {
var osx = $(this).data("osx")
var linux = $(this).data("linux")
var win = $(this).data("win")
if (platform === "other") {
if (win != linux) {
var keybind = '<code class="osx">' + osx + '</code> for macOS, <code>' + linux + '</code> for Linux, and <code>' + win + '</code> for Windows';
} else {
var keybind = '<code>' + linux + '</code> for Linux and Windows and <code class="osx">' + osx + '</code> for macOS';
}
if (platform === 'other') {
if (win !== linux) {
keybind =
`<code class="osx">${osx}</code> for macOS, ` +
`<code>${linux}</code> for Linux, ` +
`and <code>${win}</code> for Windows`;
} else {
var keybind = '<code>' + $(this).data(platform) + '</code>'
keybind =
`<code>${linux}</code> for Linux and Windows and ` +
`<code class="osx">${osx}</code> for macOS`;
}
} else {
keybind = `<code>${$component.data(platform)}</code>`;
}
$(this).html(keybind)
})
$component.html(keybind);
}
addOSClass(platform)
updateKeyBindings()
/**
* Initialize and render platform-specific keybindings
* @param {Object} options - Component options
* @param {HTMLElement} options.component - DOM element
* @returns {void}
*/
export default function KeyBinding({ component }) {
// Initialize keybindings
const platform = getPlatform();
const $component = $(component);
addOSClass(platform, { $component });
updateKeyBindings({ $component, platform });
}

View File

@ -1,11 +1,15 @@
import $ from 'jquery';
// Count tag elements
function countTag(tag) {
return $(".visible[data-tags*='" + tag + "']").length
return $(".visible[data-tags*='" + tag + "']").length;
}
function getFilterCounts() {
$('#list-filters label').each(function() {
var tagName = $('input', this).attr('name').replace(/[\W/]+/, "-");
function getFilterCounts($labels) {
$labels.each(function () {
var tagName = $('input', this)
.attr('name')
.replace(/[\W/]+/, '-');
var tagCount = countTag(tagName);
$(this).attr('data-count', '(' + tagCount + ')');
if (tagCount <= 0) {
@ -13,38 +17,58 @@ function getFilterCounts() {
} else {
$(this).fadeTo(400, 1.0);
}
})
});
}
// Get initial filter count on page load
getFilterCounts()
/** TODO: Include the data source value in the as an additional attribute
* in the HTML and pass it into the component, which would let us use selectors
* for only the source items and let us have more than one
* list filter component per page without conflicts */
export default function ListFilters({ component }) {
const $component = $(component);
const $labels = $component.find('label');
const $inputs = $component.find('input');
$("#list-filters input").click(function() {
getFilterCounts($labels);
// List of tags to hide
var tagArray = $("#list-filters input:checkbox:checked").map(function(){
return $(this).attr('name').replace(/[\W]+/, "-");
}).get();
$inputs.click(function () {
// List of tags to hide
var tagArray = $component
.find('input:checkbox:checked')
.map(function () {
return $(this).attr('name').replace(/[\W]+/, '-');
})
.get();
// List of tags to restore
var restoreArray = $("#list-filters input:checkbox:not(:checked)").map(function(){
return $(this).attr('name').replace(/[\W]+/, "-");
}).get();
// List of tags to restore
var restoreArray = $component
.find('input:checkbox:not(:checked)')
.map(function () {
return $(this).attr('name').replace(/[\W]+/, '-');
})
.get();
// Actions for filter select
if ( $(this).is(':checked') ) {
$.each( tagArray, function( index, value ) {
$(".filter-item.visible:not([data-tags~='" + value + "'])").removeClass('visible').fadeOut()
})
} else {
$.each( restoreArray, function( index, value ) {
$(".filter-item:not(.visible)[data-tags~='" + value + "']").addClass('visible').fadeIn()
})
$.each( tagArray, function( index, value ) {
$(".filter-item.visible:not([data-tags~='" + value + "'])").removeClass('visible').hide()
})
}
// Actions for filter select
if ($(this).is(':checked')) {
$.each(tagArray, function (index, value) {
$(".filter-item.visible:not([data-tags~='" + value + "'])")
.removeClass('visible')
.fadeOut();
});
} else {
$.each(restoreArray, function (index, value) {
$(".filter-item:not(.visible)[data-tags~='" + value + "']")
.addClass('visible')
.fadeIn();
});
$.each(tagArray, function (index, value) {
$(".filter-item.visible:not([data-tags~='" + value + "'])")
.removeClass('visible')
.hide();
});
}
// Refresh filter count
getFilterCounts()
});
// Refresh filter count
getFilterCounts($labels);
});
}

View File

@ -1,7 +1,7 @@
// assets/js/main.js
// If you need to pass parameters from the calling Hugo page, you can import them here like so:
// import * as pageParams from '@params';
// Import dependencies that we still need to load in the global scope
import $ from 'jquery';
/** Import modules that are not components.
* TODO: Refactor these into single-purpose component modules.
@ -9,9 +9,10 @@
import * as apiLibs from './api-libs.js';
import * as codeControls from './code-controls.js';
import * as contentInteractions from './content-interactions.js';
import * as datetime from './datetime.js';
import { delay } from './helpers.js';
import { InfluxDBUrl } from './influxdb-url.js';
import * as localStorage from './local-storage.js';
import * as localStorage from './services/local-storage.js';
import * as modals from './modals.js';
import * as notifications from './notifications.js';
import * as pageContext from './page-context.js';
@ -29,8 +30,17 @@ import * as v3Wayfinding from './v3-wayfinding.js';
import AskAITrigger from './ask-ai-trigger.js';
import CodePlaceholder from './code-placeholders.js';
import { CustomTimeTrigger } from './custom-timestamps.js';
import Diagram from './components/diagram.js';
import DocSearch from './components/doc-search.js';
import FeatureCallout from './feature-callouts.js';
import FluxGroupKeysDemo from './flux-group-keys.js';
import FluxInfluxDBVersionsTrigger from './flux-influxdb-versions.js';
import KeyBinding from './keybindings.js';
import ListFilters from './list-filters.js';
import ProductSelector from './version-selector.js';
import ReleaseToc from './release-toc.js';
import { SearchButton } from './search-button.js';
import SidebarSearch from './components/sidebar-search.js';
import { SidebarToggle } from './sidebar-toggle.js';
import Theme from './theme.js';
import ThemeSwitch from './theme-switch.js';
@ -49,11 +59,20 @@ const componentRegistry = {
'ask-ai-trigger': AskAITrigger,
'code-placeholder': CodePlaceholder,
'custom-time-trigger': CustomTimeTrigger,
diagram: Diagram,
'doc-search': DocSearch,
'feature-callout': FeatureCallout,
'flux-group-keys-demo': FluxGroupKeysDemo,
'flux-influxdb-versions-trigger': FluxInfluxDBVersionsTrigger,
keybinding: KeyBinding,
'list-filters': ListFilters,
'product-selector': ProductSelector,
'release-toc': ReleaseToc,
'search-button': SearchButton,
'sidebar-search': SidebarSearch,
'sidebar-toggle': SidebarToggle,
'theme': Theme,
'theme-switch': ThemeSwitch
theme: Theme,
'theme-switch': ThemeSwitch,
};
/**
@ -71,7 +90,12 @@ function initGlobals() {
window.influxdatadocs.pageContext = pageContext;
window.influxdatadocs.toggleModal = modals.toggleModal;
window.influxdatadocs.componentRegistry = componentRegistry;
// Re-export jQuery to global namespace for legacy scripts
if (typeof window.jQuery === 'undefined') {
window.jQuery = window.$ = $;
}
return window.influxdatadocs;
}
@ -81,32 +105,35 @@ function initGlobals() {
*/
function initComponents(globals) {
const components = document.querySelectorAll('[data-component]');
components.forEach((component) => {
const componentName = component.getAttribute('data-component');
const ComponentConstructor = componentRegistry[componentName];
if (ComponentConstructor) {
// Initialize the component and store its instance in the global namespace
try {
const instance = ComponentConstructor({ component });
globals[componentName] = ComponentConstructor;
// Optionally store component instances for future reference
if (!globals.instances) {
globals.instances = {};
}
if (!globals.instances[componentName]) {
globals.instances[componentName] = [];
}
globals.instances[componentName].push({
element: component,
instance
instance,
});
} catch (error) {
console.error(`Error initializing component "${componentName}":`, error);
console.error(
`Error initializing component "${componentName}":`,
error
);
}
} else {
console.warn(`Unknown component: "${componentName}"`);
@ -122,6 +149,7 @@ function initModules() {
apiLibs.initialize();
codeControls.initialize();
contentInteractions.initialize();
datetime.initialize();
InfluxDBUrl();
notifications.initialize();
pageFeedback.initialize();
@ -135,10 +163,10 @@ function initModules() {
function init() {
// Initialize global namespace and expose core modules
const globals = initGlobals();
// Initialize non-component UI modules
initModules();
// Initialize components from registry
initComponents(globals);
}
@ -147,4 +175,4 @@ function init() {
document.addEventListener('DOMContentLoaded', init);
// Export public API
export { initGlobals, componentRegistry };
export { initGlobals, componentRegistry };

View File

@ -1,34 +1,80 @@
/** This module retrieves browser context information and site data for the
* current page, version, and product.
*/
import { products, influxdb_urls } from '@params';
const safeProducts = products || {};
const safeUrls = influxdb_urls || {};
import { products } from './services/influxdata-products.js';
import { influxdbUrls } from './services/influxdb-urls.js';
function getCurrentProductData() {
const path = window.location.pathname;
const mappings = [
{ pattern: /\/influxdb\/cloud\//, product: safeProducts.cloud, urls: safeUrls.influxdb_cloud },
{ pattern: /\/influxdb3\/core/, product: safeProducts.influxdb3_core, urls: safeUrls.core },
{ pattern: /\/influxdb3\/enterprise/, product: safeProducts.influxdb3_enterprise, urls: safeUrls.enterprise },
{ pattern: /\/influxdb3\/cloud-serverless/, product: safeProducts.influxdb3_cloud_serverless, urls: safeUrls.cloud },
{ pattern: /\/influxdb3\/cloud-dedicated/, product: safeProducts.influxdb3_cloud_dedicated, urls: safeUrls.dedicated },
{ pattern: /\/influxdb3\/clustered/, product: safeProducts.influxdb3_clustered, urls: safeUrls.clustered },
{ pattern: /\/enterprise_v1\//, product: safeProducts.enterprise_influxdb, urls: safeUrls.oss },
{ pattern: /\/influxdb.*v1\//, product: safeProducts.influxdb, urls: safeUrls.oss },
{ pattern: /\/influxdb.*v2\//, product: safeProducts.influxdb, urls: safeUrls.oss },
{ pattern: /\/kapacitor\//, product: safeProducts.kapacitor, urls: safeUrls.oss },
{ pattern: /\/telegraf\//, product: safeProducts.telegraf, urls: safeUrls.oss },
{ pattern: /\/chronograf\//, product: safeProducts.chronograf, urls: safeUrls.oss },
{ pattern: /\/flux\//, product: safeProducts.flux, urls: safeUrls.oss },
{
pattern: /\/influxdb\/cloud\//,
product: products.cloud,
urls: influxdbUrls.influxdb_cloud,
},
{
pattern: /\/influxdb3\/core/,
product: products.influxdb3_core,
urls: influxdbUrls.core,
},
{
pattern: /\/influxdb3\/enterprise/,
product: products.influxdb3_enterprise,
urls: influxdbUrls.enterprise,
},
{
pattern: /\/influxdb3\/cloud-serverless/,
product: products.influxdb3_cloud_serverless,
urls: influxdbUrls.cloud,
},
{
pattern: /\/influxdb3\/cloud-dedicated/,
product: products.influxdb3_cloud_dedicated,
urls: influxdbUrls.dedicated,
},
{
pattern: /\/influxdb3\/clustered/,
product: products.influxdb3_clustered,
urls: influxdbUrls.clustered,
},
{
pattern: /\/enterprise_v1\//,
product: products.enterprise_influxdb,
urls: influxdbUrls.oss,
},
{
pattern: /\/influxdb.*v1\//,
product: products.influxdb,
urls: influxdbUrls.oss,
},
{
pattern: /\/influxdb.*v2\//,
product: products.influxdb,
urls: influxdbUrls.oss,
},
{
pattern: /\/kapacitor\//,
product: products.kapacitor,
urls: influxdbUrls.oss,
},
{
pattern: /\/telegraf\//,
product: products.telegraf,
urls: influxdbUrls.oss,
},
{
pattern: /\/chronograf\//,
product: products.chronograf,
urls: influxdbUrls.oss,
},
{ pattern: /\/flux\//, product: products.flux, urls: influxdbUrls.oss },
];
for (const { pattern, product, urls } of mappings) {
if (pattern.test(path)) {
return {
product: product || 'unknown',
urls: urls || {}
return {
product: product || 'unknown',
urls: urls || {},
};
}
}
@ -36,7 +82,8 @@ function getCurrentProductData() {
return { product: 'other', urls: {} };
}
// Return the page context (cloud, serverless, oss/enterprise, dedicated, clustered, other)
// Return the page context
// (cloud, serverless, oss/enterprise, dedicated, clustered, other)
function getContext() {
if (/\/influxdb\/cloud\//.test(window.location.pathname)) {
return 'cloud';
@ -78,8 +125,12 @@ const context = getContext(),
protocol = location.protocol,
referrer = document.referrer === '' ? 'direct' : document.referrer,
referrerHost = getReferrerHost(),
// TODO: Verify this still does what we want since the addition of InfluxDB 3 naming and the Core and Enterprise versions.
version = (/^v\d/.test(pathArr[1]) || pathArr[1]?.includes('cloud') ? pathArr[1].replace(/^v/, '') : "n/a")
// TODO: Verify this works since the addition of InfluxDB 3 naming
// and the Core and Enterprise versions.
version =
/^v\d/.test(pathArr[1]) || pathArr[1]?.includes('cloud')
? pathArr[1].replace(/^v/, '')
: 'n/a';
export {
context,
@ -92,4 +143,4 @@ export {
referrer,
referrerHost,
version,
};
};

View File

@ -1,26 +1,67 @@
/////////////////////////// Table of Contents Script ///////////////////////////
/*
* This script is used to generate a table of contents for the
* release notes pages.
*/
* This script is used to generate a table of contents for the
* release notes pages.
*/
export default function ReleaseToc({ component }) {
// Get all h2 elements that are not checkpoint-releases
const releases = Array.from(document.querySelectorAll('h2')).filter(
(el) => !el.id.match(/checkpoint-releases/)
);
// Get all h2 elements that are not checkpoint-releases
const releases = Array.from(document.querySelectorAll('h2')).filter(
el => !el.id.match(/checkpoint-releases/)
);
// Extract data about each release from the array of releases
const releaseData = releases.map((el) => ({
name: el.textContent,
id: el.id,
class: el.getAttribute('class'),
date: el.getAttribute('date'),
}));
// Extract data about each release from the array of releases
const releaseData = releases.map(el => ({
name: el.textContent,
id: el.id,
class: el.getAttribute('class'),
date: el.getAttribute('date')
}));
// Build the release table of contents
const releaseTocUl = component.querySelector('#release-toc ul');
releaseData.forEach((release) => {
releaseTocUl.appendChild(getReleaseItem(release));
});
/*
* This script is used to expand the release notes table of contents by the
* number specified in the `show` attribute of `ul.release-list`.
* Once all the release items are visible, the "Show More" button is hidden.
*/
const showMoreBtn = component.querySelector('.show-more');
if (showMoreBtn) {
showMoreBtn.addEventListener('click', function () {
const itemHeight = 1.885; // Item height in rem
const releaseNum = releaseData.length;
const maxHeight = releaseNum * itemHeight;
const releaseList = document.getElementById('release-list');
const releaseIncrement = Number(releaseList.getAttribute('show'));
const currentHeightMatch = releaseList.style.height.match(/\d+\.?\d+/);
const currentHeight = currentHeightMatch
? Number(currentHeightMatch[0])
: 0;
const potentialHeight = currentHeight + releaseIncrement * itemHeight;
const newHeight =
potentialHeight > maxHeight ? maxHeight : potentialHeight;
releaseList.style.height = `${newHeight}rem`;
if (newHeight >= maxHeight) {
// Simple fade out
showMoreBtn.style.transition = 'opacity 0.1s';
showMoreBtn.style.opacity = 0;
setTimeout(() => {
showMoreBtn.style.display = 'none';
}, 100);
}
});
}
}
// Use release data to generate a list item for each release
function getReleaseItem(releaseData) {
const li = document.createElement("li");
const li = document.createElement('li');
if (releaseData.class !== null) {
li.className = releaseData.class;
}
@ -28,42 +69,3 @@ function getReleaseItem(releaseData) {
li.setAttribute('date', releaseData.date);
return li;
}
// Build the release table of contents
const releaseTocUl = document.querySelector('#release-toc ul');
releaseData.forEach(release => {
releaseTocUl.appendChild(getReleaseItem(release));
});
/*
* This script is used to expand the release notes table of contents by the
* number specified in the `show` attribute of `ul.release-list`.
* Once all the release items are visible, the "Show More" button is hidden.
*/
const showMoreBtn = document.querySelector('#release-toc .show-more');
if (showMoreBtn) {
showMoreBtn.addEventListener('click', function () {
const itemHeight = 1.885; // Item height in rem
const releaseNum = releaseData.length;
const maxHeight = releaseNum * itemHeight;
const releaseList = document.getElementById('release-list');
const releaseIncrement = Number(releaseList.getAttribute('show'));
const currentHeightMatch = releaseList.style.height.match(/\d+\.?\d+/);
const currentHeight = currentHeightMatch
? Number(currentHeightMatch[0])
: 0;
const potentialHeight = currentHeight + releaseIncrement * itemHeight;
const newHeight = potentialHeight > maxHeight ? maxHeight : potentialHeight;
releaseList.style.height = `${newHeight}rem`;
if (newHeight >= maxHeight) {
// Simple fade out
showMoreBtn.style.transition = 'opacity 0.1s';
showMoreBtn.style.opacity = 0;
setTimeout(() => {
showMoreBtn.style.display = 'none';
}, 100);
}
});
}

View File

@ -1,10 +0,0 @@
// Fade content wrapper when focusing on search input
$('#algolia-search-input').focus(function() {
$('.content-wrapper').fadeTo(300, .35);
})
// Hide search dropdown when leaving search input
$('#algolia-search-input').blur(function() {
$('.content-wrapper').fadeTo(200, 1);
$('.ds-dropdown-menu').hide();
})

View File

@ -0,0 +1,3 @@
import { products as productsParam } from '@params';
export const products = productsParam || {};

View File

@ -0,0 +1,3 @@
import { influxdb_urls as influxdbUrlsParam } from '@params';
export const influxdbUrls = influxdbUrlsParam || {};

View File

@ -10,7 +10,8 @@
- messages: Messages (data/notifications.yaml) that have been seen (array)
- callouts: Feature callouts that have been seen (array)
*/
import * as pageParams from '@params';
import { influxdbUrls } from './influxdb-urls.js';
// Prefix for all InfluxData docs local storage
const storagePrefix = 'influxdata_docs_';
@ -82,14 +83,12 @@ function getPreferences() {
//////////// MANAGE INFLUXDATA DOCS URLS IN LOCAL STORAGE //////////////////////
////////////////////////////////////////////////////////////////////////////////
const defaultUrls = {};
// Guard against pageParams being null/undefined and safely access nested properties
if (pageParams && pageParams.influxdb_urls) {
Object.entries(pageParams.influxdb_urls).forEach(([product, {providers}]) => {
defaultUrls[product] = providers.filter(provider => provider.name === 'Default')[0]?.regions[0]?.url;
});
}
Object.entries(influxdbUrls).forEach(([product, { providers }]) => {
defaultUrls[product] =
providers.filter((provider) => provider.name === 'Default')[0]?.regions[0]
?.url || 'https://cloud2.influxdata.com';
});
export const DEFAULT_STORAGE_URLS = {
oss: defaultUrls.oss,
@ -177,7 +176,10 @@ const defaultNotificationsObj = {
function getNotifications() {
// Initialize notifications data if it doesn't already exist
if (localStorage.getItem(notificationStorageKey) === null) {
initializeStorageItem('notifications', JSON.stringify(defaultNotificationsObj));
initializeStorageItem(
'notifications',
JSON.stringify(defaultNotificationsObj)
);
}
// Retrieve and parse the notifications data as JSON
@ -221,7 +223,10 @@ function setNotificationAsRead(notificationID, notificationType) {
readNotifications.push(notificationID);
notificationsObj[notificationType + 's'] = readNotifications;
localStorage.setItem(notificationStorageKey, JSON.stringify(notificationsObj));
localStorage.setItem(
notificationStorageKey,
JSON.stringify(notificationsObj)
);
}
// Export functions as a module and make the file backwards compatible for non-module environments until all remaining dependent scripts are ported to modules

View File

@ -3,7 +3,7 @@
http://www.thesitewizard.com/javascripts/change-style-sheets.shtml
*/
import * as localStorage from './local-storage.js';
import * as localStorage from './services/local-storage.js';
// *** TO BE CUSTOMISED ***
var sidebar_state_preference_name = 'sidebar_state';

View File

@ -1,20 +1,21 @@
import Theme from './theme.js';
export default function ThemeSwitch({ component }) {
if ( component == undefined) {
if (component === undefined) {
component = document;
}
component.querySelectorAll(`.theme-switch-light`).forEach((button) => {
button.addEventListener('click', function(event) {
component.querySelectorAll('.theme-switch-light').forEach((button) => {
button.addEventListener('click', function (event) {
event.preventDefault();
Theme({ style: 'light-theme' });
Theme({ component, style: 'light-theme' });
});
});
component.querySelectorAll(`.theme-switch-dark`).forEach((button) => {
button.addEventListener('click', function(event) {
component.querySelectorAll('.theme-switch-dark').forEach((button) => {
button.addEventListener('click', function (event) {
event.preventDefault();
Theme({ style: 'dark-theme' });
Theme({ component, style: 'dark-theme' });
});
});
}

View File

@ -1,4 +1,4 @@
import { getPreference, setPreference } from './local-storage.js';
import { getPreference, setPreference } from './services/local-storage.js';
const PROPS = {
style_preference_name: 'theme',
@ -6,19 +6,22 @@ const PROPS = {
style_domain: 'docs.influxdata.com',
};
function getPreferredTheme () {
function getPreferredTheme() {
return `${getPreference(PROPS.style_preference_name)}-theme`;
}
function switchStyle({ styles_element, css_title }) {
// Disable all other theme stylesheets
styles_element.querySelectorAll('link[rel*="stylesheet"][title*="theme"]')
.forEach(function (link) {
link.disabled = true;
});
styles_element
.querySelectorAll('link[rel*="stylesheet"][title*="theme"]')
.forEach(function (link) {
link.disabled = true;
});
// Enable the stylesheet with the specified title
const link = styles_element.querySelector(`link[rel*="stylesheet"][title="${css_title}"]`);
const link = styles_element.querySelector(
`link[rel*="stylesheet"][title="${css_title}"]`
);
link && (link.disabled = false);
setPreference(PROPS.style_preference_name, css_title.replace(/-theme/, ''));
@ -38,5 +41,4 @@ export default function Theme({ component, style }) {
if (component.dataset?.themeCallback === 'setVisibility') {
setVisibility(component);
}
}

View File

@ -0,0 +1,38 @@
/**
* Helper functions for debugging without source maps
* Example usage:
* In your code, you can use these functions like this:
* ```javascript
* import { debugLog, debugBreak, debugInspect } from './debug-helpers.js';
*
* const data = debugInspect(someData, 'Data');
* debugLog('Processing data', 'myFunction');
*
* function processData() {
* // Add a breakpoint that works with DevTools
* debugBreak();
*
* // Your existing code...
* }
* ```
*
* @fileoverview DEVELOPMENT USE ONLY - Functions should not be committed to production
*/
/* eslint-disable no-debugger */
/* eslint-disable-next-line */
// NOTE: These functions are detected by ESLint rules to prevent committing debug code
export function debugLog(message, context = '') {
const contextStr = context ? `[${context}]` : '';
console.log(`DEBUG${contextStr}: ${message}`);
}
export function debugBreak() {
debugger;
}
export function debugInspect(value, label = 'Inspect') {
console.log(`DEBUG[${label}]:`, value);
return value;
}

View File

@ -0,0 +1,107 @@
/**
* Manages search interactions for DocSearch integration
* Uses MutationObserver to watch for dropdown creation
*/
export default function SearchInteractions({ searchInput }) {
const contentWrapper = document.querySelector('.content-wrapper');
let observer = null;
let dropdownObserver = null;
let dropdownMenu = null;
const debug = false; // Set to true for debugging logs
// Fade content wrapper when focusing on search input
function handleFocus() {
contentWrapper.style.opacity = '0.35';
contentWrapper.style.transition = 'opacity 300ms';
}
// Hide search dropdown when leaving search input
function handleBlur(event) {
// Only process blur if not clicking within dropdown
const relatedTarget = event.relatedTarget;
if (
relatedTarget &&
(relatedTarget.closest('.algolia-autocomplete') ||
relatedTarget.closest('.ds-dropdown-menu'))
) {
return;
}
contentWrapper.style.opacity = '1';
contentWrapper.style.transition = 'opacity 200ms';
// Hide dropdown if it exists
if (dropdownMenu) {
dropdownMenu.style.display = 'none';
}
}
// Add event listeners
searchInput.addEventListener('focus', handleFocus);
searchInput.addEventListener('blur', handleBlur);
// Use MutationObserver to detect when dropdown is added to the DOM
observer = new MutationObserver((mutations) => {
for (const mutation of mutations) {
if (mutation.type === 'childList') {
const newDropdown = document.querySelector(
'.ds-dropdown-menu:not([data-monitored])'
);
if (newDropdown) {
// Save reference to dropdown
dropdownMenu = newDropdown;
newDropdown.setAttribute('data-monitored', 'true');
// Monitor dropdown removal/display changes
dropdownObserver = new MutationObserver((dropdownMutations) => {
for (const dropdownMutation of dropdownMutations) {
if (debug) {
if (
dropdownMutation.type === 'attributes' &&
dropdownMutation.attributeName === 'style'
) {
console.log(
'Dropdown style changed:',
dropdownMenu.style.display
);
}
}
}
});
// Observe changes to dropdown attributes (like style)
dropdownObserver.observe(dropdownMenu, {
attributes: true,
attributeFilter: ['style'],
});
// Add event listeners to keep dropdown open when interacted with
dropdownMenu.addEventListener('mousedown', (e) => {
// Prevent blur on searchInput when clicking in dropdown
e.preventDefault();
});
}
}
}
});
// Start observing the document body for dropdown creation
observer.observe(document.body, {
childList: true,
subtree: true,
});
// Return cleanup function
return function cleanup() {
searchInput.removeEventListener('focus', handleFocus);
searchInput.removeEventListener('blur', handleBlur);
if (observer) {
observer.disconnect();
}
if (dropdownObserver) {
dropdownObserver.disconnect();
}
};
}

View File

@ -0,0 +1,35 @@
/**
* Platform detection utility functions
* Provides methods for detecting user's operating system
*/
/**
* Detects user's operating system using modern techniques
* Falls back to userAgent parsing when newer APIs aren't available
* @returns {string} Operating system identifier ("osx", "win", "linux", or "other")
*/
export function getPlatform() {
// Try to use modern User-Agent Client Hints API first (Chrome 89+, Edge 89+)
if (navigator.userAgentData && navigator.userAgentData.platform) {
const platform = navigator.userAgentData.platform.toLowerCase();
if (platform.includes('mac')) return 'osx';
if (platform.includes('win')) return 'win';
if (platform.includes('linux')) return 'linux';
}
// Fall back to userAgent string parsing
const userAgent = navigator.userAgent.toLowerCase();
if (
userAgent.includes('mac') ||
userAgent.includes('iphone') ||
userAgent.includes('ipad')
)
return 'osx';
if (userAgent.includes('win')) return 'win';
if (userAgent.includes('linux') || userAgent.includes('android'))
return 'linux';
return 'other';
}

View File

@ -1,6 +1,14 @@
import { CLOUD_URLS } from './influxdb-url.js';
import * as localStorage from './local-storage.js';
import { context, host, hostname, path, protocol, referrer, referrerHost } from './page-context.js';
import * as localStorage from './services/local-storage.js';
import {
context,
host,
hostname,
path,
protocol,
referrer,
referrerHost,
} from './page-context.js';
/**
* Builds a referrer whitelist array that includes the current page host and all
@ -69,8 +77,6 @@ function setWayfindingInputState() {
}
function submitWayfindingData(engine, action) {
// Build lp using page data and engine data
const lp = `ioxwayfinding,host=${hostname},path=${path},referrer=${referrer},engine=${engine} action="${action}"`;
@ -81,10 +87,7 @@ function submitWayfindingData(engine, action) {
'https://j32dswat7l.execute-api.us-east-1.amazonaws.com/prod/wayfinding'
);
xhr.setRequestHeader('X-Requested-With', 'XMLHttpRequest');
xhr.setRequestHeader(
'Access-Control-Allow-Origin',
`${protocol}//${host}`
);
xhr.setRequestHeader('Access-Control-Allow-Origin', `${protocol}//${host}`);
xhr.setRequestHeader('Content-Type', 'text/plain; charset=utf-8');
xhr.setRequestHeader('Accept', 'application/json');
xhr.send(lp);

View File

@ -1,19 +1,21 @@
// Select the product dropdown and dropdown items
const productDropdown = document.querySelector("#product-dropdown");
const dropdownItems = document.querySelector("#dropdown-items");
export default function ProductSelector({ component }) {
// Select the product dropdown and dropdown items
const productDropdown = component.querySelector('#product-dropdown');
const dropdownItems = component.querySelector('#dropdown-items');
// Expand the menu on click
if (productDropdown) {
productDropdown.addEventListener("click", function() {
productDropdown.classList.toggle("open");
dropdownItems.classList.toggle("open");
// Expand the menu on click
if (productDropdown) {
productDropdown.addEventListener('click', function () {
productDropdown.classList.toggle('open');
dropdownItems.classList.toggle('open');
});
}
// Close the dropdown by clicking anywhere else
document.addEventListener('click', function (e) {
// Check if the click was outside of the '.product-list' container
if (!e.target.closest('.product-list')) {
dropdownItems.classList.remove('open');
}
});
}
// Close the dropdown by clicking anywhere else
document.addEventListener("click", function(e) {
// Check if the click was outside of the '.product-list' container
if (!e.target.closest('.product-list')) {
dropdownItems.classList.remove("open");
}
});

View File

@ -0,0 +1,18 @@
/*
Datetime Components
----------------------------------------------
*/
.current-timestamp,
.current-date,
.current-time,
.enterprise-eol-date {
color: $current-timestamp-color;
display: inline-block;
font-family: $proxima;
white-space: nowrap;
}
.nowrap {
white-space: nowrap;
}

View File

@ -16,6 +16,10 @@
background: $article-code-bg !important;
font-size: .85em;
font-weight: $medium;
p {
background: $article-bg !important;
}
}
.node {

View File

@ -23,6 +23,7 @@
"layouts/syntax-highlighting",
"layouts/algolia-search-overrides",
"layouts/landing",
"layouts/datetime",
"layouts/error-page",
"layouts/footer-widgets",
"layouts/modals",

View File

@ -203,6 +203,12 @@ $article-btn-text-hover: $g20-white;
$article-nav-icon-bg: $g5-pepper;
$article-nav-acct-bg: $g3-castle;
// Datetime shortcode colors
$current-timestamp-color: $g15-platinum;
$current-date-color: $g15-platinum;
$current-time-color: $g15-platinum;
$enterprise-eol-date-color: $g15-platinum;
// Error Page Colors
$error-page-btn: $b-pool;
$error-page-btn-text: $g20-white;

View File

@ -203,6 +203,12 @@ $article-btn-text-hover: $g20-white !default;
$article-nav-icon-bg: $g6-smoke !default;
$article-nav-acct-bg: $g5-pepper !default;
// Datetime Colors
$current-timestamp-color: $article-text !default;
$current-date-color: $article-text !default;
$current-time-color: $article-text !default;
$enterprise-eol-date-color: $article-text !default;
// Error Page Colors
$error-page-btn: $b-pool !default;
$error-page-btn-text: $g20-white !default;

View File

@ -1,2 +0,0 @@
import:
- hugo.yml

View File

@ -1,4 +1,4 @@
baseURL: 'https://docs.influxdata.com/'
baseURL: https://docs.influxdata.com/
languageCode: en-us
title: InfluxDB Documentation
@ -49,21 +49,52 @@ privacy:
youtube:
disable: false
privacyEnhanced: true
outputFormats:
json:
mediaType: application/json
baseName: pages
isPlainText: true
# Asset processing configuration for development
build:
# Ensure Hugo correctly processes JavaScript modules
jsConfig:
nodeEnv: "development"
# Development asset processing
writeStats: false
useResourceCacheWhen: "fallback"
noJSConfigInAssets: false
# Asset processing configuration
assetDir: "assets"
module:
mounts:
- source: assets
target: assets
- source: node_modules
target: assets/node_modules
target: assets/node_modules
# Environment parameters
params:
env: development
environment: development
# Configure the server for development
server:
port: 1313
baseURL: 'http://localhost:1313/'
watchChanges: true
disableLiveReload: false
# Ignore specific warning logs
ignoreLogs:
- warning-goldmark-raw-html
# Disable minification for development
minify:
disableJS: true
disableCSS: true
disableHTML: true
minifyOutput: false

View File

@ -0,0 +1,40 @@
# Production overrides for CI/CD builds
baseURL: 'https://docs.influxdata.com/'
# Production environment parameters
params:
env: production
environment: production
# Enable minification for production
minify:
disableJS: false
disableCSS: false
disableHTML: false
minifyOutput: true
# Production asset processing
build:
writeStats: false
useResourceCacheWhen: "fallback"
buildOptions:
sourcemap: false
target: "es2015"
# Asset processing configuration
assetDir: "assets"
# Mount assets for production
module:
mounts:
- source: assets
target: assets
- source: node_modules
target: assets/node_modules
# Disable development server settings
server: {}
# Suppress the warning mentioned in the error
ignoreLogs:
- 'warning-goldmark-raw-html'

View File

@ -0,0 +1,17 @@
build:
writeStats: false
useResourceCacheWhen: "fallback"
buildOptions:
sourcemap: false
target: "es2015"
minify:
disableJS: false
disableCSS: false
disableHTML: false
minifyOutput: true
params:
env: production
environment: production
server: {
disableLiveReload: true
}

18
config/staging/hugo.yml Normal file
View File

@ -0,0 +1,18 @@
baseURL: https://test2.docs.influxdata.com/
build:
writeStats: false
useResourceCacheWhen: "fallback"
buildOptions:
sourcemap: false
target: "es2015"
minify:
disableJS: false
disableCSS: false
disableHTML: false
minifyOutput: true
params:
env: staging
environment: staging
server:
disableLiveReload: true

View File

@ -1,20 +0,0 @@
baseURL: 'http://localhost:1315/'
server:
port: 1315
# Override settings for testing
buildFuture: true
# Configure what content is built in testing env
params:
environment: testing
buildTestContent: true
# Keep your shared content exclusions
ignoreFiles:
- "content/shared/.*"
# Ignore specific warning logs
ignoreLogs:
- warning-goldmark-raw-html

View File

@ -1267,3 +1267,106 @@ This is small tab 2.4 content.
{{% /tab-content %}}
{{< /tabs-wrapper >}}
## Group key demo
Used to demonstrate Flux group keys
{{< tabs-wrapper >}}
{{% tabs "small" %}}
[Input](#)
[Output](#)
<span class="tab-view-output">Click to view output</span>
{{% /tabs %}}
{{% tab-content %}}
The following data is output from the last `filter()` and piped forward into `group()`:
> [!Note]
> `_start` and `_stop` columns have been omitted.
{{% flux/group-key "[_measurement=home, room=Kitchen, _field=hum]" true %}}
| _time | _measurement | room | _field | _value |
| :------------------- | :----------- | :---------- | :----- | :----- |
| 2022-01-01T08:00:00Z | home | Kitchen | hum | 35.9 |
| 2022-01-01T09:00:00Z | home | Kitchen | hum | 36.2 |
| 2022-01-01T10:00:00Z | home | Kitchen | hum | 36.1 |
{{% flux/group-key "[_measurement=home, room=Living Room, _field=hum]" true %}}
| _time | _measurement | room | _field | _value |
| :------------------- | :----------- | :---------- | :----- | :----- |
| 2022-01-01T08:00:00Z | home | Living Room | hum | 35.9 |
| 2022-01-01T09:00:00Z | home | Living Room | hum | 35.9 |
| 2022-01-01T10:00:00Z | home | Living Room | hum | 36 |
{{% flux/group-key "[_measurement=home, room=Kitchen, _field=temp]" true %}}
| _time | _measurement | room | _field | _value |
| :------------------- | :----------- | :---------- | :----- | :----- |
| 2022-01-01T08:00:00Z | home | Kitchen | temp | 21 |
| 2022-01-01T09:00:00Z | home | Kitchen | temp | 23 |
| 2022-01-01T10:00:00Z | home | Kitchen | temp | 22.7 |
{{% flux/group-key "[_measurement=home, room=Living Room, _field=temp]" true %}}
| _time | _measurement | room | _field | _value |
| :------------------- | :----------- | :---------- | :----- | :----- |
| 2022-01-01T08:00:00Z | home | Living Room | temp | 21.1 |
| 2022-01-01T09:00:00Z | home | Living Room | temp | 21.4 |
| 2022-01-01T10:00:00Z | home | Living Room | temp | 21.8 |
{{% /tab-content %}}
{{% tab-content %}}
When grouped by `_field`, all rows with the `temp` field will be in one table
and all the rows with the `hum` field will be in another.
`_measurement` and `room` columns no longer affect how rows are grouped.
{{% note %}}
`_start` and `_stop` columns have been omitted.
{{% /note %}}
{{% flux/group-key "[_field=hum]" true %}}
| _time | _measurement | room | _field | _value |
| :------------------- | :----------- | :---------- | :----- | :----- |
| 2022-01-01T08:00:00Z | home | Kitchen | hum | 35.9 |
| 2022-01-01T09:00:00Z | home | Kitchen | hum | 36.2 |
| 2022-01-01T10:00:00Z | home | Kitchen | hum | 36.1 |
| 2022-01-01T08:00:00Z | home | Living Room | hum | 35.9 |
| 2022-01-01T09:00:00Z | home | Living Room | hum | 35.9 |
| 2022-01-01T10:00:00Z | home | Living Room | hum | 36 |
{{% flux/group-key "[_field=temp]" true %}}
| _time | _measurement | room | _field | _value |
| :------------------- | :----------- | :---------- | :----- | :----- |
| 2022-01-01T08:00:00Z | home | Kitchen | temp | 21 |
| 2022-01-01T09:00:00Z | home | Kitchen | temp | 23 |
| 2022-01-01T10:00:00Z | home | Kitchen | temp | 22.7 |
| 2022-01-01T08:00:00Z | home | Living Room | temp | 21.1 |
| 2022-01-01T09:00:00Z | home | Living Room | temp | 21.4 |
| 2022-01-01T10:00:00Z | home | Living Room | temp | 21.8 |
{{% /tab-content %}}
{{< /tabs-wrapper >}}
## datetime/current-timestamp shortcode
### Default usage
{{< datetime/current-timestamp >}}
### Format YYYY-MM-DD HH:mm:ss
{{< datetime/current-timestamp format="YYYY-MM-DD HH:mm:ss" >}}
### Format with UTC timezone
{{< datetime/current-timestamp format="YYYY-MM-DD HH:mm:ss" timezone="UTC" >}}
### Format with America/New_York timezone
{{< datetime/current-timestamp format="YYYY-MM-DD HH:mm:ss" timezone="America/New_York" >}}

View File

@ -2,8 +2,10 @@ import { spawn } from 'child_process';
import fs from 'fs';
import http from 'http';
import net from 'net';
import process from 'process';
// Hugo server constants
export const HUGO_ENVIRONMENT = 'testing';
export const HUGO_PORT = 1315;
export const HUGO_LOG_FILE = '/tmp/hugo_server.log';
@ -28,7 +30,8 @@ export async function isPortInUse(port) {
/**
* Start the Hugo server with the specified options
* @param {Object} options - Configuration options for Hugo
* @param {string} options.configFile - Path to Hugo config file (e.g., 'config/testing/config.yml')
* @param {string} options.configFile - Path to Hugo config file
* @param {string} options.environment - Environment to run Hugo in
* @param {number} options.port - Port number for Hugo server
* @param {boolean} options.buildDrafts - Whether to build draft content
* @param {boolean} options.noHTTPCache - Whether to disable HTTP caching
@ -36,9 +39,10 @@ export async function isPortInUse(port) {
* @returns {Promise<Object>} Child process object
*/
export async function startHugoServer({
configFile = 'config/testing/config.yml',
configFile = 'config/_default/hugo.yml',
port = HUGO_PORT,
buildDrafts = true,
environment = 'testing',
buildDrafts = false,
noHTTPCache = true,
logFile = HUGO_LOG_FILE,
} = {}) {
@ -48,6 +52,8 @@ export async function startHugoServer({
const hugoArgs = [
'hugo',
'server',
'--environment',
environment,
'--config',
configFile,
'--port',
@ -64,16 +70,16 @@ export async function startHugoServer({
return new Promise((resolve, reject) => {
try {
// Use npx to find and execute Hugo, which will work regardless of installation method
console.log(`Running Hugo with npx: npx ${hugoArgs.join(' ')}`);
const hugoProc = spawn('npx', hugoArgs, {
// Use yarn to find and execute Hugo, which will work regardless of installation method
console.log(`Running Hugo with yarn: yarn ${hugoArgs.join(' ')}`);
const hugoProc = spawn('yarn', hugoArgs, {
stdio: ['ignore', 'pipe', 'pipe'],
shell: true,
});
// Check if the process started successfully
if (!hugoProc || !hugoProc.pid) {
return reject(new Error('Failed to start Hugo server via npx'));
return reject(new Error('Failed to start Hugo server via yarn'));
}
// Set up logging

View File

@ -38,9 +38,10 @@ import fs from 'fs';
import path from 'path';
import cypress from 'cypress';
import net from 'net';
import matter from 'gray-matter';
import { Buffer } from 'buffer';
import { displayBrokenLinksReport, initializeReport } from './link-reporter.js';
import {
HUGO_ENVIRONMENT,
HUGO_PORT,
HUGO_LOG_FILE,
startHugoServer,
@ -90,28 +91,6 @@ async function isPortInUse(port) {
});
}
/**
* Extract source information from frontmatter
* @param {string} filePath - Path to the markdown file
* @returns {string|null} Source information if present
*/
function getSourceFromFrontmatter(filePath) {
if (!fs.existsSync(filePath)) {
return null;
}
try {
const fileContent = fs.readFileSync(filePath, 'utf8');
const { data } = matter(fileContent);
return data.source || null;
} catch (err) {
console.warn(
`Warning: Could not extract frontmatter from ${filePath}: ${err.message}`
);
return null;
}
}
/**
* Ensures a directory exists, creating it if necessary
* Also creates an empty file to ensure the directory is not empty
@ -296,7 +275,7 @@ async function main() {
});
console.log('Hugo is available on the system');
} catch (checkErr) {
} catch {
console.log(
'Hugo not found on PATH, will use project-local Hugo via yarn'
);
@ -304,9 +283,8 @@ async function main() {
// Use the startHugoServer function from hugo-server.js
hugoProc = await startHugoServer({
configFile: 'config/testing/config.yml',
environment: HUGO_ENVIRONMENT,
port: HUGO_PORT,
buildDrafts: true,
noHTTPCache: true,
logFile: HUGO_LOG_FILE,
});
@ -412,7 +390,7 @@ async function main() {
` Note: ${testFailureCount} test(s) failed but no broken links were detected in the report.`
);
console.warn(
` This usually indicates test errors unrelated to link validation.`
' This usually indicates test errors unrelated to link validation.'
);
// We should not consider special case domains (those with expected errors) as failures

View File

@ -66,6 +66,23 @@ cloud:
- name: East US (Virginia)
location: Virginia, USA
url: https://eastus-1.azure.cloud2.influxdata.com
serverless:
product: InfluxDB Cloud
providers:
- name: Amazon Web Services
short_name: AWS
iox: true
regions:
- name: US East (Virginia)
location: Virginia, USA
url: https://us-east-1-1.aws.cloud2.influxdata.com
iox: true
- name: EU Frankfurt
location: Frankfurt, Germany
url: https://eu-central-1-1.aws.cloud2.influxdata.com
iox: true
cloud_dedicated:
providers:
- name: Default

View File

@ -106,6 +106,33 @@ export default [
files: ['assets/js/**/*.js'],
rules: {
// Rules specific to JavaScript in Hugo assets
// Prevent imports from debug-helpers.js
'no-restricted-imports': [
'error',
{
paths: [
{
name: './utils/debug-helpers.js',
message:
'Remove debugging functions before committing. Debug helpers should not be used in production code.',
},
{
name: '/utils/debug-helpers.js',
message:
'Remove debugging functions before committing. Debug helpers should not be used in production code.',
},
],
},
],
// Prevent use of debug functions in production code
'no-restricted-syntax': [
'error',
{
selector: 'CallExpression[callee.name=/^debug(Log|Break|Inspect)$/]',
message:
'Remove debugging functions before committing. Debug helpers should not be used in production code.',
},
],
},
},
{

View File

@ -1,60 +0,0 @@
baseURL: https://test2.docs.influxdata.com/
languageCode: en-us
title: InfluxDB Documentation
# Git history information for lastMod-type functionality
enableGitInfo: true
# Syntax Highlighting
pygmentsCodefences: true
pygmentsUseClasses: true
# Preserve case in article tags
preserveTaxonomyNames: true
# Generate a robots.txt
enableRobotsTXT: true
# Custom staging params
params:
environment: staging
# Markdown rendering options
blackfriday:
hrefTargetBlank: true
smartDashes: false
taxonomies:
influxdb/v2/tag: influxdb/v2/tags
influxdb/cloud/tag: influxdb/cloud/tags
influxdb3/cloud-serverless/tag: influxdb/cloud-serverless/tags
influxdb/cloud-dedicated/tag: influxdb/cloud-dedicated/tags
influxdb/clustered/tag: influxdb/clustered/tags
influxdb3/core/tag: influxdb3/core/tags
influxdb3/enterprise/tag: influxdb3/enterprise/tags
flux/v0/tag: flux/v0/tags
markup:
goldmark:
renderer:
unsafe: true
extensions:
linkify: false
parser:
attribute:
block: true
privacy:
googleAnalytics:
anonymizeIP: false
disable: false
respectDoNotTrack: true
useSessionStorage: false
youtube:
disable: false
privacyEnhanced: true
outputFormats:
json:
mediaType: application/json
baseName: pages
isPlainText: true

18019
hugo_stats.json Normal file

File diff suppressed because it is too large Load Diff

View File

@ -19,7 +19,7 @@
<div class="home-content">
<div class="section search">
<div class="sidebar--search">
<div class="sidebar--search" data-component="sidebar-search">
<input class="sidebar--search-field"
id="algolia-search-input"
type="text"

View File

@ -13,7 +13,7 @@
{{ $urlCalloutText := $scratch.Get "urlCalloutText" }}
<!-- {{ if or $isOSS $isCloud $isHome }}
<div class="feature-callout start-position" id="callout-url-selector">
<div class="feature-callout start-position" id="callout-url-selector" data-component="feature-callout">
<p>{{ $urlCalloutText }} <a href="#" class="close"><span class="icon-remove"></span></a></p>
</div>
{{ end }} -->

View File

@ -1,18 +1,3 @@
{{ $jquery := resources.Get "js/jquery-3.5.0.min.js" }}
{{ $versionSelector := resources.Get "js/version-selector.js" }}
{{ $searchInteractions := resources.Get "js/search-interactions.js" }}
{{ $listFilters := resources.Get "js/list-filters.js" }}
{{ $featureCallouts := resources.Get "js/feature-callouts.js" }}
{{ $keybindings := resources.Get "js/keybindings.js" }}
{{ $fluxGroupKeys := resources.Get "js/flux-group-keys.js" }}
{{ $dateTime := resources.Get "js/datetime.js" }}
{{ $homepageInteractions := resources.Get "js/home-interactions.js" }}
{{ $releaseTOC := resources.Get "/js/release-toc.js" }}
{{ $footerjs := slice $jquery $versionSelector $searchInteractions $listFilters $featureCallouts $keybindings $homepageInteractions | resources.Concat "js/footer.bundle.js" | resources.Fingerprint }}
{{ $fluxGroupKeyjs := $fluxGroupKeys | resources.Fingerprint }}
{{ $dateTimejs := $dateTime | resources.Fingerprint }}
{{ $releaseTOCjs := $releaseTOC | resources.Fingerprint }}
<!-- Load cloudUrls array -->
<script type="text/javascript">
cloudUrls = [
@ -21,37 +6,3 @@
{{ end -}}
]
</script>
{{ if .Page.HasShortcode "diagram" }}
<!-- Load mermaid.js for diagrams -->
<script src="https://cdn.jsdelivr.net/npm/mermaid/dist/mermaid.min.js"></script>
<script>
mermaid.initialize({
startOnLoad: true,
themeVariables: {
fontFamily: "Proxima Nova",
fontSize: '18px',
}
})
</script>
{{ end }}
<!-- Load group key demo JS if when the group key demo shortcode is present -->
{{ if .Page.HasShortcode "flux/group-key-demo" }}
<script type="text/javascript" src="{{ $fluxGroupKeyjs.RelPermalink }}"></script>
{{ end }}
<!-- Load datetime js if when datetime shortcodes are present -->
{{ if or (.Page.HasShortcode "datetime/current-time") (.Page.HasShortcode "datetime/current-timestamp")
(.Page.HasShortcode "datetime/current-date") (.Page.HasShortcode "datetime/enterprise-eol-date") }}
<script type="text/javascript" src="{{ $dateTimejs.RelPermalink }}"></script>
{{ end }}
<!-- Load code release-toc js when release-toc shortcode is present -->
{{ if .Page.HasShortcode "release-toc" }}
<script type="text/javascript" src="{{ $releaseTOCjs.RelPermalink }}"></script>
{{ end }}
<!-- Load footer.js -->
<script type="text/javascript" src="{{ $footerjs.RelPermalink }}"></script>

View File

@ -7,84 +7,15 @@
{{ $includeFlux := and (in $fluxSupported $product) (in $influxdbFluxSupport $version) }}
{{ $includeResources := not (in (slice "cloud-serverless" "cloud-dedicated" "clustered" "core" "enterprise" "explorer") $version) }}
<script src="https://cdn.jsdelivr.net/npm/docsearch.js@2/dist/cdn/docsearch.min.js"></script>
<script>
var multiVersion = ['influxdb']
docsearch({
apiKey: '501434b53a46a92a7931aecc7c9672e2',
appId: 'WHM9UWMP6M',
indexName: 'influxdata',
inputSelector: '#algolia-search-input',
// Set debug to true if you want to inspect the dropdown
debug: true,
transformData: function (hits) {
function fmtVersion (version, productKey) {
if (version == null) {
return '';
} else if (version === 'cloud') {
return 'Cloud (TSM)';
} else if (version === 'core') {
return 'Core';
} else if (version === 'enterprise') {
return 'Enterprise';
} else if (version === 'explorer') {
return 'Explorer';
} else if (version === 'cloud-serverless') {
return 'Cloud Serverless';
} else if (version === 'cloud-dedicated') {
return 'Cloud Dedicated';
} else if (version === 'clustered') {
return 'Clustered';
} else if (multiVersion.includes(productKey)) {
return version;
} else {
return '';
}
};
productNames = {
influxdb: 'InfluxDB',
influxdb3: 'InfluxDB 3',
enterprise_influxdb: 'InfluxDB Enterprise',
flux: 'Flux',
telegraf: 'Telegraf',
chronograf: 'Chronograf',
kapacitor: 'Kapacitor',
platform: 'InfluxData Platform',
resources: 'Additional Resources',
};
hits.map(hit => {
pathData = new URL(hit.url).pathname.split('/').filter(n => n);
product = productNames[pathData[0]];
version = fmtVersion(pathData[1], pathData[0]);
hit.product = product;
hit.version = version;
hit.hierarchy.lvl0 =
hit.hierarchy.lvl0 +
` <span class=\"search-product-version\">${product} ${version}</span>`;
hit._highlightResult.hierarchy.lvl0.value =
hit._highlightResult.hierarchy.lvl0.value +
` <span class=\"search-product-version\">${product} ${version}</span>`;
});
return hits;
},
algoliaOptions: {
hitsPerPage: 10,
'facetFilters': [
{{ if or (eq $product "platform") (eq $product "resources") (le (len $productPathData) 1) }}
'latest:true'
{{ else if $includeFlux }}
['searchTag: {{ $product }}-{{ $version }}', 'flux:true', 'resources:{{ $includeResources }}']
{{ else }}
['searchTag: {{ $product }}-{{ $version }}', 'resources:{{ $includeResources }}']
{{ end }}
]
},
autocompleteOptions: {
templates: {
header: '<div class="search-all-content"><a href="https:\/\/support.influxdata.com" target="_blank">Search all InfluxData content <span class="icon-arrow-up-right"></span></a>',
empty: '<div class="search-no-results"><p>Not finding what you\'re looking for?</p> <a href="https:\/\/support.influxdata.com" target="_blank">Search all InfluxData content <span class="icon-arrow-up-right"></span></a></div>'
}
}
});
</script>
<!-- DocSearch Component Container -->
<div
data-component="doc-search"
data-api-key="501434b53a46a92a7931aecc7c9672e2"
data-app-id="WHM9UWMP6M"
data-index-name="influxdata"
data-input-selector="#algolia-search-input"
data-search-tag="{{ $product }}-{{ $version }}"
data-include-flux="{{ $includeFlux }}"
data-include-resources="{{ $includeResources }}"
data-debug="{{ if hugo.IsProduction }}false{{ else }}true{{ end }}"
></div>

View File

@ -5,20 +5,49 @@
<!-- Get site data -->
<!-- Load cloudUrls array -->
{{ $cloudUrls := slice }}
{{- range.Site.Data.influxdb_urls.cloud.providers }}
{{- range.regions }}
{{ $cloudUrls = $cloudUrls | append "{{ safeHTML .url }}" }}
{{- range .Site.Data.influxdb_urls.cloud.providers }}
{{- range .regions }}
{{ $cloudUrls = $cloudUrls | append (safeHTML .url) }}
{{ end -}}
{{ end -}}
{{ $products := .Site.Data.products }}
{{ $influxdb_urls := .Site.Data.influxdb_urls }}
<!-- Build main.js -->
{{ with resources.Get "js/index.js" }}
{{ $isDevelopment := false }}
{{ $isTesting := false }}
{{ with hugo }}
{{ if .IsDevelopment }}
{{ $isDevelopment = .IsDevelopment }}
{{ end }}
{{ end }}
{{ if eq .Site.Params.env "testing" }}
{{ $isTesting = true }}
{{ end }}
{{ if eq .Site.Params.environment "testing" }}
{{ $isTesting = true }}
{{ end }}
{{ $isDevelopmentOrTesting := or $isDevelopment $isTesting }}
{{ with resources.Get "js/main.js" }}
{{ $opts := dict
"minify" hugo.IsProduction
"sourceMap" (cond hugo.IsProduction "" "external")
"sourceMap" (cond $isDevelopmentOrTesting "inline" "")
"format" (cond $isDevelopmentOrTesting "esm" "iife")
"bundle" true
"targetPath" "js/main.js"
"params" (dict "product" $product "currentVersion" $currentVersion "isServer" hugo.IsServer "products" $products "influxdb_urls" $influxdb_urls "cloudUrls" $cloudUrls)
"params" (dict
"product" $product
"currentVersion" $currentVersion
"isServer" hugo.IsServer
"products" $products
"influxdb_urls" $influxdb_urls
"cloudUrls" $cloudUrls
"isDevelopment" $isDevelopmentOrTesting
)
}}
{{ with . | js.Build $opts }}
{{ if hugo.IsProduction }}
@ -26,7 +55,7 @@
<script src="{{ .RelPermalink }}" integrity="{{ .Data.Integrity }}" crossorigin="anonymous"></script>
{{ end }}
{{ else }}
<script src="{{ .RelPermalink }}"></script>
<script type="module" src="{{ .RelPermalink }}"></script>
{{ end }}
{{ end }}
{{ end }}
{{ end }}

View File

@ -49,7 +49,7 @@
<aside class="sidebar">
{{ partial "sidebar/sidebar-toggle.html" (dict "state" "Close") }}
<div class="search-and-nav-toggle">
<div class="sidebar--search">
<div class="sidebar--search" data-component="sidebar-search">
<input class="sidebar--search-field"
id="algolia-search-input"
type="text"

View File

@ -43,7 +43,7 @@ Identify products by their product path. Dictionary schema:
{{ $templateDefaults := dict "context" . "productInfo" $productInfo "altLinks" $altLinks "pageRoot" $pageRoot "useRootProductLink" $useRootProductLink }}
<div class="product-list">
<div class="product-list" data-component="product-selector">
<div id="product-dropdown">
<p class="selected">{{ index (index $productInfo $pageRoot) 0 | default "Select product" }}</p>
</div>

View File

@ -1,3 +1,3 @@
<div class="mermaid">
<div class="mermaid" data-component="diagram">
{{.Inner}}
</div>

View File

@ -1,4 +1,4 @@
<div id="flux-group-keys-demo">
<div id="flux-group-keys-demo" data-component="flux-group-keys-demo">
<div id="group-by-columns">
<ul class="column-list">
<li>

View File

@ -5,4 +5,4 @@
{{- $mac := .Get "mac" | default $default -}}
{{- $win := .Get "win" | default $default -}}
{{- $linux := .Get "linux" | default $default -}}
<span class="keybinding" data-osx="{{ $mac }}" data-win="{{ $win }}" data-linux="{{ $linux }}"><code>{{ $default }}</code></span>
<span class="keybinding" data-osx="{{ $mac }}" data-win="{{ $win }}" data-linux="{{ $linux }}" data-component="keybinding"><code>{{ $default }}</code></span>

View File

@ -1,6 +1,6 @@
{{ $source := .Get 0 | default "telegraf"}}
<div id="list-filters">
<div id="list-filters" data-component="list-filters">
{{ range ( index .Site.Data.list_filters $source) }}
{{ $numValues := len .values }}

View File

@ -2,7 +2,7 @@
{{- $currentVersion := index $productPathData 1 -}}
{{- $show := .Get "show" | default 12 -}}
<div id="release-toc" class="{{ $currentVersion }}">
<div id="release-toc" class="{{ $currentVersion }}" data-component="release-toc">
<ul id="release-list" style="height: calc({{ $show }} * 1.885rem);" show="{{ $show }}">
<!-- PLACEHOLDER FOR JS-GENERATED LIST ITEMS -->
</ul>

View File

@ -5,6 +5,10 @@
pre-commit:
parallel: true
commands:
eslint-debug-check:
glob: "assets/js/*.js"
run: yarn eslint {staged_files}
fail_text: "Debug helpers found! Remove debug imports and calls before committing."
build-copilot-instructions:
glob: "CONTRIBUTING.md"
run: yarn build:copilot-instructions
@ -58,6 +62,10 @@ pre-commit:
{ echo "⚠️ Prettier found formatting issues. Automatic formatting applied."
git add {staged_files}
}
lint-js:
glob: "assets/js/*.{js,ts}"
run: yarn eslint {staged_files}
fail_text: "JavaScript linting failed. Fix errors before committing."
pre-push:
commands:
packages-audit:

View File

@ -66,8 +66,6 @@
"test:links:api-docs": "node cypress/support/run-e2e-specs.js --spec \"cypress/e2e/content/article-links.cy.js\" /influxdb3/core/api/,/influxdb3/enterprise/api/,/influxdb3/cloud-dedicated/api/,/influxdb3/cloud-dedicated/api/v1/,/influxdb/cloud-dedicated/api/v1/,/influxdb/cloud-dedicated/api/management/,/influxdb3/cloud-dedicated/api/management/",
"test:shortcode-examples": "node cypress/support/run-e2e-specs.js --spec \"cypress/e2e/content/article-links.cy.js\" content/example.md"
},
"main": "assets/js/main.js",
"module": "assets/js/main.js",
"type": "module",
"browserslist": [
"last 2 versions",