@patternfly/quickstarts 2.2.1 → 2.2.4
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/README.md +20 -20
- package/dist/ConsoleShared/src/components/markdown-extensions/code-extension.d.ts +7 -0
- package/dist/ConsoleShared/src/components/markdown-extensions/index.d.ts +1 -0
- package/dist/HelpTopicDrawer.d.ts +8 -2
- package/dist/QuickStartDrawer.d.ts +21 -2
- package/dist/index.d.ts +1 -0
- package/dist/index.es.js +127 -21
- package/dist/index.es.js.map +1 -1
- package/dist/index.js +126 -19
- package/dist/index.js.map +1 -1
- package/dist/patternfly-docs/quick-starts/design-guidelines/design-guidelines.md +105 -0
- package/dist/patternfly-docs/quick-starts/design-guidelines/img/card-elements copy.png +0 -0
- package/dist/patternfly-docs/quick-starts/design-guidelines/img/card-elements.png +0 -0
- package/dist/patternfly-docs/quick-starts/design-guidelines/img/catalog-elements.png +0 -0
- package/dist/patternfly-docs/quick-starts/design-guidelines/img/check-your-work.png +0 -0
- package/dist/patternfly-docs/quick-starts/design-guidelines/img/introduction-screen.png +0 -0
- package/dist/patternfly-docs/quick-starts/design-guidelines/img/mixed-catalog.png +0 -0
- package/dist/patternfly-docs/quick-starts/design-guidelines/img/prerequisites.png +0 -0
- package/dist/patternfly-docs/quick-starts/design-guidelines/img/qs-context.png +0 -0
- package/dist/patternfly-docs/quick-starts/design-guidelines/img/side-panel-elements.png +0 -0
- package/dist/patternfly-docs/quick-starts/design-guidelines/img/side-panel-resized.png +0 -0
- package/dist/patternfly-docs/quick-starts/design-guidelines/img/side-panel.png +0 -0
- package/dist/patternfly-docs/quick-starts/design-guidelines/img/task-no.png +0 -0
- package/dist/patternfly-docs/quick-starts/design-guidelines/img/task-yes.png +0 -0
- package/dist/patternfly-docs/quick-starts/design-guidelines/img/task.png +0 -0
- package/dist/patternfly-docs/quick-starts/examples/Basic.jsx +73 -0
- package/dist/patternfly-docs/quick-starts/examples/HelpTopic.jsx +53 -0
- package/dist/patternfly-docs/quick-starts/examples/about.md +77 -0
- package/dist/patternfly-docs/quick-starts/examples/basic.md +27 -0
- package/dist/patternfly-docs/quick-starts/examples/example-data/example-help-topics.js +173 -0
- package/dist/patternfly-docs/quick-starts/examples/example-data/example-quickstarts.js +215 -0
- package/dist/patternfly-docs/quick-starts/examples/example-data/index.js +15 -0
- package/dist/patternfly-docs/quick-starts/examples/help-topics.md +25 -0
- package/dist/patternfly-docs/quick-starts/examples/img/catalog.png +0 -0
- package/dist/patternfly-docs/quick-starts/examples/img/help-topic.png +0 -0
- package/dist/patternfly-docs/quick-starts/examples/img/side-panel.png +0 -0
- package/dist/patternfly-nested.css +11 -1
- package/dist/quickstarts-base.css +49 -11
- package/dist/quickstarts-full.es.js +451 -305
- package/dist/quickstarts-full.es.js.map +1 -1
- package/dist/quickstarts-standalone.css +5 -15
- package/dist/quickstarts-standalone.min.css +1 -1
- package/dist/quickstarts.css +49 -11
- package/dist/quickstarts.min.css +1 -1
- package/dist/utils/asciidoc-procedure-parser.d.ts +12 -0
- package/dist/utils/help-topic-types.d.ts +7 -1
- package/package.json +11 -4
- package/src/ConsoleInternal/components/_icon-and-text.scss +14 -0
- package/src/ConsoleInternal/components/_markdown-view.scss +19 -0
- package/src/ConsoleInternal/components/catalog/_catalog.scss +390 -0
- package/src/ConsoleInternal/components/markdown-view.tsx +305 -0
- package/src/ConsoleInternal/components/utils/_status-box.scss +58 -0
- package/src/ConsoleInternal/components/utils/camel-case-wrap.tsx +33 -0
- package/src/ConsoleInternal/components/utils/index.tsx +3 -0
- package/src/ConsoleInternal/components/utils/router.ts +47 -0
- package/src/ConsoleInternal/components/utils/status-box.tsx +94 -0
- package/src/ConsoleInternal/module/k8s/types.ts +53 -0
- package/src/ConsoleShared/index.ts +1 -0
- package/src/ConsoleShared/src/components/index.ts +7 -0
- package/src/ConsoleShared/src/components/layout/PageLayout.scss +29 -0
- package/src/ConsoleShared/src/components/markdown-extensions/MarkdownCopyClipboard.tsx +93 -0
- package/src/ConsoleShared/src/components/markdown-extensions/__tests__/MarkdownCopyClipboard.spec.tsx +25 -0
- package/src/ConsoleShared/src/components/markdown-extensions/__tests__/test-data.ts +5 -0
- package/src/ConsoleShared/src/components/markdown-extensions/admonition-extension.tsx +66 -0
- package/src/ConsoleShared/src/components/markdown-extensions/code-extension.tsx +25 -0
- package/src/ConsoleShared/src/components/markdown-extensions/const.ts +3 -0
- package/src/ConsoleShared/src/components/markdown-extensions/index.ts +5 -0
- package/src/ConsoleShared/src/components/markdown-extensions/inline-clipboard-extension.tsx +45 -0
- package/src/ConsoleShared/src/components/markdown-extensions/multiline-clipboard-extension.tsx +50 -0
- package/src/ConsoleShared/src/components/markdown-extensions/showdown-extension.scss +52 -0
- package/src/ConsoleShared/src/components/markdown-extensions/utils.ts +3 -0
- package/src/ConsoleShared/src/components/markdown-highlight-extension/MarkdownHighlightExtension.tsx +64 -0
- package/src/ConsoleShared/src/components/markdown-highlight-extension/highlight-consts.ts +9 -0
- package/src/ConsoleShared/src/components/markdown-highlight-extension/index.ts +1 -0
- package/src/ConsoleShared/src/components/modal/Modal.scss +3 -0
- package/src/ConsoleShared/src/components/modal/Modal.tsx +19 -0
- package/src/ConsoleShared/src/components/modal/index.ts +1 -0
- package/src/ConsoleShared/src/components/popper/Portal.tsx +23 -0
- package/src/ConsoleShared/src/components/popper/SimplePopper.tsx +90 -0
- package/src/ConsoleShared/src/components/popper/index.ts +2 -0
- package/src/ConsoleShared/src/components/spotlight/InteractiveSpotlight.tsx +58 -0
- package/src/ConsoleShared/src/components/spotlight/Spotlight.tsx +35 -0
- package/src/ConsoleShared/src/components/spotlight/StaticSpotlight.tsx +32 -0
- package/src/ConsoleShared/src/components/spotlight/index.ts +1 -0
- package/src/ConsoleShared/src/components/spotlight/spotlight.scss +63 -0
- package/src/ConsoleShared/src/components/status/GenericStatus.tsx +33 -0
- package/src/ConsoleShared/src/components/status/NotStartedIcon.tsx +27 -0
- package/src/ConsoleShared/src/components/status/PopoverStatus.tsx +42 -0
- package/src/ConsoleShared/src/components/status/Status.tsx +38 -0
- package/src/ConsoleShared/src/components/status/StatusIconAndText.tsx +42 -0
- package/src/ConsoleShared/src/components/status/icons.tsx +77 -0
- package/src/ConsoleShared/src/components/status/index.tsx +1 -0
- package/src/ConsoleShared/src/components/status/statuses.tsx +36 -0
- package/src/ConsoleShared/src/components/status/types.ts +7 -0
- package/src/ConsoleShared/src/components/utils/FallbackImg.tsx +20 -0
- package/src/ConsoleShared/src/components/utils/index.ts +1 -0
- package/src/ConsoleShared/src/constants/index.ts +1 -0
- package/src/ConsoleShared/src/constants/ui.ts +1 -0
- package/src/ConsoleShared/src/hooks/index.ts +6 -0
- package/src/ConsoleShared/src/hooks/scroll.ts +52 -0
- package/src/ConsoleShared/src/hooks/useBoundingClientRect.ts +18 -0
- package/src/ConsoleShared/src/hooks/useEventListener.ts +14 -0
- package/src/ConsoleShared/src/hooks/useForceRender.ts +6 -0
- package/src/ConsoleShared/src/hooks/useResizeObserver.ts +20 -0
- package/src/ConsoleShared/src/hooks/useScrollShadows.ts +45 -0
- package/src/ConsoleShared/src/index.ts +4 -0
- package/src/ConsoleShared/src/utils/index.ts +1 -0
- package/src/ConsoleShared/src/utils/useCombineRefs.ts +17 -0
- package/src/HelpTopicDrawer.tsx +124 -0
- package/src/HelpTopicPanelContent.tsx +152 -0
- package/src/QuickStartCatalogPage.tsx +190 -0
- package/src/QuickStartCloseModal.tsx +47 -0
- package/src/QuickStartController.tsx +113 -0
- package/src/QuickStartDrawer.scss +11 -0
- package/src/QuickStartDrawer.tsx +265 -0
- package/src/QuickStartMarkdownView.tsx +75 -0
- package/src/QuickStartPanelContent.scss +46 -0
- package/src/QuickStartPanelContent.tsx +153 -0
- package/src/__tests__/quick-start-utils.spec.tsx +16 -0
- package/src/catalog/Catalog/QuickStartCatalogHeader.tsx +18 -0
- package/src/catalog/Catalog/QuickStartCatalogSection.tsx +9 -0
- package/src/catalog/Catalog/QuickStartCatalogToolbar.tsx +12 -0
- package/src/catalog/Catalog/index.ts +3 -0
- package/src/catalog/QuickStartCatalog.scss +8 -0
- package/src/catalog/QuickStartCatalog.tsx +42 -0
- package/src/catalog/QuickStartTile.scss +11 -0
- package/src/catalog/QuickStartTile.tsx +105 -0
- package/src/catalog/QuickStartTileDescription.scss +29 -0
- package/src/catalog/QuickStartTileDescription.tsx +79 -0
- package/src/catalog/QuickStartTileFooter.tsx +101 -0
- package/src/catalog/QuickStartTileFooterExternal.tsx +40 -0
- package/src/catalog/QuickStartTileHeader.scss +12 -0
- package/src/catalog/QuickStartTileHeader.tsx +77 -0
- package/src/catalog/Toolbar/QuickStartCatalogFilter.scss +25 -0
- package/src/catalog/Toolbar/QuickStartCatalogFilter.tsx +34 -0
- package/src/catalog/Toolbar/QuickStartCatalogFilterItems.tsx +199 -0
- package/src/catalog/__tests__/QuickStartCatalog.spec.tsx +35 -0
- package/src/catalog/__tests__/QuickStartTile.spec.tsx +38 -0
- package/src/catalog/__tests__/QuickStartTileDescription.spec.tsx +44 -0
- package/src/catalog/index.ts +9 -0
- package/src/controller/QuickStartConclusion.tsx +63 -0
- package/src/controller/QuickStartContent.scss +12 -0
- package/src/controller/QuickStartContent.tsx +72 -0
- package/src/controller/QuickStartFooter.scss +13 -0
- package/src/controller/QuickStartFooter.tsx +128 -0
- package/src/controller/QuickStartIntroduction.scss +35 -0
- package/src/controller/QuickStartIntroduction.tsx +66 -0
- package/src/controller/QuickStartTaskHeader.scss +58 -0
- package/src/controller/QuickStartTaskHeader.tsx +116 -0
- package/src/controller/QuickStartTaskHeaderList.scss +17 -0
- package/src/controller/QuickStartTaskHeaderList.tsx +35 -0
- package/src/controller/QuickStartTaskReview.scss +30 -0
- package/src/controller/QuickStartTaskReview.tsx +81 -0
- package/src/controller/QuickStartTasks.scss +89 -0
- package/src/controller/QuickStartTasks.tsx +75 -0
- package/src/controller/__tests__/QuickStartConclusion.spec.tsx +95 -0
- package/src/controller/__tests__/QuickStartContent.spec.tsx +52 -0
- package/src/controller/__tests__/QuickStartFooter.spec.tsx +148 -0
- package/src/controller/__tests__/QuickStartTaskHeader.spec.tsx +56 -0
- package/src/controller/__tests__/QuickStartTaskReview.spec.tsx +45 -0
- package/src/controller/__tests__/QuickStartTasks.spec.tsx +81 -0
- package/src/data/mocks/json/explore-pipeline-quickstart.ts +66 -0
- package/src/data/mocks/json/explore-serverless-quickstart.ts +90 -0
- package/src/data/mocks/json/monitor-sampleapp-quickstart.ts +77 -0
- package/src/data/mocks/json/tour-icons.ts +3 -0
- package/src/data/mocks/yamls/add-healthchecks-quickstart.yaml +67 -0
- package/src/data/mocks/yamls/explore-pipeline-quickstart.yaml +57 -0
- package/src/data/mocks/yamls/explore-serverless-quickstart.yaml +83 -0
- package/src/data/mocks/yamls/install-associate-pipeline-quickstart.yaml +74 -0
- package/src/data/mocks/yamls/monitor-sampleapp-quickstart.yaml +66 -0
- package/src/data/mocks/yamls/sample-application-quickstart.yaml +97 -0
- package/src/data/mocks/yamls/serverless-application-quickstart.yaml +141 -0
- package/src/data/quick-start-test-data.ts +10 -0
- package/src/data/test-utils.ts +11 -0
- package/src/declaration.d.ts +2 -0
- package/src/index.ts +17 -0
- package/src/locales/en/quickstart.json +46 -0
- package/src/styles/_base.scss +54 -0
- package/src/styles/_dark-custom-override.scss +62 -0
- package/src/styles/legacy-bootstrap/README.md +21 -0
- package/src/styles/legacy-bootstrap/_code.scss +44 -0
- package/src/styles/legacy-bootstrap/_tables.scss +38 -0
- package/src/styles/legacy-bootstrap/_type.scss +90 -0
- package/src/styles/legacy-bootstrap/_variables.scss +48 -0
- package/src/styles/legacy-bootstrap.scss +5 -0
- package/src/styles/patternfly-global-entry.ts +1 -0
- package/src/styles/patternfly-global.scss +28 -0
- package/src/styles/patternfly-nested-entry.ts +1 -0
- package/src/styles/patternfly-nested.scss +18 -0
- package/src/styles/quickstarts-standalone-entry.ts +1 -0
- package/src/styles/quickstarts-standalone.scss +7 -0
- package/src/styles/style.scss +12 -0
- package/src/styles/vendor-entry.ts +1 -0
- package/src/styles/vendor.scss +7 -0
- package/src/utils/PluralResolver.ts +356 -0
- package/src/utils/asciidoc-procedure-parser.ts +132 -0
- package/src/utils/const.ts +10 -0
- package/src/utils/help-topic-context.tsx +74 -0
- package/src/utils/help-topic-types.ts +16 -0
- package/src/utils/quick-start-context.tsx +477 -0
- package/src/utils/quick-start-types.ts +72 -0
- package/src/utils/quick-start-utils.ts +92 -0
- package/src/utils/useLocalStorage.ts +38 -0
|
@@ -0,0 +1,128 @@
|
|
|
1
|
+
import * as React from 'react';
|
|
2
|
+
import { Button } from '@patternfly/react-core';
|
|
3
|
+
import { QuickStartContext, QuickStartContextValues } from '../utils/quick-start-context';
|
|
4
|
+
import { QuickStartStatus } from '../utils/quick-start-types';
|
|
5
|
+
import { camelize } from '../utils/quick-start-utils';
|
|
6
|
+
|
|
7
|
+
import './QuickStartFooter.scss';
|
|
8
|
+
|
|
9
|
+
export type QuickStartFooterProps = {
|
|
10
|
+
status: QuickStartStatus;
|
|
11
|
+
footerClass: string;
|
|
12
|
+
taskNumber: number;
|
|
13
|
+
totalTasks: number;
|
|
14
|
+
onNext: () => void;
|
|
15
|
+
onBack: () => void;
|
|
16
|
+
quickStartId: string;
|
|
17
|
+
};
|
|
18
|
+
|
|
19
|
+
const QuickStartFooter: React.FC<QuickStartFooterProps> = ({
|
|
20
|
+
status,
|
|
21
|
+
taskNumber,
|
|
22
|
+
totalTasks,
|
|
23
|
+
onNext,
|
|
24
|
+
onBack,
|
|
25
|
+
footerClass,
|
|
26
|
+
quickStartId,
|
|
27
|
+
}) => {
|
|
28
|
+
const { restartQuickStart, getResource } = React.useContext<QuickStartContextValues>(
|
|
29
|
+
QuickStartContext,
|
|
30
|
+
);
|
|
31
|
+
|
|
32
|
+
const PrimaryButtonText = React.useMemo(() => {
|
|
33
|
+
return {
|
|
34
|
+
START: getResource('Start'),
|
|
35
|
+
CONTINUE: getResource('Continue'),
|
|
36
|
+
NEXT: getResource('Next'),
|
|
37
|
+
CLOSE: getResource('Close'),
|
|
38
|
+
};
|
|
39
|
+
}, [getResource]);
|
|
40
|
+
|
|
41
|
+
const SecondaryButtonText = React.useMemo(() => {
|
|
42
|
+
return {
|
|
43
|
+
BACK: getResource('Back'),
|
|
44
|
+
RESTART: getResource('Restart'),
|
|
45
|
+
};
|
|
46
|
+
}, [getResource]);
|
|
47
|
+
|
|
48
|
+
const onRestart = React.useCallback(
|
|
49
|
+
(e: React.SyntheticEvent) => {
|
|
50
|
+
e.preventDefault();
|
|
51
|
+
e.stopPropagation();
|
|
52
|
+
restartQuickStart(quickStartId, totalTasks);
|
|
53
|
+
},
|
|
54
|
+
[quickStartId, restartQuickStart, totalTasks],
|
|
55
|
+
);
|
|
56
|
+
|
|
57
|
+
const getPrimaryButtonText = React.useMemo(() => {
|
|
58
|
+
if (status === QuickStartStatus.NOT_STARTED) {
|
|
59
|
+
return PrimaryButtonText.START;
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
if (taskNumber === totalTasks) {
|
|
63
|
+
return PrimaryButtonText.CLOSE;
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
if (taskNumber > -1 && taskNumber < totalTasks) {
|
|
67
|
+
return PrimaryButtonText.NEXT;
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
return PrimaryButtonText.CONTINUE;
|
|
71
|
+
}, [taskNumber, totalTasks, PrimaryButtonText, status]);
|
|
72
|
+
|
|
73
|
+
const getPrimaryButton = React.useMemo(
|
|
74
|
+
() => (
|
|
75
|
+
<Button
|
|
76
|
+
variant="primary"
|
|
77
|
+
className="pfext-quick-start-footer__actionbtn"
|
|
78
|
+
onClick={onNext}
|
|
79
|
+
data-testid={`qs-drawer-${camelize(getPrimaryButtonText)}`}
|
|
80
|
+
data-test={`${getPrimaryButtonText} button`}
|
|
81
|
+
>
|
|
82
|
+
{getPrimaryButtonText}
|
|
83
|
+
</Button>
|
|
84
|
+
),
|
|
85
|
+
[getPrimaryButtonText, onNext],
|
|
86
|
+
);
|
|
87
|
+
|
|
88
|
+
const getSecondaryButton = React.useMemo(
|
|
89
|
+
() =>
|
|
90
|
+
taskNumber === -1 && status !== QuickStartStatus.NOT_STARTED ? (
|
|
91
|
+
<Button variant="secondary" onClick={onRestart} data-testid="qs-drawer-restart">
|
|
92
|
+
{SecondaryButtonText.RESTART}
|
|
93
|
+
</Button>
|
|
94
|
+
) : (
|
|
95
|
+
taskNumber > -1 && (
|
|
96
|
+
<Button variant="secondary" onClick={onBack} data-testid="qs-drawer-back">
|
|
97
|
+
{SecondaryButtonText.BACK}
|
|
98
|
+
</Button>
|
|
99
|
+
)
|
|
100
|
+
),
|
|
101
|
+
[onRestart, onBack, SecondaryButtonText, status, taskNumber],
|
|
102
|
+
);
|
|
103
|
+
|
|
104
|
+
const getSideNoteAction = React.useMemo(
|
|
105
|
+
() =>
|
|
106
|
+
taskNumber !== -1 && (
|
|
107
|
+
<Button
|
|
108
|
+
variant="link"
|
|
109
|
+
className="pfext-quick-start-footer__restartbtn"
|
|
110
|
+
onClick={onRestart}
|
|
111
|
+
data-testid="qs-drawer-side-note-action"
|
|
112
|
+
>
|
|
113
|
+
{SecondaryButtonText.RESTART}
|
|
114
|
+
</Button>
|
|
115
|
+
),
|
|
116
|
+
[taskNumber, onRestart, SecondaryButtonText.RESTART],
|
|
117
|
+
);
|
|
118
|
+
|
|
119
|
+
return (
|
|
120
|
+
<div className={`pfext-quick-start-footer ${footerClass}`}>
|
|
121
|
+
{getPrimaryButton}
|
|
122
|
+
{getSecondaryButton}
|
|
123
|
+
{getSideNoteAction}
|
|
124
|
+
</div>
|
|
125
|
+
);
|
|
126
|
+
};
|
|
127
|
+
|
|
128
|
+
export default QuickStartFooter;
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
.pfext-quick-start-intro {
|
|
2
|
+
&__prereq {
|
|
3
|
+
margin-bottom: var(--pf-global--spacer--md);
|
|
4
|
+
.pf-c-expandable-section__content {
|
|
5
|
+
margin-top: var(--pf-global--spacer--sm);
|
|
6
|
+
}
|
|
7
|
+
.pf-c-expandable-section__toggle {
|
|
8
|
+
padding-top: 0;
|
|
9
|
+
padding-bottom: 0;
|
|
10
|
+
&-text {
|
|
11
|
+
margin-left: var(--pf-global--spacer--sm);
|
|
12
|
+
}
|
|
13
|
+
&-icon {
|
|
14
|
+
color: var(--pf-c-expandable-section__toggle--Color);
|
|
15
|
+
&:focus, &:hover {
|
|
16
|
+
--pf-c-expandable-section__toggle--Color: var(--pf-c-expandable-section__toggle--focus--Color);
|
|
17
|
+
}
|
|
18
|
+
}
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
&-list {
|
|
22
|
+
padding-left: 20px;
|
|
23
|
+
&__item {
|
|
24
|
+
&::marker {
|
|
25
|
+
font-size: 0.8rem;
|
|
26
|
+
}
|
|
27
|
+
&-content {
|
|
28
|
+
position: relative;
|
|
29
|
+
left: 2px;
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
}
|
|
35
|
+
}
|
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
import * as React from 'react';
|
|
2
|
+
import { ExpandableSection, List, ListItem } from '@patternfly/react-core';
|
|
3
|
+
import QuickStartMarkdownView from '../QuickStartMarkdownView';
|
|
4
|
+
import { QuickStartContext, QuickStartContextValues } from '../utils/quick-start-context';
|
|
5
|
+
import { QuickStartTask, QuickStartTaskStatus } from '../utils/quick-start-types';
|
|
6
|
+
import TaskHeaderList from './QuickStartTaskHeaderList';
|
|
7
|
+
|
|
8
|
+
import './QuickStartIntroduction.scss';
|
|
9
|
+
|
|
10
|
+
type QuickStartIntroductionProps = {
|
|
11
|
+
introduction: string;
|
|
12
|
+
tasks: QuickStartTask[];
|
|
13
|
+
allTaskStatuses: QuickStartTaskStatus[];
|
|
14
|
+
prerequisites?: string[];
|
|
15
|
+
onTaskSelect: (selectedTaskNumber: number) => void;
|
|
16
|
+
};
|
|
17
|
+
|
|
18
|
+
const QuickStartIntroduction: React.FC<QuickStartIntroductionProps> = ({
|
|
19
|
+
tasks,
|
|
20
|
+
introduction,
|
|
21
|
+
allTaskStatuses,
|
|
22
|
+
prerequisites,
|
|
23
|
+
onTaskSelect,
|
|
24
|
+
}) => {
|
|
25
|
+
const { getResource } = React.useContext<QuickStartContextValues>(QuickStartContext);
|
|
26
|
+
const prereqs = prerequisites?.filter((p) => p);
|
|
27
|
+
const [isPrereqsExpanded, setIsPrereqsExpanded] = React.useState(false);
|
|
28
|
+
const prereqList = prereqs?.length > 0 && (
|
|
29
|
+
<ExpandableSection
|
|
30
|
+
toggleText={getResource('View Prerequisites ({{totalPrereqs}})').replace(
|
|
31
|
+
'{{totalPrereqs}}',
|
|
32
|
+
prereqs.length,
|
|
33
|
+
)}
|
|
34
|
+
onToggle={() => setIsPrereqsExpanded(!isPrereqsExpanded)}
|
|
35
|
+
className="pfext-quick-start-intro__prereq"
|
|
36
|
+
>
|
|
37
|
+
<List className="pfext-quick-start-intro__prereq-list">
|
|
38
|
+
{prereqs.map((pr) => {
|
|
39
|
+
return (
|
|
40
|
+
<ListItem key={pr} className="pfext-quick-start-intro__prereq-list__item">
|
|
41
|
+
<span className="pfext-quick-start-intro__prereq-list__item-content">
|
|
42
|
+
<QuickStartMarkdownView content={pr} />
|
|
43
|
+
</span>
|
|
44
|
+
</ListItem>
|
|
45
|
+
);
|
|
46
|
+
})}
|
|
47
|
+
</List>
|
|
48
|
+
</ExpandableSection>
|
|
49
|
+
);
|
|
50
|
+
return (
|
|
51
|
+
<>
|
|
52
|
+
<QuickStartMarkdownView content={introduction} />
|
|
53
|
+
{prereqList}
|
|
54
|
+
<p style={{ marginBottom: 'var(--pf-global--spacer--md)' }}>
|
|
55
|
+
{getResource(
|
|
56
|
+
'In this quick start, you will complete {{count, number}} task',
|
|
57
|
+
tasks.length,
|
|
58
|
+
).replace('{{count, number}}', tasks.length)}
|
|
59
|
+
:
|
|
60
|
+
</p>
|
|
61
|
+
<TaskHeaderList tasks={tasks} allTaskStatuses={allTaskStatuses} onTaskSelect={onTaskSelect} />
|
|
62
|
+
</>
|
|
63
|
+
);
|
|
64
|
+
};
|
|
65
|
+
|
|
66
|
+
export default QuickStartIntroduction;
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
.pfext-quick-start-task-header {
|
|
2
|
+
margin-bottom: var(--pf-global--spacer--sm);
|
|
3
|
+
display: grid;
|
|
4
|
+
grid-template-columns: min-content auto;
|
|
5
|
+
font-size: var(--pf-global--FontSize--md);
|
|
6
|
+
button::before {
|
|
7
|
+
content: none;
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
&__title {
|
|
11
|
+
color: var(--pf-global--primary-color--100) !important;
|
|
12
|
+
margin-right: var(--pf-global--spacer--md) !important;
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
&__subtitle {
|
|
16
|
+
font-size: var(--pf-global--FontSize--sm);
|
|
17
|
+
color: var(--pf-global--Color--dark-200) !important;
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
&__tryagain {
|
|
21
|
+
display: block;
|
|
22
|
+
font-size: var(--pf-global--FontSize--md);
|
|
23
|
+
color: var(--pf-global--Color--300) !important;
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
&__title-success {
|
|
27
|
+
color: var(--pf-global--success-color--100) !important;
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
&__title-failed {
|
|
31
|
+
color: var(--pf-global--danger-color--100) !important;
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
&__task-icon {
|
|
35
|
+
&-init {
|
|
36
|
+
background-color: var(--pf-global--primary-color--100);
|
|
37
|
+
border-radius: var(--pf-global--BorderRadius--lg);
|
|
38
|
+
color: var(--pf-global--Color--light-100);
|
|
39
|
+
display: inline-flex;
|
|
40
|
+
justify-content: center;
|
|
41
|
+
height: 1.5em;
|
|
42
|
+
width: 1.5em;
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
&-success,
|
|
46
|
+
&-failed {
|
|
47
|
+
vertical-align: middle !important;
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
&-success {
|
|
51
|
+
color: var(--pf-global--success-color--100) !important;
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
&-failed {
|
|
55
|
+
color: var(--pf-global--danger-color--100) !important;
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
}
|
|
@@ -0,0 +1,116 @@
|
|
|
1
|
+
import * as React from 'react';
|
|
2
|
+
import { Title, WizardNavItem } from '@patternfly/react-core';
|
|
3
|
+
import CheckCircleIcon from '@patternfly/react-icons/dist/js/icons/check-circle-icon';
|
|
4
|
+
import ExclamationCircleIcon from '@patternfly/react-icons/dist/js/icons/exclamation-circle-icon';
|
|
5
|
+
import { css } from '@patternfly/react-styles';
|
|
6
|
+
import { markdownConvert } from '../ConsoleInternal/components/markdown-view';
|
|
7
|
+
import { removeParagraphWrap } from '../QuickStartMarkdownView';
|
|
8
|
+
import { QuickStartContext, QuickStartContextValues } from '../utils/quick-start-context';
|
|
9
|
+
import { QuickStartTaskStatus } from '../utils/quick-start-types';
|
|
10
|
+
|
|
11
|
+
import './QuickStartTaskHeader.scss';
|
|
12
|
+
|
|
13
|
+
type QuickStartTaskHeaderProps = {
|
|
14
|
+
title: string;
|
|
15
|
+
taskIndex: number;
|
|
16
|
+
subtitle?: string;
|
|
17
|
+
taskStatus?: QuickStartTaskStatus;
|
|
18
|
+
size?: 'md' | 'lg' | 'xl' | '2xl' | '3xl' | '4xl';
|
|
19
|
+
isActiveTask?: boolean;
|
|
20
|
+
onTaskSelect: (index: number) => void;
|
|
21
|
+
chidlren?: React.ReactNode;
|
|
22
|
+
};
|
|
23
|
+
|
|
24
|
+
const TaskIcon: React.FC<{
|
|
25
|
+
taskIndex: number;
|
|
26
|
+
taskStatus: QuickStartTaskStatus;
|
|
27
|
+
}> = ({ taskIndex, taskStatus }) => {
|
|
28
|
+
const { getResource } = React.useContext<QuickStartContextValues>(QuickStartContext);
|
|
29
|
+
const success = taskStatus === QuickStartTaskStatus.SUCCESS;
|
|
30
|
+
const failed = taskStatus === QuickStartTaskStatus.FAILED;
|
|
31
|
+
|
|
32
|
+
const classNames = css('pfext-icon-and-text__icon', {
|
|
33
|
+
'pfext-quick-start-task-header__task-icon-init': !failed && !success,
|
|
34
|
+
});
|
|
35
|
+
let content: {};
|
|
36
|
+
if (success) {
|
|
37
|
+
content = (
|
|
38
|
+
<CheckCircleIcon size="md" className="pfext-quick-start-task-header__task-icon-success" />
|
|
39
|
+
);
|
|
40
|
+
} else if (failed) {
|
|
41
|
+
content = (
|
|
42
|
+
<ExclamationCircleIcon
|
|
43
|
+
size="md"
|
|
44
|
+
className="pfext-quick-start-task-header__task-icon-failed"
|
|
45
|
+
/>
|
|
46
|
+
);
|
|
47
|
+
} else {
|
|
48
|
+
content = getResource('{{taskIndex, number}}', taskIndex).replace(
|
|
49
|
+
'{{taskIndex, number}}',
|
|
50
|
+
taskIndex,
|
|
51
|
+
);
|
|
52
|
+
}
|
|
53
|
+
return <span className={classNames}>{content}</span>;
|
|
54
|
+
};
|
|
55
|
+
|
|
56
|
+
const QuickStartTaskHeader: React.FC<QuickStartTaskHeaderProps> = ({
|
|
57
|
+
title,
|
|
58
|
+
taskIndex,
|
|
59
|
+
subtitle,
|
|
60
|
+
taskStatus,
|
|
61
|
+
size,
|
|
62
|
+
isActiveTask,
|
|
63
|
+
onTaskSelect,
|
|
64
|
+
children,
|
|
65
|
+
}) => {
|
|
66
|
+
const classNames = css('pfext-quick-start-task-header__title', {
|
|
67
|
+
'pfext-quick-start-task-header__title-success': taskStatus === QuickStartTaskStatus.SUCCESS,
|
|
68
|
+
'pfext-quick-start-task-header__title-failed':
|
|
69
|
+
taskStatus === (QuickStartTaskStatus.FAILED || QuickStartTaskStatus.VISITED),
|
|
70
|
+
});
|
|
71
|
+
// const notCompleted = taskStatus === QuickStartTaskStatus.VISITED;
|
|
72
|
+
// const skippedReview = taskStatus === QuickStartTaskStatus.REVIEW;
|
|
73
|
+
const failedReview = taskStatus === QuickStartTaskStatus.FAILED;
|
|
74
|
+
|
|
75
|
+
// TODO: toned down when this is shown, investigate further when we should display it
|
|
76
|
+
// related: https://github.com/patternfly/patternfly-quickstarts/issues/104
|
|
77
|
+
const tryAgain = failedReview && (
|
|
78
|
+
<>
|
|
79
|
+
<div />
|
|
80
|
+
<div className="pfext-quick-start-task-header__tryagain">Try the steps again.</div>
|
|
81
|
+
</>
|
|
82
|
+
);
|
|
83
|
+
|
|
84
|
+
const content = (
|
|
85
|
+
<div className="pfext-quick-start-task-header">
|
|
86
|
+
<TaskIcon taskIndex={taskIndex} taskStatus={taskStatus} />
|
|
87
|
+
<Title headingLevel="h3" size={size} className={classNames}>
|
|
88
|
+
<span dangerouslySetInnerHTML={{ __html: removeParagraphWrap(markdownConvert(title)) }} />
|
|
89
|
+
{isActiveTask && subtitle && (
|
|
90
|
+
<span
|
|
91
|
+
className="pfext-quick-start-task-header__subtitle"
|
|
92
|
+
data-test-id="quick-start-task-subtitle"
|
|
93
|
+
>
|
|
94
|
+
{' '}
|
|
95
|
+
{subtitle}
|
|
96
|
+
</span>
|
|
97
|
+
)}
|
|
98
|
+
</Title>
|
|
99
|
+
{tryAgain}
|
|
100
|
+
</div>
|
|
101
|
+
);
|
|
102
|
+
|
|
103
|
+
return (
|
|
104
|
+
<WizardNavItem
|
|
105
|
+
content={content}
|
|
106
|
+
step={taskIndex}
|
|
107
|
+
onNavItemClick={() => onTaskSelect(taskIndex - 1)}
|
|
108
|
+
navItemComponent="button"
|
|
109
|
+
isCurrent={isActiveTask}
|
|
110
|
+
>
|
|
111
|
+
{children}
|
|
112
|
+
</WizardNavItem>
|
|
113
|
+
);
|
|
114
|
+
};
|
|
115
|
+
|
|
116
|
+
export default QuickStartTaskHeader;
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
.pfext-quick-start-task-header {
|
|
2
|
+
&__list {
|
|
3
|
+
padding: 0 !important;
|
|
4
|
+
|
|
5
|
+
button::before {
|
|
6
|
+
content: none;
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
li {
|
|
10
|
+
list-style-type: none;
|
|
11
|
+
display: flex;
|
|
12
|
+
flex-wrap: wrap;
|
|
13
|
+
align-items: center;
|
|
14
|
+
margin-bottom: var(--pf-global--spacer--xs);
|
|
15
|
+
}
|
|
16
|
+
}
|
|
17
|
+
}
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
import * as React from 'react';
|
|
2
|
+
import { List } from '@patternfly/react-core';
|
|
3
|
+
import { QuickStartTask, QuickStartTaskStatus } from '../utils/quick-start-types';
|
|
4
|
+
import TaskHeader from './QuickStartTaskHeader';
|
|
5
|
+
|
|
6
|
+
import './QuickStartTaskHeaderList.scss';
|
|
7
|
+
|
|
8
|
+
type QuickStartTaskHeaderListProps = {
|
|
9
|
+
tasks: QuickStartTask[];
|
|
10
|
+
allTaskStatuses: QuickStartTaskStatus[];
|
|
11
|
+
onTaskSelect: (selectedTaskNumber: number) => void;
|
|
12
|
+
};
|
|
13
|
+
|
|
14
|
+
const QuickStartTaskHeaderList: React.FC<QuickStartTaskHeaderListProps> = ({
|
|
15
|
+
tasks,
|
|
16
|
+
allTaskStatuses,
|
|
17
|
+
onTaskSelect,
|
|
18
|
+
}) => {
|
|
19
|
+
return tasks.length > 0 ? (
|
|
20
|
+
<List className="pfext-quick-start-task-header__list">
|
|
21
|
+
{tasks.map((task, index) => (
|
|
22
|
+
<TaskHeader
|
|
23
|
+
key={task.title}
|
|
24
|
+
title={task.title}
|
|
25
|
+
taskIndex={index + 1}
|
|
26
|
+
size="md"
|
|
27
|
+
taskStatus={allTaskStatuses[index]}
|
|
28
|
+
onTaskSelect={onTaskSelect}
|
|
29
|
+
/>
|
|
30
|
+
))}
|
|
31
|
+
</List>
|
|
32
|
+
) : null;
|
|
33
|
+
};
|
|
34
|
+
|
|
35
|
+
export default QuickStartTaskHeaderList;
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
.pfext-quick-start-task-review-alert {
|
|
2
|
+
margin: var(--pf-global--spacer--lg) 0;
|
|
3
|
+
}
|
|
4
|
+
.pfext-quick-start-task-review {
|
|
5
|
+
font-size: var(--pf-global--FontSize--md);
|
|
6
|
+
line-height: var(--pf-global--FontSize--xl);
|
|
7
|
+
font-family: var(--pf-global--FontFamily--heading--sans-serif);
|
|
8
|
+
|
|
9
|
+
&__actions {
|
|
10
|
+
display: flex;
|
|
11
|
+
align-items: flex-start;
|
|
12
|
+
margin-bottom: var(--pf-global--spacer--sm);
|
|
13
|
+
input[type='radio'] {
|
|
14
|
+
margin-top: 0;
|
|
15
|
+
margin-right: 0;
|
|
16
|
+
}
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
&__radio {
|
|
20
|
+
margin-right: var(--pf-global--spacer--xl) !important;
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
&--success {
|
|
24
|
+
color: var(--pf-global--success-color--100) !important;
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
&--failed {
|
|
28
|
+
color: var(--pf-chart-global--danger--Color--100);
|
|
29
|
+
}
|
|
30
|
+
}
|
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
import * as React from 'react';
|
|
2
|
+
import { Alert, Radio } from '@patternfly/react-core';
|
|
3
|
+
import { css } from '@patternfly/react-styles';
|
|
4
|
+
import QuickStartMarkdownView from '../QuickStartMarkdownView';
|
|
5
|
+
import { QuickStartContext, QuickStartContextValues } from '../utils/quick-start-context';
|
|
6
|
+
import {
|
|
7
|
+
QuickStartTaskReview as QuickStartTaskReviewType,
|
|
8
|
+
QuickStartTaskStatus,
|
|
9
|
+
} from '../utils/quick-start-types';
|
|
10
|
+
|
|
11
|
+
import './QuickStartTaskReview.scss';
|
|
12
|
+
|
|
13
|
+
type QuickStartTaskReviewProps = {
|
|
14
|
+
review: QuickStartTaskReviewType;
|
|
15
|
+
taskStatus: QuickStartTaskStatus;
|
|
16
|
+
onTaskReview: (status: QuickStartTaskStatus) => void;
|
|
17
|
+
};
|
|
18
|
+
|
|
19
|
+
const getAlertVariant = (status) => {
|
|
20
|
+
switch (status) {
|
|
21
|
+
case QuickStartTaskStatus.SUCCESS:
|
|
22
|
+
return 'success';
|
|
23
|
+
case QuickStartTaskStatus.FAILED:
|
|
24
|
+
return 'danger';
|
|
25
|
+
default:
|
|
26
|
+
return 'info';
|
|
27
|
+
}
|
|
28
|
+
};
|
|
29
|
+
|
|
30
|
+
const QuickStartTaskReview: React.FC<QuickStartTaskReviewProps> = ({
|
|
31
|
+
review,
|
|
32
|
+
taskStatus,
|
|
33
|
+
onTaskReview,
|
|
34
|
+
}) => {
|
|
35
|
+
const { instructions, failedTaskHelp: taskHelp } = review;
|
|
36
|
+
const { getResource } = React.useContext<QuickStartContextValues>(QuickStartContext);
|
|
37
|
+
|
|
38
|
+
const alertClassNames = css('pfext-quick-start-task-review', {
|
|
39
|
+
'pfext-quick-start-task-review--success': taskStatus === QuickStartTaskStatus.SUCCESS,
|
|
40
|
+
'pfext-quick-start-task-review--failed': taskStatus === QuickStartTaskStatus.FAILED,
|
|
41
|
+
});
|
|
42
|
+
|
|
43
|
+
const title = <span className={alertClassNames}>{getResource('Check your work')}</span>;
|
|
44
|
+
|
|
45
|
+
return (
|
|
46
|
+
<Alert
|
|
47
|
+
className="pfext-quick-start-task-review-alert"
|
|
48
|
+
variant={getAlertVariant(taskStatus)}
|
|
49
|
+
title={title}
|
|
50
|
+
isInline
|
|
51
|
+
role="alert"
|
|
52
|
+
>
|
|
53
|
+
<QuickStartMarkdownView content={instructions} />
|
|
54
|
+
<span className="pfext-quick-start-task-review__actions">
|
|
55
|
+
<Radio
|
|
56
|
+
id="review-success"
|
|
57
|
+
name="review-success"
|
|
58
|
+
data-testid="qs-drawer-check-yes"
|
|
59
|
+
label={getResource('Yes')}
|
|
60
|
+
className="pfext-quick-start-task-review__radio"
|
|
61
|
+
isChecked={taskStatus === QuickStartTaskStatus.SUCCESS}
|
|
62
|
+
onChange={() => onTaskReview(QuickStartTaskStatus.SUCCESS)}
|
|
63
|
+
/>
|
|
64
|
+
<Radio
|
|
65
|
+
id="review-failed"
|
|
66
|
+
name="review-failed"
|
|
67
|
+
data-testid="qs-drawer-check-no"
|
|
68
|
+
label={getResource('No')}
|
|
69
|
+
className="pfext-quick-start-task-review__radio"
|
|
70
|
+
isChecked={taskStatus === QuickStartTaskStatus.FAILED}
|
|
71
|
+
onChange={() => onTaskReview(QuickStartTaskStatus.FAILED)}
|
|
72
|
+
/>
|
|
73
|
+
</span>
|
|
74
|
+
{taskStatus === QuickStartTaskStatus.FAILED && taskHelp && (
|
|
75
|
+
<QuickStartMarkdownView content={taskHelp} exactHeight />
|
|
76
|
+
)}
|
|
77
|
+
</Alert>
|
|
78
|
+
);
|
|
79
|
+
};
|
|
80
|
+
|
|
81
|
+
export default QuickStartTaskReview;
|
|
@@ -0,0 +1,89 @@
|
|
|
1
|
+
.pfext-quick-start-tasks {
|
|
2
|
+
&__list {
|
|
3
|
+
button::before {
|
|
4
|
+
content: none;
|
|
5
|
+
}
|
|
6
|
+
|
|
7
|
+
li {
|
|
8
|
+
&.pf-c-wizard__nav-item {
|
|
9
|
+
list-style-type: none;
|
|
10
|
+
display: flex;
|
|
11
|
+
flex-wrap: wrap;
|
|
12
|
+
align-items: center;
|
|
13
|
+
margin-bottom: var(--pf-global--spacer--xs);
|
|
14
|
+
}
|
|
15
|
+
}
|
|
16
|
+
.pfext-quick-start-task__content {
|
|
17
|
+
margin-bottom: var(--pf-global--spacer--md);
|
|
18
|
+
}
|
|
19
|
+
// Custom styles on PF React List rendered in custom react renderer
|
|
20
|
+
.task-pflist {
|
|
21
|
+
&-title {
|
|
22
|
+
font-size: 14px !important;
|
|
23
|
+
margin-bottom: 6px;
|
|
24
|
+
}
|
|
25
|
+
&-subtitle {
|
|
26
|
+
color: var(--pf-global--Color--200);
|
|
27
|
+
font-size: 16px;
|
|
28
|
+
}
|
|
29
|
+
&-list {
|
|
30
|
+
font-size: 16px;
|
|
31
|
+
&--prereq {
|
|
32
|
+
padding-left: var(--pf-global--spacer--lg);
|
|
33
|
+
li + li {
|
|
34
|
+
margin-top: 4px;
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
&--proc {
|
|
38
|
+
padding-left: 10px;
|
|
39
|
+
li + li {
|
|
40
|
+
margin-top: 16px;
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
&__item {
|
|
44
|
+
&--prereq {
|
|
45
|
+
&::marker {
|
|
46
|
+
font-size: 0.8rem;
|
|
47
|
+
}
|
|
48
|
+
.task-pflist-list__item__content {
|
|
49
|
+
position: relative;
|
|
50
|
+
left: 2px;
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
&--proc {
|
|
54
|
+
font-size: 14px;
|
|
55
|
+
.task-pflist-list__item__content {
|
|
56
|
+
position: relative;
|
|
57
|
+
left: 4px;
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
&__content {
|
|
61
|
+
.pf-c-alert {
|
|
62
|
+
// add margins to match design
|
|
63
|
+
margin: var(--pf-global--spacer--md) 0;
|
|
64
|
+
}
|
|
65
|
+
.pf-c-alert__title {
|
|
66
|
+
// remove margins from markdown css
|
|
67
|
+
margin-top: 0;
|
|
68
|
+
margin-bottom: 0;
|
|
69
|
+
// lift PF style specificity to override markdown css
|
|
70
|
+
font-weight: var(--pf-c-alert__title--FontWeight);
|
|
71
|
+
font-family: inherit;
|
|
72
|
+
line-height: inherit;
|
|
73
|
+
color: var(--pf-c-alert__title--Color);
|
|
74
|
+
word-break: break-word;
|
|
75
|
+
}
|
|
76
|
+
.task-pflist-list__item__content__note {
|
|
77
|
+
background-color: var(--pf-global--palette--blue-50);
|
|
78
|
+
border-color: var(--pf-global--active-color--200);
|
|
79
|
+
margin: var(--pf-global--spacer--md) 0;
|
|
80
|
+
&__body {
|
|
81
|
+
font-size: 14px;
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
}
|