src/documentation/generateTable.js
import { isUndefined } from 'lodash';
import stripAnsi from 'strip-ansi';
import merge from '../helpers/merge';
import addPadding from './helpers/addPadding';
import pad from './helpers/pad';
/* eslint-disable no-unused-vars */
const defaultSettings = {
groupTitleWrapper: (name, level, parents) => name,
cellDivider: '|',
rowWrapper: (input, level) => `|${input}|`,
cellWrapper: (input) => ` ${input} `,
header: true,
compact: false,
};
/* eslint-enable */
/**
* Creates a table based on a documentation object.
*
* @returns {string} - A formatted table.
*/
export default function generateTable(initalDocumentationObject, header, settings) {
const finalSettings = merge(defaultSettings, settings);
const headerLengths = createLengthObject(header, header, {}, true);
const lengths = createLengthObject(initalDocumentationObject, header, headerLengths);
const printTableCell = (key, element, renderer = (input) => input || '', fill = false, object) => {
if (fill) {
return finalSettings.cellWrapper(pad(lengths[key], '-'));
}
const padding = isUndefined(header[key].padding) ? true : header[key].padding;
const text = padding ?
addPadding(renderer(element, object), lengths[key]) :
renderer(element, object);
return finalSettings.cellWrapper(text);
};
const printTableRow = (object, level, isHeader = false) =>
finalSettings.rowWrapper(Object.keys(header).map((key) => {
const { value, renderer } = getValueAndRenderer(isHeader, header[key], object[key]);
return printTableCell(key, value, renderer, undefined, object);
}).join(finalSettings.cellDivider), level);
const printTableSplitter = (level) =>
finalSettings.rowWrapper(Object.keys(header).map((key) =>
printTableCell(key, undefined, undefined, true)
).join(finalSettings.cellDivider), level);
const printTableHead = ({ name, description, raw }, parentNames, level = 0, printTableHeader = true) => {
const rows = [];
rows.push(finalSettings.groupTitleWrapper(name, level, parentNames));
if (description) {
rows.push(description, '');
} else if (finalSettings.header) {
rows.push('');
}
if (raw) {
rows.push('✓ ― Supports __raw', '');
}
if (finalSettings.header && printTableHeader) {
rows.push(printTableRow(header, level, true));
rows.push(printTableSplitter(level));
}
return rows;
};
const recursiveHelper = (documentationObject = [], parentNames, first = false) => {
const spacing = finalSettings.compact || first ? [] : [''];
return documentationObject.map((group) => {
let rows = [];
const level = group.level || 0;
const objects = group.objects || [];
if (level === 0 || !finalSettings.compact) {
rows = rows.concat(
printTableHead(group, parentNames, level, objects.length > 0)
);
}
rows = rows.concat(objects.map((element) =>
printTableRow(element, level)));
rows = rows.concat(recursiveHelper(group.children, parentNames.concat(group.name)));
if (group.level !== undefined && level === 0 && finalSettings.compact) {
rows.push('');
}
return rows;
}).reduce((a, b) => a.concat(b), spacing);
};
return recursiveHelper(initalDocumentationObject, [], true).join('\n');
}
function createLengthObject(initalElements, header, initalLengths, isHeader = false) {
const getObjectLength = (element, renderer = (input) => input, object) =>
stripAnsi(renderer(element, object)).length;
const getLength = (newLength, currentLength = 0) =>
(newLength > currentLength ? newLength : currentLength);
const recursiveHelper = (elements = [], lengths = {}) => {
let newLengths = { ...lengths };
elements.forEach((element) => {
const objects = element.objects || [];
objects.forEach((object) => {
Object.keys(header).forEach((key) => {
const { value, renderer } = getValueAndRenderer(isHeader, header[key], object[key]);
if (value !== undefined && value !== null) {
newLengths[key] = getLength(getObjectLength(value, renderer, object), newLengths[key]);
}
});
});
newLengths = recursiveHelper(element.children, newLengths);
});
return newLengths;
};
// Make sure the data matches the expected format
const elements = !Array.isArray(initalElements) ?
[{ objects: [initalElements], children: [] }] :
initalElements;
return recursiveHelper(elements, initalLengths);
}
function getValueAndRenderer(isHeader, headerObject, object) {
const value = isHeader ? object.name : object;
const renderer = isHeader ? (input) => input : headerObject.renderer;
return {
value,
renderer,
};
}