@redocly/theme 0.30.8 → 0.31.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/lib/components/Catalog/CatalogCard.d.ts +0 -1
- package/lib/components/Catalog/CatalogCard.js +27 -16
- package/lib/components/Catalog/useCatalog.js +14 -7
- package/lib/components/Filter/Filter.js +1 -5
- package/lib/components/LastUpdated/LastUpdated.js +4 -1
- package/lib/components/OpenApiDocs/ScorecardBadges.d.ts +2 -1
- package/lib/components/OpenApiDocs/ScorecardBadges.js +29 -5
- package/lib/components/Scorecard/Card.d.ts +4 -1
- package/lib/components/Scorecard/Card.js +14 -3
- package/lib/components/Scorecard/Gauge.d.ts +5 -2
- package/lib/components/Scorecard/Gauge.js +6 -3
- package/lib/components/Scorecard/StatusByLevelWidget.d.ts +1 -0
- package/lib/components/Scorecard/StatusByLevelWidget.js +5 -2
- package/lib/config.d.ts +24 -5
- package/lib/config.js +3 -7
- package/lib/types/portal/src/shared/constants.d.ts +1 -0
- package/lib/types/portal/src/shared/constants.js +2 -1
- package/lib/types/portal/src/shared/types/catalog.d.ts +6 -2
- package/package.json +1 -1
- package/src/components/Catalog/CatalogCard.tsx +34 -19
- package/src/components/Catalog/useCatalog.ts +23 -9
- package/src/components/Filter/Filter.tsx +1 -6
- package/src/components/LastUpdated/LastUpdated.tsx +6 -1
- package/src/components/OpenApiDocs/ScorecardBadges.tsx +35 -7
- package/src/components/Scorecard/Card.tsx +14 -2
- package/src/components/Scorecard/Gauge.tsx +16 -8
- package/src/components/Scorecard/StatusByLevelWidget.tsx +6 -2
- package/src/config.ts +2 -6
- package/src/types/portal/src/shared/constants.ts +1 -0
- package/src/types/portal/src/shared/types/catalog.ts +3 -2
|
@@ -26,7 +26,7 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
|
26
26
|
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
27
27
|
};
|
|
28
28
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
29
|
-
exports.
|
|
29
|
+
exports.CatalogCard = void 0;
|
|
30
30
|
const React = __importStar(require("react"));
|
|
31
31
|
const styled_components_1 = __importDefault(require("styled-components"));
|
|
32
32
|
const Link_1 = require("../../mocks/Link");
|
|
@@ -35,9 +35,10 @@ const Tag_1 = require("../../components/Tag");
|
|
|
35
35
|
const icons_1 = require("../../icons");
|
|
36
36
|
const telemetry_1 = require("../../mocks/telemetry");
|
|
37
37
|
const utils_1 = require("../../utils");
|
|
38
|
+
const Card_1 = require("../../components/Scorecard/Card");
|
|
38
39
|
function CatalogCard({ item }) {
|
|
39
40
|
var _a, _b;
|
|
40
|
-
const hasTags = item.
|
|
41
|
+
const hasTags = item.scorecardLevel || ((_a = item.tags) === null || _a === void 0 ? void 0 : _a.length);
|
|
41
42
|
return (React.createElement(Link_1.Link, { key: item.docsLink || item.link, to: item.docsLink || item.link },
|
|
42
43
|
React.createElement(StyledCard, { onClick: () => telemetry_1.telemetry.send('catalog_item_clicked', {}) },
|
|
43
44
|
React.createElement(CardTitle, null,
|
|
@@ -49,26 +50,13 @@ function CatalogCard({ item }) {
|
|
|
49
50
|
hasTags && (React.createElement(CardTags, null,
|
|
50
51
|
(item.tags || []).map((tag, index) => (React.createElement(CardTag, { key: tag + index, color: (0, utils_1.slug)(tag) },
|
|
51
52
|
React.createElement(Highlight_1.Highlight, null, (0, utils_1.capitalize)(tag))))),
|
|
52
|
-
(item.scorecardLevel &&
|
|
53
|
+
(item.scorecardLevel && (React.createElement(StatusDot, { color: (0, Card_1.getLevelColor)(item.scorecardLevelIdx || -1, Object.keys(item.scorecardLevels || {}).length) },
|
|
53
54
|
React.createElement(Highlight_1.Highlight, null, item.scorecardLevel)))) ||
|
|
54
55
|
null)),
|
|
55
56
|
React.createElement(SelectButton, null,
|
|
56
57
|
React.createElement(icons_1.PointingArrowIcon, null)))))));
|
|
57
58
|
}
|
|
58
59
|
exports.CatalogCard = CatalogCard;
|
|
59
|
-
function statusToColor(status) {
|
|
60
|
-
switch (status) {
|
|
61
|
-
case 'Below minimum':
|
|
62
|
-
return 'error';
|
|
63
|
-
case 'Minimum':
|
|
64
|
-
return 'warning';
|
|
65
|
-
case 'Highest':
|
|
66
|
-
return 'success';
|
|
67
|
-
default:
|
|
68
|
-
return '';
|
|
69
|
-
}
|
|
70
|
-
}
|
|
71
|
-
exports.statusToColor = statusToColor;
|
|
72
60
|
const SelectButton = styled_components_1.default.div `
|
|
73
61
|
border: 1px solid var(--catalog-card-button-border-color);
|
|
74
62
|
border-radius: 100%;
|
|
@@ -153,4 +141,27 @@ const CardTag = (0, styled_components_1.default)(Tag_1.Tag) `
|
|
|
153
141
|
text-transform: inherit;
|
|
154
142
|
margin: 0;
|
|
155
143
|
`;
|
|
144
|
+
const StatusDot = styled_components_1.default.div `
|
|
145
|
+
font-size: var(--font-size-base);
|
|
146
|
+
font-style: normal;
|
|
147
|
+
font-weight: 400;
|
|
148
|
+
line-height: 22px;
|
|
149
|
+
display: flex;
|
|
150
|
+
align-items: center;
|
|
151
|
+
&:not(:only-child) {
|
|
152
|
+
margin-left: 4px;
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
${({ color }) => color &&
|
|
156
|
+
`
|
|
157
|
+
&::before {
|
|
158
|
+
content: '';
|
|
159
|
+
display: inline-block;
|
|
160
|
+
width: 10px;
|
|
161
|
+
height: 10px;
|
|
162
|
+
border-radius: 50%;
|
|
163
|
+
background-color: ${color};
|
|
164
|
+
margin-right: 8px;
|
|
165
|
+
}`};
|
|
166
|
+
`;
|
|
156
167
|
//# sourceMappingURL=CatalogCard.js.map
|
|
@@ -117,7 +117,6 @@ function useCatalog(items, config) {
|
|
|
117
117
|
// filterParents[i] is a Set with indexes of parents of this filter
|
|
118
118
|
const filterParents = React.useMemo(() => collectFilterParents(config.filters), [config.filters]);
|
|
119
119
|
return React.useMemo(() => {
|
|
120
|
-
var _a;
|
|
121
120
|
const filters = filtersWithOptions.map((filter, idx) => {
|
|
122
121
|
var _a, _b, _c, _d;
|
|
123
122
|
return (Object.assign(Object.assign({}, filter), { toggleOption: (value) => toggleOption(idx, value), selectOption: (value) => selectOption(idx, value), selectedOptions: (_a = filtersState[idx]) !== null && _a !== void 0 ? _a : new Set(), isFilterUsed: ((_c = (_b = filtersState[idx]) === null || _b === void 0 ? void 0 : _b.size) !== null && _c !== void 0 ? _c : 0) > 0 || !!((_d = filtersState[idx]) === null || _d === void 0 ? void 0 : _d.from) }));
|
|
@@ -140,7 +139,7 @@ function useCatalog(items, config) {
|
|
|
140
139
|
const adjustedFilterOptions = collectFilterOptions(filteredWithOtherFilters.map((m) => ({ metadata: m })), config.filters);
|
|
141
140
|
return Object.assign(Object.assign({}, filter), { parentUsed, filteredOptions: adjustedFilterOptions[idx].options });
|
|
142
141
|
});
|
|
143
|
-
const groups = config.groupByFirstFilter
|
|
142
|
+
const groups = config.groupByFirstFilter
|
|
144
143
|
? groupByFirstFilter(resolvedFilters, filteredItems)
|
|
145
144
|
: [{ title: 'APIs', items: filteredItems }];
|
|
146
145
|
return {
|
|
@@ -241,7 +240,7 @@ function collectFilterOptions(items, filters) {
|
|
|
241
240
|
? (_b = item.metadata) === null || _b === void 0 ? void 0 : _b[filter.property]
|
|
242
241
|
: [(_c = item.metadata) === null || _c === void 0 ? void 0 : _c[filter.property]];
|
|
243
242
|
for (const value of values) {
|
|
244
|
-
const str = String(value);
|
|
243
|
+
const str = mapFilterValues(String(value), filter.valuesMapping);
|
|
245
244
|
if (staticOptions) {
|
|
246
245
|
if (str in staticOptions) {
|
|
247
246
|
usedOptions[str] = usedOptions[str] + 1;
|
|
@@ -256,9 +255,10 @@ function collectFilterOptions(items, filters) {
|
|
|
256
255
|
}
|
|
257
256
|
}
|
|
258
257
|
}
|
|
259
|
-
const options = Object.entries(usedOptions)
|
|
260
|
-
|
|
261
|
-
.sort((a, b) => b.value.localeCompare(a.value));
|
|
258
|
+
const options = Object.entries(usedOptions).map(([value, count]) => ({ value, count }));
|
|
259
|
+
if (!staticOptions) {
|
|
260
|
+
options.sort((a, b) => b.value.localeCompare(a.value));
|
|
261
|
+
}
|
|
262
262
|
if (othersCount) {
|
|
263
263
|
options.push({
|
|
264
264
|
value: filter.missingCategoryNameTranslationKey || filter.missingCategoryName || 'Others',
|
|
@@ -286,7 +286,7 @@ function filterItems(normalizedItems, filters, term) {
|
|
|
286
286
|
if (filter.selectedOptions.size === 0) {
|
|
287
287
|
return true;
|
|
288
288
|
}
|
|
289
|
-
const itemValue = (item === null || item === void 0 ? void 0 : item[filter.property]) || filter.missingCategoryName || 'Others';
|
|
289
|
+
const itemValue = mapFilterValues((item === null || item === void 0 ? void 0 : item[filter.property]) || filter.missingCategoryName || 'Others', filter.valuesMapping);
|
|
290
290
|
if (Array.isArray(itemValue)) {
|
|
291
291
|
return itemValue.some((value) => filter.selectedOptions.has(value));
|
|
292
292
|
}
|
|
@@ -307,4 +307,11 @@ function filterItems(normalizedItems, filters, term) {
|
|
|
307
307
|
return filteredByFilters;
|
|
308
308
|
}
|
|
309
309
|
}
|
|
310
|
+
function mapFilterValues(value, mapping) {
|
|
311
|
+
if (!mapping)
|
|
312
|
+
return value;
|
|
313
|
+
return Array.isArray(value)
|
|
314
|
+
? value.map((v) => mapping[String(v)] || v)
|
|
315
|
+
: mapping[String(value)] || value;
|
|
316
|
+
}
|
|
310
317
|
//# sourceMappingURL=useCatalog.js.map
|
|
@@ -52,7 +52,7 @@ function Filter({ filter, filterValuesCasing, }) {
|
|
|
52
52
|
return;
|
|
53
53
|
filter.selectOption(Object.assign(Object.assign({}, filter.selectedOptions), { to: formatDateWithNoTimeZone(to) }));
|
|
54
54
|
} })))) : (filter.filteredOptions.map((value) => {
|
|
55
|
-
const id = 'filter--' + filter.property + '--' +
|
|
55
|
+
const id = 'filter--' + filter.property + '--' + value.value;
|
|
56
56
|
return (react_1.default.createElement(FilterOption, { key: id, role: "link", onClick: () => filter.toggleOption(value.value) },
|
|
57
57
|
react_1.default.createElement(icons_1.CheckboxIcon, { checked: filter.selectedOptions.has(value.value) }),
|
|
58
58
|
react_1.default.createElement(FilterOptionLabel, null, changeCasing(translate(value.value), filterValuesCasing)),
|
|
@@ -133,10 +133,6 @@ const StyledSelect = (0, styled_components_1.default)(Select_1.Select) `
|
|
|
133
133
|
margin: var(--filter-select-option-margin);
|
|
134
134
|
}
|
|
135
135
|
`;
|
|
136
|
-
// TODO: import from portal
|
|
137
|
-
function slug(str) {
|
|
138
|
-
return str.replace(/\s/g, '-').toLowerCase();
|
|
139
|
-
}
|
|
140
136
|
const DatePickerWrapper = styled_components_1.default.div `
|
|
141
137
|
color: var(--filter-date-picker-color);
|
|
142
138
|
display: flex;
|
|
@@ -32,6 +32,7 @@ const styled_components_1 = __importDefault(require("styled-components"));
|
|
|
32
32
|
const timeago_js_1 = require("timeago.js");
|
|
33
33
|
const useThemeConfig_1 = require("../../hooks/useThemeConfig");
|
|
34
34
|
const hooks_1 = require("../../mocks/hooks");
|
|
35
|
+
const constants_1 = require("../../types/portal/src/shared/constants");
|
|
35
36
|
const FORMATS = {
|
|
36
37
|
timeago: (date, locale) => (0, timeago_js_1.format)(date, locale),
|
|
37
38
|
iso: (date) => date.toISOString().split('T')[0],
|
|
@@ -47,7 +48,9 @@ function LastUpdated(props) {
|
|
|
47
48
|
}
|
|
48
49
|
const lastModified = props.lastModified;
|
|
49
50
|
const format = props.format || lastUpdatedBlock.format || 'timeago';
|
|
50
|
-
const locale = props.locale ||
|
|
51
|
+
const locale = props.locale ||
|
|
52
|
+
lastUpdatedBlock.locale ||
|
|
53
|
+
(currentLocale !== constants_1.DEFAULT_LOCALE_PLACEHOLDER ? currentLocale || 'en-US' : 'en-US');
|
|
51
54
|
const isoDate = lastModified.toISOString().split('T')[0];
|
|
52
55
|
const lastUpdatedString = FORMATS[format](lastModified, locale);
|
|
53
56
|
const translationKey = format === 'timeago' ? 'theme.page.lastUpdated.timeago' : 'theme.page.lastUpdated.on';
|
|
@@ -2,8 +2,9 @@ import React from 'react';
|
|
|
2
2
|
interface ScorecardBadgesProps {
|
|
3
3
|
metadata?: {
|
|
4
4
|
scorecardLevel?: string;
|
|
5
|
-
scorecardStatus?: string;
|
|
6
5
|
scoreCardSlug?: string;
|
|
6
|
+
scorecardLevelIdx?: number;
|
|
7
|
+
scorecardLevels?: Record<string, any>;
|
|
7
8
|
};
|
|
8
9
|
}
|
|
9
10
|
export declare function ScorecardBadges(props: ScorecardBadgesProps): React.JSX.Element | null;
|
|
@@ -9,11 +9,11 @@ const styled_components_1 = __importDefault(require("styled-components"));
|
|
|
9
9
|
const Tag_1 = require("../../components/Tag");
|
|
10
10
|
const Link_1 = require("../../mocks/Link");
|
|
11
11
|
const telemetry_1 = require("../../mocks/telemetry");
|
|
12
|
-
const
|
|
12
|
+
const Card_1 = require("../../components/Scorecard/Card");
|
|
13
13
|
function ScorecardBadges(props) {
|
|
14
|
-
var _a, _b, _c;
|
|
14
|
+
var _a, _b, _c, _d;
|
|
15
15
|
return ((((_a = props.metadata) === null || _a === void 0 ? void 0 : _a.scorecardLevel) && (react_1.default.createElement(ScorecardBadgesWrapper, { "data-component-name": "OpenApiDocs/ScorecardBadges" },
|
|
16
|
-
react_1.default.createElement(ComplianceTag, { level: props.metadata.scorecardLevel,
|
|
16
|
+
react_1.default.createElement(ComplianceTag, { level: props.metadata.scorecardLevel, slug: (_b = props.metadata) === null || _b === void 0 ? void 0 : _b.scoreCardSlug, color: (0, Card_1.getLevelColor)((_c = props.metadata) === null || _c === void 0 ? void 0 : _c.scorecardLevelIdx, Object.keys(((_d = props.metadata) === null || _d === void 0 ? void 0 : _d.scorecardLevels) || {}).length) })))) ||
|
|
17
17
|
null);
|
|
18
18
|
}
|
|
19
19
|
exports.ScorecardBadges = ScorecardBadges;
|
|
@@ -27,8 +27,32 @@ const ScorecardBadgesWrapper = styled_components_1.default.div `
|
|
|
27
27
|
right: var(--panel-gap-horizontal);
|
|
28
28
|
`;
|
|
29
29
|
function ComplianceTag(props) {
|
|
30
|
-
const { level,
|
|
30
|
+
const { level, slug, color } = props;
|
|
31
31
|
return (react_1.default.createElement(Link_1.Link, { to: slug },
|
|
32
|
-
react_1.default.createElement(Tag_1.Tag, {
|
|
32
|
+
react_1.default.createElement(Tag_1.Tag, { size: "large", onClick: () => telemetry_1.telemetry.send('scorecard_link_clicked', { action: 'click' }) },
|
|
33
|
+
react_1.default.createElement(StatusDot, { color: color }, level))));
|
|
33
34
|
}
|
|
35
|
+
const StatusDot = styled_components_1.default.div `
|
|
36
|
+
font-size: var(--font-size-base);
|
|
37
|
+
font-style: normal;
|
|
38
|
+
font-weight: 400;
|
|
39
|
+
line-height: 22px;
|
|
40
|
+
display: flex;
|
|
41
|
+
align-items: center;
|
|
42
|
+
&:not(:only-child) {
|
|
43
|
+
margin-left: 4px;
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
${({ color }) => color &&
|
|
47
|
+
`
|
|
48
|
+
&::before {
|
|
49
|
+
content: '';
|
|
50
|
+
display: inline-block;
|
|
51
|
+
width: 10px;
|
|
52
|
+
height: 10px;
|
|
53
|
+
border-radius: 50%;
|
|
54
|
+
background-color: ${color};
|
|
55
|
+
margin-right: 8px;
|
|
56
|
+
}`};
|
|
57
|
+
`;
|
|
34
58
|
//# sourceMappingURL=ScorecardBadges.js.map
|
|
@@ -1,2 +1,5 @@
|
|
|
1
|
-
export declare const ScorecardCard: import("styled-components").StyledComponent<"div", any, {
|
|
1
|
+
export declare const ScorecardCard: import("styled-components").StyledComponent<"div", any, {
|
|
2
|
+
'data-component-name': string;
|
|
3
|
+
}, "data-component-name">;
|
|
2
4
|
export declare const ScorecardCardTitle: import("styled-components").StyledComponent<"h3", any, {}, never>;
|
|
5
|
+
export declare function getLevelColor(idx: number, numberOfLevels: number): string;
|
|
@@ -3,11 +3,13 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
|
3
3
|
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
4
|
};
|
|
5
5
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
-
exports.ScorecardCardTitle = exports.ScorecardCard = void 0;
|
|
6
|
+
exports.getLevelColor = exports.ScorecardCardTitle = exports.ScorecardCard = void 0;
|
|
7
7
|
const styled_components_1 = __importDefault(require("styled-components"));
|
|
8
|
-
exports.ScorecardCard = styled_components_1.default.div
|
|
8
|
+
exports.ScorecardCard = styled_components_1.default.div.attrs({
|
|
9
|
+
'data-component-name': 'Scorecard/ScorecardCard',
|
|
10
|
+
}) `
|
|
9
11
|
color: var(--text-primary);
|
|
10
|
-
background-color: var(--
|
|
12
|
+
background-color: var(--bg-raised);
|
|
11
13
|
border-radius: 4px;
|
|
12
14
|
|
|
13
15
|
border: 1px solid var(--border-primary);
|
|
@@ -23,4 +25,13 @@ exports.ScorecardCardTitle = styled_components_1.default.h3 `
|
|
|
23
25
|
margin-bottom: 10px;
|
|
24
26
|
margin-top: 0;
|
|
25
27
|
`;
|
|
28
|
+
function getLevelColor(idx, numberOfLevels) {
|
|
29
|
+
if (numberOfLevels === 0) {
|
|
30
|
+
return 'hsl(0, 0%, 50%)';
|
|
31
|
+
}
|
|
32
|
+
const hue = ((idx + 1) / numberOfLevels) * 120;
|
|
33
|
+
const lum = 50 - ((idx + 1) / numberOfLevels) * 25;
|
|
34
|
+
return `hsl(${hue}, 100%, ${lum}%)`;
|
|
35
|
+
}
|
|
36
|
+
exports.getLevelColor = getLevelColor;
|
|
26
37
|
//# sourceMappingURL=Card.js.map
|
|
@@ -1,8 +1,11 @@
|
|
|
1
1
|
import * as React from 'react';
|
|
2
|
-
export
|
|
2
|
+
export interface GaugeProps {
|
|
3
3
|
chunks: {
|
|
4
4
|
share: number;
|
|
5
5
|
color: string;
|
|
6
|
+
title?: string;
|
|
6
7
|
}[];
|
|
7
|
-
|
|
8
|
+
className?: string;
|
|
9
|
+
}
|
|
10
|
+
export declare function Gauge({ chunks, className }: GaugeProps): React.JSX.Element;
|
|
8
11
|
export declare const GaugeValue: import("styled-components").StyledComponent<"span", any, {}, never>;
|
|
@@ -29,15 +29,18 @@ Object.defineProperty(exports, "__esModule", { value: true });
|
|
|
29
29
|
exports.GaugeValue = exports.Gauge = void 0;
|
|
30
30
|
const React = __importStar(require("react"));
|
|
31
31
|
const styled_components_1 = __importDefault(require("styled-components"));
|
|
32
|
-
function Gauge({ chunks, }) {
|
|
33
|
-
|
|
32
|
+
function Gauge({ chunks, className }) {
|
|
33
|
+
const title = chunks
|
|
34
|
+
.map((chunk) => chunk.title)
|
|
35
|
+
.filter(Boolean)
|
|
36
|
+
.join(', ');
|
|
37
|
+
return (React.createElement(GaugeWrapper, { "data-component-name": "Scorecard/StatusByLevelWidget", className: className, title: title }, chunks.map((chunk, i) => (React.createElement(GaugeChunk, { key: i, share: chunk.share, color: chunk.color })))));
|
|
34
38
|
}
|
|
35
39
|
exports.Gauge = Gauge;
|
|
36
40
|
exports.GaugeValue = styled_components_1.default.span `
|
|
37
41
|
font-size: var(--font-size-lg);
|
|
38
42
|
line-height: var(line-height-regular);
|
|
39
43
|
|
|
40
|
-
margin-right: 5px;
|
|
41
44
|
vertical-align: middle;
|
|
42
45
|
display: inline-block;
|
|
43
46
|
margin-right: 0;
|
|
@@ -32,8 +32,8 @@ const styled_components_1 = __importDefault(require("styled-components"));
|
|
|
32
32
|
const Gauge_1 = require("../../components/Scorecard/Gauge");
|
|
33
33
|
const Card_1 = require("../../components/Scorecard/Card");
|
|
34
34
|
function StatusByLevelWidget(props) {
|
|
35
|
-
const { levels, title } = props;
|
|
36
|
-
return (React.createElement(Card_1.ScorecardCard,
|
|
35
|
+
const { levels, title, className } = props;
|
|
36
|
+
return (React.createElement(Card_1.ScorecardCard, { "data-component-name": "Scorecard/StatusByLevelWidget", className: className },
|
|
37
37
|
React.createElement(Card_1.ScorecardCardTitle, null, title),
|
|
38
38
|
React.createElement(CardBody, null, levels.map((level) => {
|
|
39
39
|
const success = level.total - level.errors - level.warnings;
|
|
@@ -43,14 +43,17 @@ function StatusByLevelWidget(props) {
|
|
|
43
43
|
{
|
|
44
44
|
share: (success / level.total) * 100,
|
|
45
45
|
color: 'var(--scorecard-color-success)',
|
|
46
|
+
title: `${success} passed`,
|
|
46
47
|
},
|
|
47
48
|
{
|
|
48
49
|
share: (level.warnings / level.total) * 100,
|
|
49
50
|
color: 'var(--scorecard-color-warning)',
|
|
51
|
+
title: `${level.warnings} ${level.warnings === 1 ? 'warning' : 'warnings'}`,
|
|
50
52
|
},
|
|
51
53
|
{
|
|
52
54
|
share: (level.errors / level.total) * 100,
|
|
53
55
|
color: 'var(--scorecard-color-error)',
|
|
56
|
+
title: `${level.errors} ${level.errors === 1 ? 'error' : 'errors'}`,
|
|
54
57
|
},
|
|
55
58
|
] }),
|
|
56
59
|
React.createElement(Gauge_1.GaugeValue, null,
|
package/lib/config.d.ts
CHANGED
|
@@ -269,6 +269,12 @@ declare const catalogFilterSchema: {
|
|
|
269
269
|
readonly parentFilter: {
|
|
270
270
|
readonly type: "string";
|
|
271
271
|
};
|
|
272
|
+
readonly valuesMapping: {
|
|
273
|
+
readonly type: "object";
|
|
274
|
+
readonly additionalProperties: {
|
|
275
|
+
readonly type: "string";
|
|
276
|
+
};
|
|
277
|
+
};
|
|
272
278
|
readonly missingCategoryName: {
|
|
273
279
|
readonly type: "string";
|
|
274
280
|
};
|
|
@@ -315,6 +321,9 @@ declare const scorecardConfigSchema: {
|
|
|
315
321
|
readonly name: {
|
|
316
322
|
readonly type: "string";
|
|
317
323
|
};
|
|
324
|
+
readonly color: {
|
|
325
|
+
readonly type: "string";
|
|
326
|
+
};
|
|
318
327
|
readonly extends: {
|
|
319
328
|
readonly type: "array";
|
|
320
329
|
readonly items: {
|
|
@@ -390,6 +399,12 @@ declare const catalogSchema: {
|
|
|
390
399
|
readonly parentFilter: {
|
|
391
400
|
readonly type: "string";
|
|
392
401
|
};
|
|
402
|
+
readonly valuesMapping: {
|
|
403
|
+
readonly type: "object";
|
|
404
|
+
readonly additionalProperties: {
|
|
405
|
+
readonly type: "string";
|
|
406
|
+
};
|
|
407
|
+
};
|
|
393
408
|
readonly missingCategoryName: {
|
|
394
409
|
readonly type: "string";
|
|
395
410
|
};
|
|
@@ -1690,6 +1705,12 @@ export declare const themeConfigSchema: {
|
|
|
1690
1705
|
readonly parentFilter: {
|
|
1691
1706
|
readonly type: "string";
|
|
1692
1707
|
};
|
|
1708
|
+
readonly valuesMapping: {
|
|
1709
|
+
readonly type: "object";
|
|
1710
|
+
readonly additionalProperties: {
|
|
1711
|
+
readonly type: "string";
|
|
1712
|
+
};
|
|
1713
|
+
};
|
|
1693
1714
|
readonly missingCategoryName: {
|
|
1694
1715
|
readonly type: "string";
|
|
1695
1716
|
};
|
|
@@ -1882,6 +1903,9 @@ export declare const themeConfigSchema: {
|
|
|
1882
1903
|
readonly name: {
|
|
1883
1904
|
readonly type: "string";
|
|
1884
1905
|
};
|
|
1906
|
+
readonly color: {
|
|
1907
|
+
readonly type: "string";
|
|
1908
|
+
};
|
|
1885
1909
|
readonly extends: {
|
|
1886
1910
|
readonly type: "array";
|
|
1887
1911
|
readonly items: {
|
|
@@ -2414,9 +2438,4 @@ export type GoogleAnalyticsConfig = FromSchema<typeof googleAnalyticsConfigSchem
|
|
|
2414
2438
|
export type CatalogConfig = FromSchema<typeof catalogSchema>;
|
|
2415
2439
|
export type CatalogFilterConfig = FromSchema<typeof catalogFilterSchema>;
|
|
2416
2440
|
export type ScorecardConfig = FromSchema<typeof scorecardConfigSchema>;
|
|
2417
|
-
export declare enum ScorecardStatus {
|
|
2418
|
-
BelowMinimum = "Below minimum",
|
|
2419
|
-
Highest = "Highest",
|
|
2420
|
-
Minimum = "Minimum"
|
|
2421
|
-
}
|
|
2422
2441
|
export {};
|
package/lib/config.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.
|
|
3
|
+
exports.productThemeOverrideSchema = exports.themeConfigSchema = void 0;
|
|
4
4
|
const logoConfigSchema = {
|
|
5
5
|
type: 'object',
|
|
6
6
|
properties: {
|
|
@@ -259,6 +259,7 @@ const catalogFilterSchema = {
|
|
|
259
259
|
titleTranslationKey: { type: 'string' },
|
|
260
260
|
property: { type: 'string' },
|
|
261
261
|
parentFilter: { type: 'string' },
|
|
262
|
+
valuesMapping: { type: 'object', additionalProperties: { type: 'string' } },
|
|
262
263
|
missingCategoryName: { type: 'string' },
|
|
263
264
|
missingCategoryNameTranslationKey: { type: 'string' },
|
|
264
265
|
options: { type: 'array', items: { type: 'string' } },
|
|
@@ -285,6 +286,7 @@ const scorecardConfigSchema = {
|
|
|
285
286
|
required: ['name'],
|
|
286
287
|
properties: {
|
|
287
288
|
name: { type: 'string' },
|
|
289
|
+
color: { type: 'string' },
|
|
288
290
|
extends: { type: 'array', items: { type: 'string' } },
|
|
289
291
|
rules: {
|
|
290
292
|
type: 'object',
|
|
@@ -609,10 +611,4 @@ exports.productThemeOverrideSchema = {
|
|
|
609
611
|
additionalProperties: true,
|
|
610
612
|
default: {},
|
|
611
613
|
};
|
|
612
|
-
var ScorecardStatus;
|
|
613
|
-
(function (ScorecardStatus) {
|
|
614
|
-
ScorecardStatus["BelowMinimum"] = "Below minimum";
|
|
615
|
-
ScorecardStatus["Highest"] = "Highest";
|
|
616
|
-
ScorecardStatus["Minimum"] = "Minimum";
|
|
617
|
-
})(ScorecardStatus || (exports.ScorecardStatus = ScorecardStatus = {}));
|
|
618
614
|
//# sourceMappingURL=config.js.map
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
export declare const DEFAULT_THEME_NAME = "@redocly/theme";
|
|
2
2
|
export declare const USER_THEME_ALIAS = "@theme";
|
|
3
3
|
export declare const REDOCLY_TEAMS_RBAC = "redocly::teams-rbac";
|
|
4
|
+
export declare const DEFAULT_LOCALE_PLACEHOLDER = "default_locale";
|
|
4
5
|
export type REQUIRED_OIDC_SCOPES = string[];
|
|
5
6
|
export type DEFAULT_COOKIE_EXPIRATION = number;
|
|
6
7
|
export declare enum FEEDBACK_TYPES {
|
|
@@ -1,9 +1,10 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.FsErrors = exports.ExternalRoutes = exports.DEV_LOGIN_SLUG = exports.FEEDBACK_TYPES = exports.REDOCLY_TEAMS_RBAC = exports.USER_THEME_ALIAS = exports.DEFAULT_THEME_NAME = void 0;
|
|
3
|
+
exports.FsErrors = exports.ExternalRoutes = exports.DEV_LOGIN_SLUG = exports.FEEDBACK_TYPES = exports.DEFAULT_LOCALE_PLACEHOLDER = exports.REDOCLY_TEAMS_RBAC = exports.USER_THEME_ALIAS = exports.DEFAULT_THEME_NAME = void 0;
|
|
4
4
|
exports.DEFAULT_THEME_NAME = '@redocly/theme';
|
|
5
5
|
exports.USER_THEME_ALIAS = '@theme';
|
|
6
6
|
exports.REDOCLY_TEAMS_RBAC = 'redocly::teams-rbac';
|
|
7
|
+
exports.DEFAULT_LOCALE_PLACEHOLDER = 'default_locale';
|
|
7
8
|
var FEEDBACK_TYPES;
|
|
8
9
|
(function (FEEDBACK_TYPES) {
|
|
9
10
|
FEEDBACK_TYPES["RATING"] = "rating";
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { CatalogFilterConfig
|
|
1
|
+
import { CatalogFilterConfig } from '../../../../../index.js';
|
|
2
2
|
export type FilteredCatalog = {
|
|
3
3
|
groups: {
|
|
4
4
|
title: string;
|
|
@@ -31,8 +31,12 @@ export type CatalogItem = {
|
|
|
31
31
|
description?: string;
|
|
32
32
|
image?: string;
|
|
33
33
|
docsLink?: string;
|
|
34
|
-
scorecardStatus?: ScorecardStatus;
|
|
35
34
|
scorecardLevel?: string;
|
|
35
|
+
scorecardLevelIdx?: number;
|
|
36
|
+
scorecardLevels?: Record<string, {
|
|
37
|
+
uniqueErrors: number;
|
|
38
|
+
uniqueWarnings: number;
|
|
39
|
+
}>;
|
|
36
40
|
scoreCardSlug?: string;
|
|
37
41
|
tags?: unknown[];
|
|
38
42
|
[k: string]: unknown;
|
package/package.json
CHANGED
|
@@ -8,9 +8,10 @@ import { Tag } from '@theme/components/Tag';
|
|
|
8
8
|
import { PointingArrowIcon } from '@theme/icons';
|
|
9
9
|
import { telemetry } from '@portal/telemetry';
|
|
10
10
|
import { slug, capitalize } from '@theme/utils';
|
|
11
|
+
import { getLevelColor } from '@theme/components/Scorecard/Card';
|
|
11
12
|
|
|
12
13
|
export function CatalogCard({ item }: { item: CatalogItem }): JSX.Element {
|
|
13
|
-
const hasTags = item.
|
|
14
|
+
const hasTags = item.scorecardLevel || item.tags?.length;
|
|
14
15
|
return (
|
|
15
16
|
<Link key={item.docsLink || item.link} to={item.docsLink || item.link}>
|
|
16
17
|
<StyledCard onClick={() => telemetry.send('catalog_item_clicked', {})}>
|
|
@@ -29,13 +30,15 @@ export function CatalogCard({ item }: { item: CatalogItem }): JSX.Element {
|
|
|
29
30
|
<Highlight>{capitalize(tag)}</Highlight>
|
|
30
31
|
</CardTag>
|
|
31
32
|
))}
|
|
32
|
-
{(item.scorecardLevel &&
|
|
33
|
-
<
|
|
34
|
-
color={
|
|
35
|
-
|
|
33
|
+
{(item.scorecardLevel && (
|
|
34
|
+
<StatusDot
|
|
35
|
+
color={getLevelColor(
|
|
36
|
+
item.scorecardLevelIdx || -1,
|
|
37
|
+
Object.keys(item.scorecardLevels || {}).length,
|
|
38
|
+
)}
|
|
36
39
|
>
|
|
37
40
|
<Highlight>{item.scorecardLevel}</Highlight>
|
|
38
|
-
</
|
|
41
|
+
</StatusDot>
|
|
39
42
|
)) ||
|
|
40
43
|
null}
|
|
41
44
|
</CardTags>
|
|
@@ -50,19 +53,6 @@ export function CatalogCard({ item }: { item: CatalogItem }): JSX.Element {
|
|
|
50
53
|
);
|
|
51
54
|
}
|
|
52
55
|
|
|
53
|
-
export function statusToColor(status: string) {
|
|
54
|
-
switch (status) {
|
|
55
|
-
case 'Below minimum':
|
|
56
|
-
return 'error';
|
|
57
|
-
case 'Minimum':
|
|
58
|
-
return 'warning';
|
|
59
|
-
case 'Highest':
|
|
60
|
-
return 'success';
|
|
61
|
-
default:
|
|
62
|
-
return '';
|
|
63
|
-
}
|
|
64
|
-
}
|
|
65
|
-
|
|
66
56
|
const SelectButton = styled.div`
|
|
67
57
|
border: 1px solid var(--catalog-card-button-border-color);
|
|
68
58
|
border-radius: 100%;
|
|
@@ -154,3 +144,28 @@ const CardTag = styled(Tag)`
|
|
|
154
144
|
text-transform: inherit;
|
|
155
145
|
margin: 0;
|
|
156
146
|
`;
|
|
147
|
+
|
|
148
|
+
const StatusDot = styled.div<{ color: string }>`
|
|
149
|
+
font-size: var(--font-size-base);
|
|
150
|
+
font-style: normal;
|
|
151
|
+
font-weight: 400;
|
|
152
|
+
line-height: 22px;
|
|
153
|
+
display: flex;
|
|
154
|
+
align-items: center;
|
|
155
|
+
&:not(:only-child) {
|
|
156
|
+
margin-left: 4px;
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
${({ color }) =>
|
|
160
|
+
color &&
|
|
161
|
+
`
|
|
162
|
+
&::before {
|
|
163
|
+
content: '';
|
|
164
|
+
display: inline-block;
|
|
165
|
+
width: 10px;
|
|
166
|
+
height: 10px;
|
|
167
|
+
border-radius: 50%;
|
|
168
|
+
background-color: ${color};
|
|
169
|
+
margin-right: 8px;
|
|
170
|
+
}`};
|
|
171
|
+
`;
|
|
@@ -158,10 +158,9 @@ export function useCatalog(items: ResolvedNavItem[], config: CatalogConfig): Fil
|
|
|
158
158
|
};
|
|
159
159
|
});
|
|
160
160
|
|
|
161
|
-
const groups =
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
: [{ title: 'APIs', items: filteredItems }];
|
|
161
|
+
const groups = config.groupByFirstFilter
|
|
162
|
+
? groupByFirstFilter(resolvedFilters, filteredItems)
|
|
163
|
+
: [{ title: 'APIs', items: filteredItems }];
|
|
165
164
|
|
|
166
165
|
return {
|
|
167
166
|
groups,
|
|
@@ -283,7 +282,7 @@ function collectFilterOptions(
|
|
|
283
282
|
: [item.metadata?.[filter.property]];
|
|
284
283
|
|
|
285
284
|
for (const value of values) {
|
|
286
|
-
const str = String(value);
|
|
285
|
+
const str = mapFilterValues(String(value), filter.valuesMapping) as string;
|
|
287
286
|
if (staticOptions) {
|
|
288
287
|
if (str in staticOptions) {
|
|
289
288
|
usedOptions[str] = usedOptions[str] + 1;
|
|
@@ -299,15 +298,19 @@ function collectFilterOptions(
|
|
|
299
298
|
}
|
|
300
299
|
}
|
|
301
300
|
|
|
302
|
-
const options = Object.entries(usedOptions)
|
|
303
|
-
|
|
304
|
-
|
|
301
|
+
const options = Object.entries(usedOptions).map(([value, count]) => ({ value, count }));
|
|
302
|
+
|
|
303
|
+
if (!staticOptions) {
|
|
304
|
+
options.sort((a, b) => b.value.localeCompare(a.value));
|
|
305
|
+
}
|
|
306
|
+
|
|
305
307
|
if (othersCount) {
|
|
306
308
|
options.push({
|
|
307
309
|
value: filter.missingCategoryNameTranslationKey || filter.missingCategoryName || 'Others',
|
|
308
310
|
count: othersCount,
|
|
309
311
|
});
|
|
310
312
|
}
|
|
313
|
+
|
|
311
314
|
return { ...filter, options };
|
|
312
315
|
});
|
|
313
316
|
}
|
|
@@ -336,7 +339,11 @@ function filterItems(
|
|
|
336
339
|
return true;
|
|
337
340
|
}
|
|
338
341
|
|
|
339
|
-
const itemValue =
|
|
342
|
+
const itemValue = mapFilterValues(
|
|
343
|
+
item?.[filter.property] || filter.missingCategoryName || 'Others',
|
|
344
|
+
filter.valuesMapping,
|
|
345
|
+
);
|
|
346
|
+
|
|
340
347
|
if (Array.isArray(itemValue)) {
|
|
341
348
|
return itemValue.some((value) => (filter.selectedOptions as Set<any>).has(value));
|
|
342
349
|
}
|
|
@@ -357,3 +364,10 @@ function filterItems(
|
|
|
357
364
|
return filteredByFilters;
|
|
358
365
|
}
|
|
359
366
|
}
|
|
367
|
+
|
|
368
|
+
function mapFilterValues(value: unknown | unknown[], mapping?: Record<string, string>) {
|
|
369
|
+
if (!mapping) return value;
|
|
370
|
+
return Array.isArray(value)
|
|
371
|
+
? value.map((v) => mapping[String(v)] || v)
|
|
372
|
+
: mapping[String(value)] || value;
|
|
373
|
+
}
|
|
@@ -109,7 +109,7 @@ export function Filter({
|
|
|
109
109
|
</>
|
|
110
110
|
) : (
|
|
111
111
|
filter.filteredOptions.map((value: any) => {
|
|
112
|
-
const id = 'filter--' + filter.property + '--' +
|
|
112
|
+
const id = 'filter--' + filter.property + '--' + value.value;
|
|
113
113
|
return (
|
|
114
114
|
<FilterOption key={id} role="link" onClick={() => filter.toggleOption(value.value)}>
|
|
115
115
|
<CheckboxIcon checked={filter.selectedOptions.has(value.value)} />
|
|
@@ -210,11 +210,6 @@ const StyledSelect = styled(Select)`
|
|
|
210
210
|
}
|
|
211
211
|
`;
|
|
212
212
|
|
|
213
|
-
// TODO: import from portal
|
|
214
|
-
function slug(str: string): string {
|
|
215
|
-
return str.replace(/\s/g, '-').toLowerCase();
|
|
216
|
-
}
|
|
217
|
-
|
|
218
213
|
const DatePickerWrapper = styled.div`
|
|
219
214
|
color: var(--filter-date-picker-color);
|
|
220
215
|
display: flex;
|
|
@@ -5,6 +5,8 @@ import { format } from 'timeago.js';
|
|
|
5
5
|
import { useThemeConfig } from '@theme/hooks/useThemeConfig';
|
|
6
6
|
import { useI18nConfig, useTranslate } from '@portal/hooks';
|
|
7
7
|
|
|
8
|
+
import { DEFAULT_LOCALE_PLACEHOLDER } from '../../types/portal/src/shared/constants';
|
|
9
|
+
|
|
8
10
|
const FORMATS = {
|
|
9
11
|
timeago: (date: Date, locale: string) => format(date, locale),
|
|
10
12
|
iso: (date: Date) => date.toISOString().split('T')[0],
|
|
@@ -32,7 +34,10 @@ export function LastUpdated(props: LastUpdatedProps): JSX.Element | null {
|
|
|
32
34
|
|
|
33
35
|
const lastModified = props.lastModified;
|
|
34
36
|
const format = props.format || lastUpdatedBlock.format || 'timeago';
|
|
35
|
-
const locale =
|
|
37
|
+
const locale =
|
|
38
|
+
props.locale ||
|
|
39
|
+
lastUpdatedBlock.locale ||
|
|
40
|
+
(currentLocale !== DEFAULT_LOCALE_PLACEHOLDER ? currentLocale || 'en-US' : 'en-US');
|
|
36
41
|
|
|
37
42
|
const isoDate = lastModified.toISOString().split('T')[0];
|
|
38
43
|
|
|
@@ -4,13 +4,14 @@ import styled from 'styled-components';
|
|
|
4
4
|
import { Tag } from '@theme/components/Tag';
|
|
5
5
|
import { Link } from '@portal/Link';
|
|
6
6
|
import { telemetry } from '@portal/telemetry';
|
|
7
|
-
import {
|
|
7
|
+
import { getLevelColor } from '@theme/components/Scorecard/Card';
|
|
8
8
|
|
|
9
9
|
interface ScorecardBadgesProps {
|
|
10
10
|
metadata?: {
|
|
11
11
|
scorecardLevel?: string;
|
|
12
|
-
scorecardStatus?: string;
|
|
13
12
|
scoreCardSlug?: string;
|
|
13
|
+
scorecardLevelIdx?: number;
|
|
14
|
+
scorecardLevels?: Record<string, any>;
|
|
14
15
|
};
|
|
15
16
|
}
|
|
16
17
|
|
|
@@ -20,8 +21,11 @@ export function ScorecardBadges(props: ScorecardBadgesProps) {
|
|
|
20
21
|
<ScorecardBadgesWrapper data-component-name="OpenApiDocs/ScorecardBadges">
|
|
21
22
|
<ComplianceTag
|
|
22
23
|
level={props.metadata.scorecardLevel as string}
|
|
23
|
-
status={props.metadata?.scorecardStatus as string}
|
|
24
24
|
slug={props.metadata?.scoreCardSlug as string}
|
|
25
|
+
color={getLevelColor(
|
|
26
|
+
props.metadata?.scorecardLevelIdx as number,
|
|
27
|
+
Object.keys(props.metadata?.scorecardLevels || {}).length,
|
|
28
|
+
)}
|
|
25
29
|
/>
|
|
26
30
|
</ScorecardBadgesWrapper>
|
|
27
31
|
)) ||
|
|
@@ -39,17 +43,41 @@ const ScorecardBadgesWrapper = styled.div`
|
|
|
39
43
|
right: var(--panel-gap-horizontal);
|
|
40
44
|
`;
|
|
41
45
|
|
|
42
|
-
function ComplianceTag(props: { level: string;
|
|
43
|
-
const { level,
|
|
46
|
+
function ComplianceTag(props: { level: string; slug: string; color: string }) {
|
|
47
|
+
const { level, slug, color } = props;
|
|
44
48
|
return (
|
|
45
49
|
<Link to={slug}>
|
|
46
50
|
<Tag
|
|
47
|
-
color={statusToColor(status)}
|
|
48
51
|
size="large"
|
|
49
52
|
onClick={() => telemetry.send('scorecard_link_clicked', { action: 'click' })}
|
|
50
53
|
>
|
|
51
|
-
{level}
|
|
54
|
+
<StatusDot color={color}>{level}</StatusDot>
|
|
52
55
|
</Tag>
|
|
53
56
|
</Link>
|
|
54
57
|
);
|
|
55
58
|
}
|
|
59
|
+
|
|
60
|
+
const StatusDot = styled.div<{ color: string }>`
|
|
61
|
+
font-size: var(--font-size-base);
|
|
62
|
+
font-style: normal;
|
|
63
|
+
font-weight: 400;
|
|
64
|
+
line-height: 22px;
|
|
65
|
+
display: flex;
|
|
66
|
+
align-items: center;
|
|
67
|
+
&:not(:only-child) {
|
|
68
|
+
margin-left: 4px;
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
${({ color }) =>
|
|
72
|
+
color &&
|
|
73
|
+
`
|
|
74
|
+
&::before {
|
|
75
|
+
content: '';
|
|
76
|
+
display: inline-block;
|
|
77
|
+
width: 10px;
|
|
78
|
+
height: 10px;
|
|
79
|
+
border-radius: 50%;
|
|
80
|
+
background-color: ${color};
|
|
81
|
+
margin-right: 8px;
|
|
82
|
+
}`};
|
|
83
|
+
`;
|
|
@@ -1,8 +1,10 @@
|
|
|
1
1
|
import styled from 'styled-components';
|
|
2
2
|
|
|
3
|
-
export const ScorecardCard = styled.div
|
|
3
|
+
export const ScorecardCard = styled.div.attrs({
|
|
4
|
+
'data-component-name': 'Scorecard/ScorecardCard',
|
|
5
|
+
})`
|
|
4
6
|
color: var(--text-primary);
|
|
5
|
-
background-color: var(--
|
|
7
|
+
background-color: var(--bg-raised);
|
|
6
8
|
border-radius: 4px;
|
|
7
9
|
|
|
8
10
|
border: 1px solid var(--border-primary);
|
|
@@ -19,3 +21,13 @@ export const ScorecardCardTitle = styled.h3`
|
|
|
19
21
|
margin-bottom: 10px;
|
|
20
22
|
margin-top: 0;
|
|
21
23
|
`;
|
|
24
|
+
|
|
25
|
+
export function getLevelColor(idx: number, numberOfLevels: number) {
|
|
26
|
+
if (numberOfLevels === 0) {
|
|
27
|
+
return 'hsl(0, 0%, 50%)';
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
const hue = ((idx + 1) / numberOfLevels) * 120;
|
|
31
|
+
const lum = 50 - ((idx + 1) / numberOfLevels) * 25;
|
|
32
|
+
return `hsl(${hue}, 100%, ${lum}%)`;
|
|
33
|
+
}
|
|
@@ -1,18 +1,27 @@
|
|
|
1
1
|
import * as React from 'react';
|
|
2
2
|
import styled from 'styled-components';
|
|
3
|
-
|
|
4
|
-
export function Gauge({
|
|
5
|
-
chunks,
|
|
6
|
-
}: {
|
|
3
|
+
export interface GaugeProps {
|
|
7
4
|
chunks: {
|
|
8
5
|
share: number;
|
|
9
6
|
color: string;
|
|
7
|
+
title?: string;
|
|
10
8
|
}[];
|
|
11
|
-
|
|
9
|
+
className?: string;
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
export function Gauge({ chunks, className }: GaugeProps) {
|
|
13
|
+
const title = chunks
|
|
14
|
+
.map((chunk) => chunk.title)
|
|
15
|
+
.filter(Boolean)
|
|
16
|
+
.join(', ');
|
|
12
17
|
return (
|
|
13
|
-
<GaugeWrapper
|
|
18
|
+
<GaugeWrapper
|
|
19
|
+
data-component-name="Scorecard/StatusByLevelWidget"
|
|
20
|
+
className={className}
|
|
21
|
+
title={title}
|
|
22
|
+
>
|
|
14
23
|
{chunks.map((chunk, i) => (
|
|
15
|
-
<GaugeChunk key={i} {
|
|
24
|
+
<GaugeChunk key={i} share={chunk.share} color={chunk.color} />
|
|
16
25
|
))}
|
|
17
26
|
</GaugeWrapper>
|
|
18
27
|
);
|
|
@@ -22,7 +31,6 @@ export const GaugeValue = styled.span`
|
|
|
22
31
|
font-size: var(--font-size-lg);
|
|
23
32
|
line-height: var(line-height-regular);
|
|
24
33
|
|
|
25
|
-
margin-right: 5px;
|
|
26
34
|
vertical-align: middle;
|
|
27
35
|
display: inline-block;
|
|
28
36
|
margin-right: 0;
|
|
@@ -7,12 +7,13 @@ import { ScorecardCard, ScorecardCardTitle } from '@theme/components/Scorecard/C
|
|
|
7
7
|
export interface StatusByLevelWidgetProps {
|
|
8
8
|
title: string;
|
|
9
9
|
levels: { name: string; errors: number; warnings: number; total: number }[];
|
|
10
|
+
className?: string;
|
|
10
11
|
}
|
|
11
12
|
|
|
12
13
|
export function StatusByLevelWidget(props: StatusByLevelWidgetProps) {
|
|
13
|
-
const { levels, title } = props;
|
|
14
|
+
const { levels, title, className } = props;
|
|
14
15
|
return (
|
|
15
|
-
<ScorecardCard>
|
|
16
|
+
<ScorecardCard data-component-name="Scorecard/StatusByLevelWidget" className={className}>
|
|
16
17
|
<ScorecardCardTitle>{title}</ScorecardCardTitle>
|
|
17
18
|
<CardBody>
|
|
18
19
|
{levels.map((level) => {
|
|
@@ -25,14 +26,17 @@ export function StatusByLevelWidget(props: StatusByLevelWidgetProps) {
|
|
|
25
26
|
{
|
|
26
27
|
share: (success / level.total) * 100,
|
|
27
28
|
color: 'var(--scorecard-color-success)',
|
|
29
|
+
title: `${success} passed`,
|
|
28
30
|
},
|
|
29
31
|
{
|
|
30
32
|
share: (level.warnings / level.total) * 100,
|
|
31
33
|
color: 'var(--scorecard-color-warning)',
|
|
34
|
+
title: `${level.warnings} ${level.warnings === 1 ? 'warning' : 'warnings'}`,
|
|
32
35
|
},
|
|
33
36
|
{
|
|
34
37
|
share: (level.errors / level.total) * 100,
|
|
35
38
|
color: 'var(--scorecard-color-error)',
|
|
39
|
+
title: `${level.errors} ${level.errors === 1 ? 'error' : 'errors'}`,
|
|
36
40
|
},
|
|
37
41
|
]}
|
|
38
42
|
/>
|
package/src/config.ts
CHANGED
|
@@ -297,6 +297,7 @@ const catalogFilterSchema = {
|
|
|
297
297
|
titleTranslationKey: { type: 'string' },
|
|
298
298
|
property: { type: 'string' },
|
|
299
299
|
parentFilter: { type: 'string' },
|
|
300
|
+
valuesMapping: { type: 'object', additionalProperties: { type: 'string' } },
|
|
300
301
|
missingCategoryName: { type: 'string' },
|
|
301
302
|
missingCategoryNameTranslationKey: { type: 'string' },
|
|
302
303
|
options: { type: 'array', items: { type: 'string' } },
|
|
@@ -324,6 +325,7 @@ const scorecardConfigSchema = {
|
|
|
324
325
|
required: ['name'],
|
|
325
326
|
properties: {
|
|
326
327
|
name: { type: 'string' },
|
|
328
|
+
color: { type: 'string' },
|
|
327
329
|
extends: { type: 'array', items: { type: 'string' } },
|
|
328
330
|
rules: {
|
|
329
331
|
type: 'object',
|
|
@@ -748,9 +750,3 @@ export type GoogleAnalyticsConfig = FromSchema<typeof googleAnalyticsConfigSchem
|
|
|
748
750
|
export type CatalogConfig = FromSchema<typeof catalogSchema>;
|
|
749
751
|
export type CatalogFilterConfig = FromSchema<typeof catalogFilterSchema>;
|
|
750
752
|
export type ScorecardConfig = FromSchema<typeof scorecardConfigSchema>;
|
|
751
|
-
|
|
752
|
-
export enum ScorecardStatus {
|
|
753
|
-
BelowMinimum = 'Below minimum',
|
|
754
|
-
Highest = 'Highest',
|
|
755
|
-
Minimum = 'Minimum',
|
|
756
|
-
}
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
export const DEFAULT_THEME_NAME = '@redocly/theme';
|
|
2
2
|
export const USER_THEME_ALIAS = '@theme';
|
|
3
3
|
export const REDOCLY_TEAMS_RBAC = 'redocly::teams-rbac';
|
|
4
|
+
export const DEFAULT_LOCALE_PLACEHOLDER = 'default_locale'
|
|
4
5
|
export type REQUIRED_OIDC_SCOPES = string[];
|
|
5
6
|
export type DEFAULT_COOKIE_EXPIRATION = number;
|
|
6
7
|
export enum FEEDBACK_TYPES {
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { CatalogFilterConfig
|
|
1
|
+
import { CatalogFilterConfig } from '@theme';
|
|
2
2
|
|
|
3
3
|
export type FilteredCatalog = {
|
|
4
4
|
groups: { title: string; items: CatalogItem[] }[];
|
|
@@ -34,8 +34,9 @@ export type CatalogItem = {
|
|
|
34
34
|
image?: string;
|
|
35
35
|
docsLink?: string;
|
|
36
36
|
|
|
37
|
-
scorecardStatus?: ScorecardStatus;
|
|
38
37
|
scorecardLevel?: string;
|
|
38
|
+
scorecardLevelIdx?: number;
|
|
39
|
+
scorecardLevels?: Record<string, { uniqueErrors: number; uniqueWarnings: number }>;
|
|
39
40
|
scoreCardSlug?: string;
|
|
40
41
|
tags?: unknown[];
|
|
41
42
|
|