mirror of
https://github.com/kemko/nomad.git
synced 2026-01-01 16:05:42 +03:00
Storybook scripts and references removed (#22232)
This commit is contained in:
1
.gitignore
vendored
1
.gitignore
vendored
@@ -97,7 +97,6 @@ rkt-*
|
||||
# misc
|
||||
/ui/.sass-cache
|
||||
/ui/.eslintcache
|
||||
/ui/.storybook/preview-head.html
|
||||
/ui/connect.lock
|
||||
/ui/coverage/*
|
||||
/ui/libpeerconnection.log
|
||||
|
||||
@@ -69,17 +69,6 @@ module.exports = {
|
||||
'node/no-unpublished-require': 'off',
|
||||
},
|
||||
},
|
||||
{
|
||||
files: ['stories/**/*.js'],
|
||||
parserOptions: {
|
||||
sourceType: 'module',
|
||||
},
|
||||
env: {
|
||||
browser: false,
|
||||
node: true,
|
||||
},
|
||||
plugins: ['node'],
|
||||
},
|
||||
{
|
||||
// Test files:
|
||||
files: ['tests/**/*-test.{js,ts}'],
|
||||
|
||||
@@ -1,23 +0,0 @@
|
||||
/**
|
||||
* Copyright (c) HashiCorp, Inc.
|
||||
* SPDX-License-Identifier: BUSL-1.1
|
||||
*/
|
||||
|
||||
/* eslint-env node */
|
||||
module.exports = {
|
||||
framework: '@storybook/ember',
|
||||
addons: [
|
||||
'@storybook/addon-docs',
|
||||
'@storybook/addon-storysource',
|
||||
'@storybook/addon-knobs',
|
||||
'@storybook/addon-viewport',
|
||||
],
|
||||
stories: [
|
||||
'../stories/theme/*.stories.js',
|
||||
'../stories/components/*.stories.js',
|
||||
'../stories/charts/*.stories.js',
|
||||
],
|
||||
core: {
|
||||
builder: '@storybook/builder-webpack4',
|
||||
},
|
||||
};
|
||||
@@ -1,46 +0,0 @@
|
||||
/**
|
||||
* Copyright (c) HashiCorp, Inc.
|
||||
* SPDX-License-Identifier: BUSL-1.1
|
||||
*/
|
||||
|
||||
import { addDecorator, addParameters } from '@storybook/ember';
|
||||
import { INITIAL_VIEWPORTS } from '@storybook/addon-viewport';
|
||||
import theme from './theme.js';
|
||||
|
||||
addParameters({
|
||||
viewport: { viewports: INITIAL_VIEWPORTS },
|
||||
options: {
|
||||
showPanel: true,
|
||||
theme,
|
||||
},
|
||||
});
|
||||
|
||||
addDecorator((storyFn) => {
|
||||
let { template, context } = storyFn();
|
||||
|
||||
let wrapperElementStyle = {
|
||||
margin: '20px',
|
||||
};
|
||||
|
||||
let applicationWrapperElement = document.createElement('div');
|
||||
Object.assign(applicationWrapperElement.style, wrapperElementStyle);
|
||||
|
||||
let storybookElement = document.createElement('div');
|
||||
storybookElement.setAttribute('id', 'storybook');
|
||||
|
||||
let wormhole = document.createElement('div');
|
||||
wormhole.setAttribute('id', 'ember-basic-dropdown-wormhole');
|
||||
|
||||
storybookElement.appendChild(wormhole);
|
||||
|
||||
applicationWrapperElement.appendChild(storybookElement);
|
||||
storybookElement.appendTo = function appendTo(el) {
|
||||
el.appendChild(applicationWrapperElement);
|
||||
};
|
||||
|
||||
return {
|
||||
template,
|
||||
context,
|
||||
element: storybookElement,
|
||||
};
|
||||
});
|
||||
@@ -1,41 +0,0 @@
|
||||
/**
|
||||
* Copyright (c) HashiCorp, Inc.
|
||||
* SPDX-License-Identifier: BUSL-1.1
|
||||
*/
|
||||
|
||||
import { create } from '@storybook/theming';
|
||||
|
||||
// From Bulma
|
||||
let blackBis = 'hsl(0, 0%, 7%)';
|
||||
let greyLight = 'hsl(0, 0%, 71%)';
|
||||
|
||||
// From product-colors.scss
|
||||
let vagrantBlue = '#1563ff';
|
||||
|
||||
export default create({
|
||||
base: 'light',
|
||||
|
||||
colorPrimary: blackBis,
|
||||
colorSecondary: vagrantBlue,
|
||||
|
||||
// UI
|
||||
appBorderColor: greyLight,
|
||||
|
||||
// Typography
|
||||
// From variables.scss
|
||||
fontBase:
|
||||
"-apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Oxygen-Sans, Ubuntu, Cantarell, 'Helvetica Neue', sans-serif",
|
||||
// From Bulma
|
||||
fontCode: 'monospace',
|
||||
|
||||
// Text colors
|
||||
textColor: blackBis,
|
||||
|
||||
// Toolbar default and active colors
|
||||
barTextColor: greyLight,
|
||||
barSelectedColor: 'white',
|
||||
barBg: blackBis,
|
||||
|
||||
brandTitle: 'Nomad Storybook',
|
||||
brandUrl: 'https://developer.hashicorp.com/nomad/',
|
||||
});
|
||||
@@ -1,9 +1,6 @@
|
||||
# Copyright (c) HashiCorp, Inc.
|
||||
# SPDX-License-Identifier: BUSL-1.1
|
||||
|
||||
STORYBOOK_LINK=true ember build
|
||||
ember build
|
||||
mkdir -p ui-dist/ui
|
||||
mv dist/* ui-dist/ui/
|
||||
|
||||
yarn build-storybook
|
||||
mv storybook-static ui-dist/storybook/
|
||||
|
||||
@@ -78,12 +78,6 @@ Nomad UI releases are in lockstep with Nomad releases and are integrated into th
|
||||
|
||||
- UI branches should be prefix with `f-ui-` for feature work and `b-ui-` for bug fixes. This instructs CI to skip running nomad backend tests.
|
||||
|
||||
### Storybook UI Library
|
||||
|
||||
The Storybook project provides a browser to see what components and patterns are present in the application and how to use them. You can run it locally with `yarn storybook` after you have `ember serve` running. The latest version from the `main` branch is at [`nomad-storybook-and-ui.vercel.app/storybook/`](https://nomad-storybook-and-ui.vercel.app/storybook/).
|
||||
|
||||
To generate a new story for a component, run `ember generate story component-name`. You can use the existing stories as a guide.
|
||||
|
||||
### Troubleshooting
|
||||
|
||||
#### The UI is running, but none of the API requests are working
|
||||
|
||||
@@ -1,23 +0,0 @@
|
||||
/**
|
||||
* Copyright (c) HashiCorp, Inc.
|
||||
* SPDX-License-Identifier: BUSL-1.1
|
||||
*/
|
||||
|
||||
import { helper } from '@ember/component/helper';
|
||||
|
||||
// Generated at compile-time by ember-inline-svg
|
||||
import SVGs from '../svgs';
|
||||
|
||||
/**
|
||||
* Icons array
|
||||
*
|
||||
* Usage: {{#each (all-icons) as |icon|}}
|
||||
*
|
||||
* Returns the array of all icon strings available to {{x-icon}}. This is a bit of a hack
|
||||
* since the above SVGs import isn't available in the Storybook context.
|
||||
*/
|
||||
export function allIcons() {
|
||||
return Object.keys(SVGs);
|
||||
}
|
||||
|
||||
export default helper(allIcons);
|
||||
@@ -10,6 +10,3 @@
|
||||
@import './components';
|
||||
@import '@hashicorp/design-system-components';
|
||||
@import './charts';
|
||||
|
||||
// Only necessary in dev
|
||||
@import './storybook.scss';
|
||||
|
||||
@@ -1,199 +0,0 @@
|
||||
/**
|
||||
* Copyright (c) HashiCorp, Inc.
|
||||
* SPDX-License-Identifier: BUSL-1.1
|
||||
*/
|
||||
|
||||
#storybook {
|
||||
.mock-content {
|
||||
display: flex;
|
||||
min-height: 250px;
|
||||
height: 100%;
|
||||
|
||||
.mock-image,
|
||||
.mock-copy {
|
||||
min-height: 100%;
|
||||
width: 100%;
|
||||
margin: 1em;
|
||||
}
|
||||
|
||||
.mock-image {
|
||||
background: linear-gradient(
|
||||
to top right,
|
||||
transparent 0%,
|
||||
transparent 49%,
|
||||
$grey-blue 49%,
|
||||
$grey-blue 51%,
|
||||
transparent 51%,
|
||||
transparent 100%
|
||||
),
|
||||
linear-gradient(
|
||||
to bottom right,
|
||||
transparent 0%,
|
||||
transparent 49%,
|
||||
$grey-blue 49%,
|
||||
$grey-blue 51%,
|
||||
transparent 51%,
|
||||
transparent 100%
|
||||
);
|
||||
}
|
||||
|
||||
.mock-copy {
|
||||
background: repeating-linear-gradient(
|
||||
to bottom,
|
||||
$grey-blue,
|
||||
$grey-blue 5px,
|
||||
transparent 5px,
|
||||
transparent 14px
|
||||
);
|
||||
}
|
||||
|
||||
.mock-vague {
|
||||
background: lighten($grey-blue, 15%);
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
}
|
||||
}
|
||||
|
||||
.mock-spacing {
|
||||
padding: 2em;
|
||||
}
|
||||
|
||||
.annotation {
|
||||
padding: 1rem 0;
|
||||
font-size: 0.9rem;
|
||||
}
|
||||
|
||||
.palette {
|
||||
.title {
|
||||
font-size: 1.4rem;
|
||||
font-weight: $weight-bold;
|
||||
padding-bottom: 2px;
|
||||
padding-top: 10px;
|
||||
}
|
||||
|
||||
.description {
|
||||
font-size: 0.8rem;
|
||||
padding-bottom: 5px;
|
||||
}
|
||||
|
||||
.item {
|
||||
border: solid 1px $grey-blue;
|
||||
display: inline-block;
|
||||
margin: 0 5px 5px 0;
|
||||
|
||||
.color {
|
||||
height: 90px;
|
||||
width: 160px;
|
||||
}
|
||||
|
||||
.info {
|
||||
background-color: white;
|
||||
border-top: solid 1px $grey-blue;
|
||||
padding: 5px;
|
||||
}
|
||||
|
||||
.hex {
|
||||
font-size: 12px;
|
||||
font-weight: $weight-bold;
|
||||
margin-bottom: 0;
|
||||
}
|
||||
|
||||
.name {
|
||||
color: $ui-gray-500;
|
||||
font-size: 11px;
|
||||
margin-top: 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.typeface {
|
||||
.hero,
|
||||
.sample {
|
||||
font-family: inherit;
|
||||
}
|
||||
|
||||
.hero {
|
||||
font-size: 140px;
|
||||
line-height: 1.05;
|
||||
}
|
||||
|
||||
.sample {
|
||||
font-size: 15px;
|
||||
margin: 0;
|
||||
}
|
||||
}
|
||||
|
||||
.multiples {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
|
||||
&.with-spacing {
|
||||
> * {
|
||||
margin-right: 1em;
|
||||
margin-bottom: 1em;
|
||||
}
|
||||
}
|
||||
|
||||
&.is-left-aligned {
|
||||
justify-content: flex-start;
|
||||
}
|
||||
}
|
||||
|
||||
.chart-container {
|
||||
width: 200px;
|
||||
padding: 15px;
|
||||
border: 1px solid $ui-gray-200;
|
||||
display: inline-block;
|
||||
|
||||
&.is-small {
|
||||
width: 150px;
|
||||
}
|
||||
|
||||
&.is-large {
|
||||
width: 250px;
|
||||
}
|
||||
|
||||
&.is-xlarge {
|
||||
width: 300px;
|
||||
}
|
||||
}
|
||||
|
||||
.tile-list {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
|
||||
.icon-tile {
|
||||
width: 240px;
|
||||
padding: 10px;
|
||||
margin: 10px;
|
||||
border: 1px solid $grey-lighter;
|
||||
border-radius: $radius;
|
||||
svg {
|
||||
margin: auto;
|
||||
width: 30px;
|
||||
height: 30;
|
||||
fill: $grey;
|
||||
color: $grey;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.mock-hover-region {
|
||||
width: 200px;
|
||||
height: 100px;
|
||||
position: relative;
|
||||
border-radius: $radius;
|
||||
margin: 1em 0;
|
||||
padding: 1em;
|
||||
border: 1px solid $grey-blue;
|
||||
background: $white-ter;
|
||||
color: $grey;
|
||||
font-weight: $weight-bold;
|
||||
}
|
||||
|
||||
.title:not(:first-child) {
|
||||
margin-top: 2em;
|
||||
}
|
||||
}
|
||||
@@ -30,16 +30,6 @@
|
||||
{{/unless}}
|
||||
{{/if}}
|
||||
<div class="navbar-end">
|
||||
{{#if this.config.APP.showStorybookLink}}
|
||||
<a
|
||||
href="/storybook/"
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
class="navbar-item"
|
||||
>
|
||||
Storybook
|
||||
</a>
|
||||
{{/if}}
|
||||
{{#if this.system.agent.config.UI.Consul.BaseUIURL}}
|
||||
<a
|
||||
data-test-header-consul-link
|
||||
@@ -82,4 +72,4 @@
|
||||
{{yield}}
|
||||
</ul>
|
||||
</nav>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -1,20 +0,0 @@
|
||||
/**
|
||||
* Copyright (c) HashiCorp, Inc.
|
||||
* SPDX-License-Identifier: BUSL-1.1
|
||||
*/
|
||||
|
||||
import hbs from 'htmlbars-inline-precompile';
|
||||
|
||||
export default {
|
||||
title: 'Components|<%= classifiedModuleName %>',
|
||||
};
|
||||
|
||||
export let <%= classifiedModuleName %> = () => {
|
||||
return {
|
||||
template: hbs`
|
||||
<h5 class="title is-5"><%= header %></h5>
|
||||
<<%= classifiedModuleName %>/>
|
||||
`,
|
||||
context: {},
|
||||
}
|
||||
};
|
||||
@@ -1,42 +0,0 @@
|
||||
/**
|
||||
* Copyright (c) HashiCorp, Inc.
|
||||
* SPDX-License-Identifier: BUSL-1.1
|
||||
*/
|
||||
|
||||
/* eslint-disable ember/no-string-prototype-extensions */
|
||||
const getPathOption = require('ember-cli-get-component-path-option');
|
||||
const stringUtil = require('ember-cli-string-utils');
|
||||
const path = require('path');
|
||||
|
||||
module.exports = {
|
||||
description: 'generates a story for storybook',
|
||||
|
||||
fileMapTokens: function () {
|
||||
let { project } = this;
|
||||
return {
|
||||
__path__: function () {
|
||||
return path.relative(project.root, project.root);
|
||||
},
|
||||
__markdownname__: function (options) {
|
||||
return options.dasherizedModuleName;
|
||||
},
|
||||
__name__: function (options) {
|
||||
return options.dasherizedModuleName;
|
||||
},
|
||||
};
|
||||
},
|
||||
|
||||
locals: function (options) {
|
||||
let contents = '';
|
||||
|
||||
return {
|
||||
contents: contents,
|
||||
path: getPathOption(options),
|
||||
header: stringUtil
|
||||
.dasherize(options.entity.name)
|
||||
.split('-')
|
||||
.map((word) => stringUtil.capitalize(word))
|
||||
.join(' '),
|
||||
};
|
||||
},
|
||||
};
|
||||
@@ -40,7 +40,6 @@ module.exports = function (environment) {
|
||||
mirageWithNamespaces: true,
|
||||
mirageWithTokens: true,
|
||||
mirageWithRegions: true,
|
||||
showStorybookLink: process.env.STORYBOOK_LINK === 'true',
|
||||
},
|
||||
|
||||
percy: {
|
||||
@@ -59,11 +58,6 @@ module.exports = function (environment) {
|
||||
enabled: USE_MIRAGE,
|
||||
excludeFilesFromBuild: !USE_MIRAGE,
|
||||
};
|
||||
|
||||
if (process.env.STORYBOOK === 'true') {
|
||||
ENV.APP.autoboot = false;
|
||||
ENV.rootURL = '/';
|
||||
}
|
||||
}
|
||||
|
||||
if (environment === 'test') {
|
||||
|
||||
@@ -17,8 +17,6 @@
|
||||
"lint:js": "eslint . --cache",
|
||||
"lint:js:fix": "eslint . --fix",
|
||||
"start": "ember server",
|
||||
"build-storybook": "STORYBOOK=true ember build && NODE_OPTIONS=--openssl-legacy-provider build-storybook -s dist",
|
||||
"storybook": "NODE_OPTIONS=--openssl-legacy-provider STORYBOOK=true start-storybook -p 6006 -s dist",
|
||||
"test": "npm-run-all lint test:*",
|
||||
"exam": "percy exec -- ember exam --split=4 --parallel",
|
||||
"exam:parallel": "percy exec --parallel -- ember exam",
|
||||
@@ -34,7 +32,7 @@
|
||||
}
|
||||
},
|
||||
"lint-staged": {
|
||||
"{app,tests,config,lib,mirage,stories}/**/*.js": [
|
||||
"{app,tests,config,lib,mirage}/**/*.js": [
|
||||
"prettier --write"
|
||||
],
|
||||
"app/styles/**/*.*": [
|
||||
@@ -50,7 +48,6 @@
|
||||
"@glimmer/component": "^1.0.4",
|
||||
"@glimmer/tracking": "^1.0.4",
|
||||
"@hashicorp/structure-icons": "^1.3.0",
|
||||
"@storybook/ember-cli-storybook": "^0.6.1",
|
||||
"anser": "^2.1.1",
|
||||
"babel-eslint": "^10.1.0",
|
||||
"base64-js": "^1.3.1",
|
||||
@@ -153,12 +150,6 @@
|
||||
},
|
||||
"optionalDependencies": {
|
||||
"@babel/plugin-transform-member-expression-literals": "^7.16.7",
|
||||
"@storybook/addon-docs": "^6.3.20",
|
||||
"@storybook/addon-knobs": "^6.3.1",
|
||||
"@storybook/addon-storysource": "^6.3.10",
|
||||
"@storybook/addon-viewport": "^6.3.10",
|
||||
"@storybook/addons": "^6.3.10",
|
||||
"@storybook/ember": "6.5.14",
|
||||
"babel-loader": "^8.0.6",
|
||||
"ember-cli-get-component-path-option": "^1.0.0",
|
||||
"ember-cli-string-utils": "^1.1.0"
|
||||
|
||||
@@ -1,179 +0,0 @@
|
||||
/**
|
||||
* Copyright (c) HashiCorp, Inc.
|
||||
* SPDX-License-Identifier: BUSL-1.1
|
||||
*/
|
||||
|
||||
import hbs from 'htmlbars-inline-precompile';
|
||||
|
||||
import EmberObject, { computed } from '@ember/object';
|
||||
import { on } from '@ember/object/evented';
|
||||
|
||||
import DelayedTruth from '../utils/delayed-truth';
|
||||
|
||||
export default {
|
||||
title: 'Charts/Distribution Bar',
|
||||
};
|
||||
|
||||
export let Standard = () => {
|
||||
return {
|
||||
template: hbs`
|
||||
<h5 class="title is-5">Distribution Bar</h5>
|
||||
<div class="block" style="height:50px; width:200px;">
|
||||
{{#if delayedTruth.complete}}
|
||||
<DistributionBar @data={{distributionBarData}} />
|
||||
{{/if}}
|
||||
</div>
|
||||
<p class="annotation">The distribution bar chart proportionally show data in a single bar. It includes a tooltip out of the box, assumes the size of the container element, and is designed to be styled with CSS.</p>
|
||||
`,
|
||||
context: {
|
||||
delayedTruth: DelayedTruth.create(),
|
||||
distributionBarData: [
|
||||
{ label: 'one', value: 10 },
|
||||
{ label: 'two', value: 20 },
|
||||
{ label: 'three', value: 30 },
|
||||
],
|
||||
},
|
||||
};
|
||||
};
|
||||
|
||||
export let WithClasses = () => {
|
||||
return {
|
||||
template: hbs`
|
||||
<h5 class="title is-5">Distribution Bar with classes</h5>
|
||||
<div class="block" style="height:50px; width:200px;">
|
||||
{{#if delayedTruth.complete}}
|
||||
<DistributionBar @data={{distributionBarDataWithClasses}} />
|
||||
{{/if}}
|
||||
</div>
|
||||
<p class="annotation">If a datum provides a <code>className</code> property, it will be assigned to the corresponding <code>rect</code> element, allowing for custom colorization.</p>
|
||||
`,
|
||||
context: {
|
||||
delayedTruth: DelayedTruth.create(),
|
||||
distributionBarDataWithClasses: [
|
||||
{ label: 'Queued', value: 10, className: 'queued' },
|
||||
{ label: 'Complete', value: 20, className: 'complete' },
|
||||
{ label: 'Failed', value: 30, className: 'failed' },
|
||||
],
|
||||
},
|
||||
};
|
||||
};
|
||||
|
||||
export let Flexibility = () => {
|
||||
return {
|
||||
template: hbs`
|
||||
<h5 class="title is-5">Distribution Bar flexibility</h5>
|
||||
<div class="block" style="height:10px; width:600px;">
|
||||
{{#if delayedTruth.complete}}
|
||||
<DistributionBar @data={{distributionBarData}} />
|
||||
{{/if}}
|
||||
</div>
|
||||
<div class="block" style="height:200px; width:30px;">
|
||||
{{#if delayedTruth.complete}}
|
||||
<DistributionBar @data={{distributionBarData}} />
|
||||
{{/if}}
|
||||
</div>
|
||||
<p class="annotation">Distribution bar assumes the dimensions of the container.</p>
|
||||
`,
|
||||
context: {
|
||||
delayedTruth: DelayedTruth.create(),
|
||||
distributionBarData: [
|
||||
{ label: 'one', value: 10 },
|
||||
{ label: 'two', value: 20 },
|
||||
{ label: 'three', value: 30 },
|
||||
],
|
||||
},
|
||||
};
|
||||
};
|
||||
|
||||
export let LiveUpdating = () => {
|
||||
return {
|
||||
template: hbs`
|
||||
<h5 class="title is-5">Live-updating Distribution Bar</h5>
|
||||
<div class="block" style="height:50px; width:600px;">
|
||||
<DistributionBar @data={{controller.distributionBarDataRotating}} />
|
||||
</div>
|
||||
<p class="annotation">Distribution bar animates with data changes.</p>
|
||||
<div class="boxed-section">
|
||||
<div class="boxed-section-body is-dark">
|
||||
<JsonViewer @json={{controller.distributionBarDataRotating}} />
|
||||
</div>
|
||||
</div>
|
||||
`,
|
||||
context: {
|
||||
controller: EmberObject.extend({
|
||||
timerTicks: 0,
|
||||
|
||||
startTimer: on('init', function () {
|
||||
this.set(
|
||||
'timer',
|
||||
setInterval(() => {
|
||||
this.incrementProperty('timerTicks');
|
||||
}, 500)
|
||||
);
|
||||
}),
|
||||
|
||||
willDestroy() {
|
||||
clearInterval(this.timer);
|
||||
},
|
||||
|
||||
distributionBarDataRotating: computed('timerTicks', function () {
|
||||
return [
|
||||
{ label: 'one', value: Math.round(Math.random() * 50) },
|
||||
{ label: 'two', value: Math.round(Math.random() * 50) },
|
||||
{ label: 'three', value: Math.round(Math.random() * 50) },
|
||||
];
|
||||
}),
|
||||
}).create(),
|
||||
},
|
||||
};
|
||||
};
|
||||
|
||||
export let SingleBar = () => {
|
||||
return {
|
||||
template: hbs`
|
||||
<h5 class="title is-5">Distribution Bar with single bar</h5>
|
||||
<div class="block" style="height:50px; width:600px;">
|
||||
{{#if delayedTruth.complete}}
|
||||
<DistributionBar @data={{distributionBarDatum}} />
|
||||
{{/if}}
|
||||
</div>
|
||||
`,
|
||||
context: {
|
||||
delayedTruth: DelayedTruth.create(),
|
||||
distributionBarDatum: [{ label: 'one', value: 10 }],
|
||||
},
|
||||
};
|
||||
};
|
||||
|
||||
export let Jumbo = () => {
|
||||
return {
|
||||
template: hbs`
|
||||
<h5 class="title is-5">Jumbo Distribution Bar</h5>
|
||||
{{#if delayedTruth.complete}}
|
||||
<DistributionBar @data={{distributionBarData}} @class="split-view" as |chart|>
|
||||
<ol class="legend">
|
||||
{{#each chart.data as |datum index|}}
|
||||
<li class="{{datum.className}} {{if (eq datum.index chart.activeDatum.index) "is-active"}} {{if (eq datum.value 0) "is-empty"}}">
|
||||
<span class="color-swatch {{if datum.className datum.className (concat "swatch-" index)}}" />
|
||||
<span class="value" data-test-legend-value="{{datum.className}}">{{datum.value}}</span>
|
||||
<span class="label">
|
||||
{{datum.label}}
|
||||
</span>
|
||||
</li>
|
||||
{{/each}}
|
||||
</ol>
|
||||
</DistributionBar>
|
||||
{{/if}}
|
||||
<p class="annotation">A variation of the Distribution Bar component for when the distribution bar is the central component of the page. It's a larger format that requires no interaction to see the data labels and values.</p>
|
||||
`,
|
||||
context: {
|
||||
delayedTruth: DelayedTruth.create(),
|
||||
distributionBarData: [
|
||||
{ label: 'one', value: 10 },
|
||||
{ label: 'two', value: 20 },
|
||||
{ label: 'three', value: 0 },
|
||||
{ label: 'four', value: 35 },
|
||||
],
|
||||
},
|
||||
};
|
||||
};
|
||||
@@ -1,120 +0,0 @@
|
||||
/**
|
||||
* Copyright (c) HashiCorp, Inc.
|
||||
* SPDX-License-Identifier: BUSL-1.1
|
||||
*/
|
||||
|
||||
import hbs from 'htmlbars-inline-precompile';
|
||||
import DelayedArray from '../utils/delayed-array';
|
||||
import DelayedTruth from '../utils/delayed-truth';
|
||||
|
||||
export default {
|
||||
title: 'Charts/Gauge Chart',
|
||||
};
|
||||
|
||||
let totalVariations = [
|
||||
{ value: 0, total: 10 },
|
||||
{ value: 1, total: 10 },
|
||||
{ value: 2, total: 10 },
|
||||
{ value: 3, total: 10 },
|
||||
{ value: 4, total: 10 },
|
||||
{ value: 5, total: 10 },
|
||||
{ value: 6, total: 10 },
|
||||
{ value: 7, total: 10 },
|
||||
{ value: 8, total: 10 },
|
||||
{ value: 9, total: 10 },
|
||||
{ value: 10, total: 10 },
|
||||
];
|
||||
|
||||
let complementVariations = [
|
||||
{ value: 0, complement: 10 },
|
||||
{ value: 1, complement: 9 },
|
||||
{ value: 2, complement: 8 },
|
||||
{ value: 3, complement: 7 },
|
||||
{ value: 4, complement: 6 },
|
||||
{ value: 5, complement: 5 },
|
||||
{ value: 6, complement: 4 },
|
||||
{ value: 7, complement: 3 },
|
||||
{ value: 8, complement: 2 },
|
||||
{ value: 9, complement: 1 },
|
||||
{ value: 10, complement: 0 },
|
||||
];
|
||||
|
||||
let colorVariations = ['is-info', 'is-warning', 'is-success', 'is-danger'];
|
||||
|
||||
export let Total = () => {
|
||||
return {
|
||||
template: hbs`
|
||||
<div class="multiples">
|
||||
{{#each variations as |v|}}
|
||||
<div class="chart-container">
|
||||
<GaugeChart @value={{v.value}} @total={{v.total}} @label="Total" @chartClass="is-info" />
|
||||
</div>
|
||||
{{/each}}
|
||||
</div>
|
||||
`,
|
||||
context: {
|
||||
variations: DelayedArray.create(totalVariations),
|
||||
},
|
||||
};
|
||||
};
|
||||
|
||||
export let Complement = () => {
|
||||
return {
|
||||
template: hbs`
|
||||
<div class="multiples">
|
||||
{{#each variations as |v|}}
|
||||
<div class="chart-container">
|
||||
<GaugeChart @value={{v.value}} @complement={{v.complement}} @label="Complement" @chartClass="is-info" />
|
||||
</div>
|
||||
{{/each}}
|
||||
</div>
|
||||
`,
|
||||
context: {
|
||||
variations: DelayedArray.create(complementVariations),
|
||||
},
|
||||
};
|
||||
};
|
||||
|
||||
export let Colors = () => {
|
||||
return {
|
||||
template: hbs`
|
||||
<div class="multiples">
|
||||
{{#each variations as |color|}}
|
||||
<div class="chart-container">
|
||||
<GaugeChart @value={{7}} @total={{10}} @label={{color}} @chartClass={{color}} />
|
||||
</div>
|
||||
{{/each}}
|
||||
</div>
|
||||
`,
|
||||
context: {
|
||||
variations: DelayedArray.create(colorVariations),
|
||||
},
|
||||
};
|
||||
};
|
||||
|
||||
export let Sizing = () => {
|
||||
return {
|
||||
template: hbs`
|
||||
{{#if delayedTruth.complete}}
|
||||
<div class="multiples">
|
||||
<div class="chart-container is-small">
|
||||
<GaugeChart @value={{7}} @total={{10}} @label="Small" />
|
||||
</div>
|
||||
<div class="chart-container">
|
||||
<GaugeChart @value={{7}} @total={{10}} @label="Regular" />
|
||||
</div>
|
||||
<div class="chart-container is-large">
|
||||
<GaugeChart @value={{7}} @total={{10}} @label="Large" />
|
||||
</div>
|
||||
<div class="chart-container is-xlarge">
|
||||
<GaugeChart @value={{7}} @total={{10}} @label="X-Large" />
|
||||
</div>
|
||||
</div>
|
||||
{{/if}}
|
||||
<p class="annotation">GaugeCharts fill the width of their container and have a dynamic height according to the height of the arc. However, the text within a gauge chart is fixed. This can create unsightly overlap or whitespace, so be careful about responsiveness when using this chart type.</p>
|
||||
`,
|
||||
context: {
|
||||
delayedTruth: DelayedTruth.create(),
|
||||
},
|
||||
};
|
||||
};
|
||||
@@ -1,463 +0,0 @@
|
||||
/**
|
||||
* Copyright (c) HashiCorp, Inc.
|
||||
* SPDX-License-Identifier: BUSL-1.1
|
||||
*/
|
||||
|
||||
import hbs from 'htmlbars-inline-precompile';
|
||||
|
||||
import EmberObject from '@ember/object';
|
||||
import { on } from '@ember/object/evented';
|
||||
import moment from 'moment';
|
||||
|
||||
import DelayedArray from '../utils/delayed-array';
|
||||
|
||||
export default {
|
||||
title: 'Charts/Line Chart',
|
||||
};
|
||||
|
||||
let data1 = [
|
||||
{ year: 2010, value: 10 },
|
||||
{ year: 2011, value: 10 },
|
||||
{ year: 2012, value: 20 },
|
||||
{ year: 2013, value: 30 },
|
||||
{ year: 2014, value: 50 },
|
||||
{ year: 2015, value: 80 },
|
||||
{ year: 2016, value: 130 },
|
||||
{ year: 2017, value: 210 },
|
||||
{ year: 2018, value: 340 },
|
||||
];
|
||||
|
||||
let data2 = [
|
||||
{ year: 2010, value: 100 },
|
||||
{ year: 2011, value: 90 },
|
||||
{ year: 2012, value: 120 },
|
||||
{ year: 2013, value: 130 },
|
||||
{ year: 2014, value: 115 },
|
||||
{ year: 2015, value: 105 },
|
||||
{ year: 2016, value: 90 },
|
||||
{ year: 2017, value: 85 },
|
||||
{ year: 2018, value: 90 },
|
||||
];
|
||||
|
||||
export let Standard = () => {
|
||||
return {
|
||||
template: hbs`
|
||||
<h5 class="title is-5">Line Chart</h5>
|
||||
<div class="block" style="height:100px; width: 400px;">
|
||||
{{#if this.lineChartData}}
|
||||
<LineChart @data={{this.lineChartData}} @xProp="year" @yProp="value">
|
||||
<:svg as |c|>
|
||||
<c.Area @data={{this.lineChartData}} />
|
||||
</:svg>
|
||||
<:after as |c|>
|
||||
<c.Tooltip class="is-snappy" as |series datum|>
|
||||
<li>
|
||||
<span class="label"><span class="color-swatch is-primary" />{{datum.formattedX}}</span>
|
||||
<span class="value">{{datum.formattedY}}</span>
|
||||
</li>
|
||||
</c.Tooltip>
|
||||
</:after>
|
||||
</LineChart>
|
||||
{{/if}}
|
||||
</div>
|
||||
<div class="block" style="height:100px; width: 400px;">
|
||||
{{#if this.lineChartMild}}
|
||||
<LineChart @data={{this.lineChartMild}} @xProp="year" @yProp="value">
|
||||
<:svg as |c|>
|
||||
<c.Area @data={{this.lineChartMild}} @colorClass="is-info" />
|
||||
</:svg>
|
||||
<:after as |c|>
|
||||
<c.Tooltip class="is-snappy" as |series datum|>
|
||||
<li>
|
||||
<span class="label"><span class="color-swatch is-info" />{{datum.formattedX}}</span>
|
||||
<span class="value">{{datum.formattedY}}</span>
|
||||
</li>
|
||||
</c.Tooltip>
|
||||
</:after>
|
||||
</LineChart>
|
||||
{{/if}}
|
||||
</div>
|
||||
`,
|
||||
context: {
|
||||
lineChartData: DelayedArray.create(data1),
|
||||
lineChartMild: DelayedArray.create(data2),
|
||||
},
|
||||
};
|
||||
};
|
||||
|
||||
export let FluidWidth = () => {
|
||||
return {
|
||||
template: hbs`
|
||||
<h5 class="title is-5">Fluid-width Line Chart</h5>
|
||||
<div class="block" style="height:250px;">
|
||||
{{#if this.lineChartData}}
|
||||
<LineChart @data={{this.lineChartData}} @xProp="year" @yProp="value">
|
||||
<:svg as |c|>
|
||||
<c.Area @data={{this.lineChartData}} @colorClass="is-danger" />
|
||||
</:svg>
|
||||
<:after as |c|>
|
||||
<c.Tooltip class="is-snappy" as |series datum|>
|
||||
<li>
|
||||
<span class="label"><span class="color-swatch is-danger" />{{datum.formattedX}}</span>
|
||||
<span class="value">{{datum.formattedY}}</span>
|
||||
</li>
|
||||
</c.Tooltip>
|
||||
</:after>
|
||||
</LineChart>
|
||||
{{/if}}
|
||||
</div>
|
||||
<div class="block" style="height:250px;">
|
||||
{{#if this.lineChartMild}}
|
||||
<LineChart @data={{this.lineChartMild}} @xProp="year" @yProp="value">
|
||||
<:svg as |c|>
|
||||
<c.Area @data={{this.lineChartMild}} @colorClass="is-warning" />
|
||||
</:svg>
|
||||
<:after as |c|>
|
||||
<c.Tooltip class="is-snappy" as |series datum|>
|
||||
<li>
|
||||
<span class="label"><span class="color-swatch is-warning" />{{datum.formattedX}}</span>
|
||||
<span class="value">{{datum.formattedY}}</span>
|
||||
</li>
|
||||
</c.Tooltip>
|
||||
</:after>
|
||||
</LineChart>
|
||||
{{/if}}
|
||||
</div>
|
||||
<p class="annotation">A line chart will assume the width of its container. This includes the dimensions of the axes, which are calculated based on real DOM measurements. This requires a two-pass render: first the axes are placed with their real domains (in order to capture width and height of tick labels), second the axes are adjusted to make sure both the x and y axes are within the height and width bounds of the container.</p>
|
||||
`,
|
||||
context: {
|
||||
lineChartData: DelayedArray.create(data1),
|
||||
lineChartMild: DelayedArray.create(data2),
|
||||
},
|
||||
};
|
||||
};
|
||||
|
||||
export let LiveData = () => {
|
||||
return {
|
||||
template: hbs`
|
||||
<h5 class="title is-5">Live data Line Chart</h5>
|
||||
<div class="block" style="height:250px">
|
||||
{{#if this.controller.lineChartLive}}
|
||||
<LineChart
|
||||
@data={{this.controller.lineChartLive}}
|
||||
@xProp="ts"
|
||||
@yProp="val"
|
||||
@timeseries={{true}}
|
||||
@xFormat={{this.controller.secondsFormat}}>
|
||||
<:svg as |c|>
|
||||
<c.Area @data={{this.controller.lineChartLive}} />
|
||||
</:svg>
|
||||
</LineChart>
|
||||
{{/if}}
|
||||
</div>
|
||||
`,
|
||||
context: {
|
||||
controller: EmberObject.extend({
|
||||
startTimer: on('init', function () {
|
||||
this.lineChartLive = [];
|
||||
|
||||
this.set(
|
||||
'timer',
|
||||
setInterval(() => {
|
||||
this.incrementProperty('timerTicks');
|
||||
|
||||
let ref = this.lineChartLive;
|
||||
ref.addObject({ ts: Date.now(), val: Math.random() * 30 + 20 });
|
||||
if (ref.length > 60) {
|
||||
ref.splice(0, ref.length - 60);
|
||||
}
|
||||
}, 500)
|
||||
);
|
||||
}),
|
||||
|
||||
willDestroy() {
|
||||
clearInterval(this.timer);
|
||||
},
|
||||
|
||||
get secondsFormat() {
|
||||
return (date) => moment(date).format('HH:mm:ss');
|
||||
},
|
||||
}).create(),
|
||||
},
|
||||
};
|
||||
};
|
||||
|
||||
export let Gaps = () => {
|
||||
return {
|
||||
template: hbs`
|
||||
<h5 class="title is-5">Line Chart data with gaps</h5>
|
||||
<div class="block" style="height:250px">
|
||||
{{#if this.lineChartGapData}}
|
||||
<LineChart @data={{this.lineChartGapData}} @xProp="year" @yProp="value">
|
||||
<:svg as |c|>
|
||||
<c.Area @data={{this.lineChartGapData}} />
|
||||
</:svg>
|
||||
<:after as |c|>
|
||||
<c.Tooltip class="is-snappy" as |series datum|>
|
||||
<li>
|
||||
<span class="label"><span class="color-swatch is-primary" />{{datum.formattedX}}</span>
|
||||
<span class="value">{{datum.formattedY}}</span>
|
||||
</li>
|
||||
</c.Tooltip>
|
||||
</:after>
|
||||
</LineChart>
|
||||
{{/if}}
|
||||
</div>
|
||||
`,
|
||||
context: {
|
||||
lineChartGapData: DelayedArray.create([
|
||||
{ year: 2010, value: 10 },
|
||||
{ year: 2011, value: 10 },
|
||||
{ year: 2012, value: null },
|
||||
{ year: 2013, value: 30 },
|
||||
{ year: 2014, value: 50 },
|
||||
{ year: 2015, value: 80 },
|
||||
{ year: 2016, value: null },
|
||||
{ year: 2017, value: 210 },
|
||||
{ year: 2018, value: 340 },
|
||||
]),
|
||||
},
|
||||
};
|
||||
};
|
||||
|
||||
export let VerticalAnnotations = () => {
|
||||
return {
|
||||
template: hbs`
|
||||
<h5 class="title is-5">Line Chart data with annotations</h5>
|
||||
<div class="block" style="height:250px">
|
||||
{{#if (and this.data this.annotations)}}
|
||||
<LineChart
|
||||
class="with-annotations"
|
||||
@timeseries={{true}}
|
||||
@xProp="x"
|
||||
@yProp="y"
|
||||
@data={{this.data}}>
|
||||
<:svg as |c|>
|
||||
<c.Area @data={{this.data}} @annotationClick={{action (mut this.activeAnnotation)}} />
|
||||
</:svg>
|
||||
<:after as |c|>
|
||||
<c.VAnnotations
|
||||
@annotations={{this.annotations}}
|
||||
@annotationClick={{action (mut this.activeAnnotation)}}
|
||||
@activeAnnotation={{this.activeAnnotation}} />
|
||||
</:after>
|
||||
</LineChart>
|
||||
{{/if}}
|
||||
</div>
|
||||
<p style="margin:2em 0; padding: 1em; background:#FFEEAC">{{this.activeAnnotation.info}}</p>
|
||||
<h5 class="title is-5">Line Chart data with staggered annotations</h5>
|
||||
<div class="block" style="height:150px; width:450px">
|
||||
{{#if (and this.data this.annotations)}}
|
||||
<LineChart
|
||||
class="with-annotations"
|
||||
@timeseries={{true}}
|
||||
@xProp="x"
|
||||
@yProp="y"
|
||||
@data={{this.data}}>
|
||||
<:svg as |c|>
|
||||
<c.Area @data={{this.data}} @annotationClick={{action (mut this.activeAnnotation)}} />
|
||||
</:svg>
|
||||
<:after as |c|>
|
||||
<c.VAnnotations
|
||||
@annotations={{this.annotations}}
|
||||
@annotationClick={{action (mut this.activeAnnotation)}}
|
||||
@activeAnnotation={{this.activeAnnotation}} />
|
||||
<c.Tooltip class="is-snappy" as |series datum|>
|
||||
<li>
|
||||
<span class="label"><span class="color-swatch is-primary" />{{datum.formattedX}}</span>
|
||||
<span class="value">{{datum.formattedY}}</span>
|
||||
</li>
|
||||
</c.Tooltip>
|
||||
</:after>
|
||||
</LineChart>
|
||||
{{/if}}
|
||||
</div>
|
||||
`,
|
||||
context: {
|
||||
data: DelayedArray.create(
|
||||
new Array(180).fill(null).map((_, idx) => ({
|
||||
y: Math.sin((idx * 4 * Math.PI) / 180) * 100 + 200,
|
||||
x: moment().add(idx, 'd').toDate(),
|
||||
}))
|
||||
),
|
||||
annotations: [
|
||||
{
|
||||
x: moment().toDate(),
|
||||
type: 'info',
|
||||
info: 'Far left',
|
||||
},
|
||||
{
|
||||
x: moment()
|
||||
.add(90 / 4, 'd')
|
||||
.toDate(),
|
||||
type: 'error',
|
||||
info: 'This is the max of the sine curve',
|
||||
},
|
||||
{
|
||||
x: moment().add(89, 'd').toDate(),
|
||||
type: 'info',
|
||||
info: 'This is the end of the first period',
|
||||
},
|
||||
{
|
||||
x: moment().add(96, 'd').toDate(),
|
||||
type: 'info',
|
||||
info: 'A close annotation for staggering purposes',
|
||||
},
|
||||
{
|
||||
x: moment()
|
||||
.add((90 / 4) * 3, 'd')
|
||||
.toDate(),
|
||||
type: 'error',
|
||||
info: 'This is the min of the sine curve',
|
||||
},
|
||||
{
|
||||
x: moment().add(179, 'd').toDate(),
|
||||
type: 'info',
|
||||
info: 'Far right',
|
||||
},
|
||||
],
|
||||
},
|
||||
};
|
||||
};
|
||||
|
||||
export let HorizontalAnnotations = () => {
|
||||
return {
|
||||
template: hbs`
|
||||
<div class="block" style="height:250px">
|
||||
{{#if (and this.data this.annotations)}}
|
||||
<LineChart
|
||||
class="with-annotations"
|
||||
@timeseries={{true}}
|
||||
@xProp="x"
|
||||
@yProp="y"
|
||||
@data={{this.data}}>
|
||||
<:svg as |c|>
|
||||
<c.Area @data={{this.data}} @annotationClick={{action (mut this.activeAnnotation)}} />
|
||||
</:svg>
|
||||
<:after as |c|>
|
||||
<c.HAnnotations @annotations={{this.annotations}} @labelProp="info" />
|
||||
</:after>
|
||||
</LineChart>
|
||||
{{/if}}
|
||||
</div>
|
||||
`,
|
||||
context: {
|
||||
data: DelayedArray.create(
|
||||
new Array(180).fill(null).map((_, idx) => ({
|
||||
y: Math.sin((idx * 4 * Math.PI) / 180) * 100 + 200,
|
||||
x: moment().add(idx, 'd').toDate(),
|
||||
}))
|
||||
),
|
||||
annotations: [
|
||||
{
|
||||
y: 300,
|
||||
info: 'High',
|
||||
},
|
||||
{
|
||||
y: 100,
|
||||
info: 'Low',
|
||||
},
|
||||
],
|
||||
},
|
||||
};
|
||||
};
|
||||
|
||||
export let StepLine = () => {
|
||||
return {
|
||||
template: hbs`
|
||||
<h5 class="title is-5">Line Chart with a Step Line</h5>
|
||||
<div class="block" style="height:250px">
|
||||
{{#if this.data}}
|
||||
<LineChart
|
||||
@xProp="x"
|
||||
@yProp="y"
|
||||
@data={{this.data}}>
|
||||
<:svg as |c|>
|
||||
<c.Area @data={{this.data}} @curve="stepAfter" />
|
||||
</:svg>
|
||||
<:after as |c|>
|
||||
<c.Tooltip class="is-snappy" as |series datum|>
|
||||
<li>
|
||||
<span class="label"><span class="color-swatch is-primary" />{{datum.formattedX}}</span>
|
||||
<span class="value">{{datum.formattedY}}</span>
|
||||
</li>
|
||||
</c.Tooltip>
|
||||
</:after>
|
||||
</LineChart>
|
||||
<p>{{this.activeAnnotation.info}}</p>
|
||||
{{/if}}
|
||||
</div>
|
||||
`,
|
||||
context: {
|
||||
data: DelayedArray.create([
|
||||
{ x: 1, y: 5 },
|
||||
{ x: 2, y: 1 },
|
||||
{ x: 3, y: 2 },
|
||||
{ x: 4, y: 2 },
|
||||
{ x: 5, y: 9 },
|
||||
{ x: 6, y: 3 },
|
||||
{ x: 7, y: 4 },
|
||||
{ x: 8, y: 1 },
|
||||
{ x: 9, y: 5 },
|
||||
]),
|
||||
},
|
||||
};
|
||||
};
|
||||
|
||||
export let MultiLine = () => ({
|
||||
template: hbs`
|
||||
<h5 class="title is-5">Multiple Lines on One Chart</h5>
|
||||
<div class="block" style="height:250px">
|
||||
{{#if this.data}}
|
||||
<LineChart
|
||||
@xProp="x"
|
||||
@yProp="y"
|
||||
@dataProp="data"
|
||||
@data={{this.data}}>
|
||||
<:svg as |c|>
|
||||
{{#each this.data as |series idx|}}
|
||||
<c.Area @data={{series.data}} @colorScale="reds" @index={{idx}} />
|
||||
{{/each}}
|
||||
</:svg>
|
||||
<:after as |c|>
|
||||
<c.Tooltip class="is-snappy" as |series datum index|>
|
||||
<li>
|
||||
<span class="label"><span class="color-swatch swatch-reds swatch-reds-{{index}}" />{{series.name}}</span>
|
||||
<span class="value">{{datum.formattedY}}</span>
|
||||
</li>
|
||||
</c.Tooltip>
|
||||
</:after>
|
||||
</LineChart>
|
||||
<p>{{this.activeAnnotation.info}}</p>
|
||||
{{/if}}
|
||||
</div>
|
||||
`,
|
||||
context: {
|
||||
data: DelayedArray.create([
|
||||
{
|
||||
name: 'Series 1',
|
||||
data: [
|
||||
{ x: 3, y: 7 },
|
||||
{ x: 4, y: 5 },
|
||||
{ x: 5, y: 8 },
|
||||
{ x: 6, y: 9 },
|
||||
{ x: 7, y: 10 },
|
||||
{ x: 8, y: 8 },
|
||||
{ x: 9, y: 6 },
|
||||
],
|
||||
},
|
||||
{
|
||||
name: 'Series 2',
|
||||
data: [
|
||||
{ x: 1, y: 5 },
|
||||
{ x: 2, y: 1 },
|
||||
{ x: 3, y: 2 },
|
||||
{ x: 4, y: 2 },
|
||||
{ x: 5, y: 9 },
|
||||
{ x: 6, y: 3 },
|
||||
{ x: 7, y: 4 },
|
||||
],
|
||||
},
|
||||
]),
|
||||
},
|
||||
});
|
||||
@@ -1,73 +0,0 @@
|
||||
/**
|
||||
* Copyright (c) HashiCorp, Inc.
|
||||
* SPDX-License-Identifier: BUSL-1.1
|
||||
*/
|
||||
|
||||
import hbs from 'htmlbars-inline-precompile';
|
||||
|
||||
export default {
|
||||
title: 'Charts/Primitives',
|
||||
};
|
||||
|
||||
export let Tooltip = () => ({
|
||||
template: hbs`
|
||||
<h5 class="title is-5">Single Entry</h5>
|
||||
<div class="mock-hover-region" style="width:300px;height:100px">
|
||||
<ChartPrimitives::Tooltip @active={{true}} @style={{this.style}} @data={{this.dataSingle}} as |series|>
|
||||
<li>
|
||||
<span class="label"><span class="color-swatch swatch-reds" />{{series.name}}</span>
|
||||
<span class="value">{{series.value}}</span>
|
||||
</li>
|
||||
</ChartPrimitives::Tooltip>
|
||||
</div>
|
||||
|
||||
<h5 class="title is-5">Multiple Entries</h5>
|
||||
<div class="mock-hover-region" style="width:300px;height:100px">
|
||||
<ChartPrimitives::Tooltip @active={{true}} @style={{this.style}} @data={{take 4 this.dataMultiple}} as |series datum index|>
|
||||
<li>
|
||||
<span class="label"><span class="color-swatch swatch-reds swatch-reds-{{index}}" />{{series.name}}</span>
|
||||
<span class="value">{{datum.value}}</span>
|
||||
</li>
|
||||
</ChartPrimitives::Tooltip>
|
||||
</div>
|
||||
|
||||
<h5 class="title is-5">Active Entry</h5>
|
||||
<div class="mock-hover-region" style="width:300px;height:100px">
|
||||
<ChartPrimitives::Tooltip @active={{true}} @style={{this.style}} @data={{take 4 this.dataMultiple}} class="with-active-datum" as |series datum index|>
|
||||
<li class="{{if (eq series.name "Three") "is-active"}}">
|
||||
<span class="label"><span class="color-swatch swatch-reds swatch-reds-{{index}}" />{{series.name}}</span>
|
||||
<span class="value">{{datum.value}}</span>
|
||||
</li>
|
||||
</ChartPrimitives::Tooltip>
|
||||
</div>
|
||||
|
||||
<h5 class="title is-5">Color Scales</h5>
|
||||
<div class="multiples is-left-aligned with-spacing">
|
||||
{{#each this.scales as |scale|}}
|
||||
<div class="mock-hover-region" style="width:300px;height:200px">
|
||||
{{scale}}
|
||||
<ChartPrimitives::Tooltip @active={{true}} @style="left:70%;top:75%" @data={{this.dataMultiple}} as |series datum index|>
|
||||
<li>
|
||||
<span class="label"><span class="color-swatch swatch-{{scale}} swatch-{{scale}}-{{index}}" />{{series.name}}</span>
|
||||
<span class="value">{{datum.value}}</span>
|
||||
</li>
|
||||
</ChartPrimitives::Tooltip>
|
||||
</div>
|
||||
{{/each}}
|
||||
</div>
|
||||
`,
|
||||
context: {
|
||||
style: 'left:70%;top:50%;',
|
||||
dataSingle: [{ series: { name: 'Example', value: 12 } }],
|
||||
dataMultiple: [
|
||||
{ series: { name: 'One' }, datum: { value: 12 }, index: 0 },
|
||||
{ series: { name: 'Two' }, datum: { value: 24 }, index: 1 },
|
||||
{ series: { name: 'Three' }, datum: { value: 36 }, index: 2 },
|
||||
{ series: { name: 'Four' }, datum: { value: 48 }, index: 3 },
|
||||
{ series: { name: 'Five' }, datum: { value: 60 }, index: 4 },
|
||||
{ series: { name: 'Six' }, datum: { value: 72 }, index: 5 },
|
||||
{ series: { name: 'Seven' }, datum: { value: 84 }, index: 6 },
|
||||
],
|
||||
scales: ['reds', 'blues', 'ordinal'],
|
||||
},
|
||||
});
|
||||
@@ -1,145 +0,0 @@
|
||||
/**
|
||||
* Copyright (c) HashiCorp, Inc.
|
||||
* SPDX-License-Identifier: BUSL-1.1
|
||||
*/
|
||||
|
||||
import hbs from 'htmlbars-inline-precompile';
|
||||
|
||||
import EmberObject, { computed } from '@ember/object';
|
||||
import { on } from '@ember/object/evented';
|
||||
|
||||
export default {
|
||||
title: 'Charts/Progress Bar',
|
||||
};
|
||||
|
||||
export let Standard = () => {
|
||||
return {
|
||||
template: hbs`
|
||||
<h5 class="title is-5">Progress Bar</h5>
|
||||
<div class="inline-chart tooltip" role="tooltip" aria-label="5 / 15">
|
||||
<progress
|
||||
class="progress is-primary is-small"
|
||||
value="0.33"
|
||||
max="1">
|
||||
0.33
|
||||
</progress>
|
||||
</div>
|
||||
`,
|
||||
};
|
||||
};
|
||||
|
||||
export let Colors = () => {
|
||||
return {
|
||||
template: hbs`
|
||||
<h5 class="title is-5">Progress Bar colors</h5>
|
||||
<div class="columns">
|
||||
<div class="column">
|
||||
<div class="inline-chart tooltip" role="tooltip" aria-label="5 / 15">
|
||||
<progress
|
||||
class="progress is-info is-small"
|
||||
value="0.33"
|
||||
max="1">
|
||||
0.33
|
||||
</progress>
|
||||
</div>
|
||||
</div>
|
||||
<div class="column">
|
||||
<div class="inline-chart tooltip" role="tooltip" aria-label="5 / 15">
|
||||
<progress
|
||||
class="progress is-success is-small"
|
||||
value="0.33"
|
||||
max="1">
|
||||
0.33
|
||||
</progress>
|
||||
</div>
|
||||
</div>
|
||||
<div class="column">
|
||||
<div class="inline-chart tooltip" role="tooltip" aria-label="5 / 15">
|
||||
<progress
|
||||
class="progress is-warning is-small"
|
||||
value="0.33"
|
||||
max="1">
|
||||
0.33
|
||||
</progress>
|
||||
</div>
|
||||
</div>
|
||||
<div class="column">
|
||||
<div class="inline-chart tooltip" role="tooltip" aria-label="5 / 15">
|
||||
<progress
|
||||
class="progress is-danger is-small"
|
||||
value="0.33"
|
||||
max="1">
|
||||
0.33
|
||||
</progress>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
`,
|
||||
};
|
||||
};
|
||||
|
||||
export let LiveUpdates = () => {
|
||||
return {
|
||||
template: hbs`
|
||||
<h5 class="title is-5">Progress Bar with live updates</h5>
|
||||
<div class="columns">
|
||||
<div class="column is-one-third">
|
||||
<div class="inline-chart tooltip" role="tooltip" aria-label="{{data.numerator}} / {{data.denominator}}">
|
||||
<progress
|
||||
class="progress is-primary is-small"
|
||||
value="{{data.percentage}}"
|
||||
max="1">
|
||||
{{data.percentage}}
|
||||
</progress>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<p class="annotation">
|
||||
<div class="boxed-section">
|
||||
<div class="boxed-section-body is-dark">
|
||||
<JsonViewer @json={{data.liveDetails}} />
|
||||
</div>
|
||||
</div>
|
||||
</p>
|
||||
`,
|
||||
context: {
|
||||
data: EmberObject.extend({
|
||||
timerTicks: 0,
|
||||
|
||||
startTimer: on('init', function () {
|
||||
this.set(
|
||||
'timer',
|
||||
setInterval(() => {
|
||||
this.incrementProperty('timerTicks');
|
||||
}, 1000)
|
||||
);
|
||||
}),
|
||||
|
||||
willDestroy() {
|
||||
clearInterval(this.timer);
|
||||
},
|
||||
|
||||
denominator: computed('timerTicks', function () {
|
||||
return Math.round(Math.random() * 1000);
|
||||
}),
|
||||
|
||||
percentage: computed('timerTicks', function () {
|
||||
return Math.round(Math.random() * 100) / 100;
|
||||
}),
|
||||
|
||||
numerator: computed('denominator', 'percentage', function () {
|
||||
return Math.round(this.denominator * this.percentage * 100) / 100;
|
||||
}),
|
||||
|
||||
liveDetails: computed(
|
||||
'denominator',
|
||||
'numerator',
|
||||
'percentage',
|
||||
function () {
|
||||
return this.getProperties('denominator', 'numerator', 'percentage');
|
||||
}
|
||||
),
|
||||
}).create(),
|
||||
},
|
||||
};
|
||||
};
|
||||
@@ -1,116 +0,0 @@
|
||||
/**
|
||||
* Copyright (c) HashiCorp, Inc.
|
||||
* SPDX-License-Identifier: BUSL-1.1
|
||||
*/
|
||||
|
||||
import hbs from 'htmlbars-inline-precompile';
|
||||
import DelayedTruth from '../utils/delayed-truth';
|
||||
import {
|
||||
withKnobs,
|
||||
optionsKnob,
|
||||
number,
|
||||
boolean,
|
||||
} from '@storybook/addon-knobs';
|
||||
|
||||
export default {
|
||||
title: 'Charts/Recomendation Chart',
|
||||
decorators: [withKnobs],
|
||||
};
|
||||
|
||||
export let Configurable = () => {
|
||||
return {
|
||||
template: hbs`
|
||||
<SvgPatterns />
|
||||
{{#if delayedTruth.complete}}
|
||||
<Das::RecommendationChart
|
||||
@resource={{resource}}
|
||||
@currentValue={{current}}
|
||||
@recommendedValue={{recommendedValue}}
|
||||
@stats={{stats}}
|
||||
@disabled={{disabled}}
|
||||
/>
|
||||
{{/if}}
|
||||
`,
|
||||
context: contextFactory(),
|
||||
};
|
||||
};
|
||||
|
||||
export let Standard = () => {
|
||||
return {
|
||||
template: hbs`
|
||||
<SvgPatterns />
|
||||
<div style="max-width: 500px">
|
||||
{{#if delayedTruth.complete}}
|
||||
<Das::RecommendationChart
|
||||
@resource="CPU"
|
||||
@currentValue={{cpu.current}}
|
||||
@recommendedValue={{cpu.recommendedValue}}
|
||||
@stats={{cpu.stats}}
|
||||
/>
|
||||
<Das::RecommendationChart
|
||||
@resource="MemoryMB"
|
||||
@currentValue={{mem.current}}
|
||||
@recommendedValue={{mem.recommendedValue}}
|
||||
@stats={{mem.stats}}
|
||||
/>
|
||||
<hr/>
|
||||
<Das::RecommendationChart
|
||||
@resource="CPU"
|
||||
@currentValue={{cpu.current}}
|
||||
@recommendedValue={{cpu.recommendedValue}}
|
||||
@stats={{cpu.stats}}
|
||||
@disabled={{true}}
|
||||
/>
|
||||
<Das::RecommendationChart
|
||||
@resource="MemoryMB"
|
||||
@currentValue={{mem.current}}
|
||||
@recommendedValue={{mem.recommendedValue}}
|
||||
@stats={{mem.stats}}
|
||||
/>
|
||||
{{/if}}
|
||||
</div>
|
||||
`,
|
||||
context: {
|
||||
delayedTruth: DelayedTruth.create(),
|
||||
cpu: {
|
||||
current: 100,
|
||||
recommendedValue: 600,
|
||||
stats: {
|
||||
mean: 300,
|
||||
p99: 500,
|
||||
max: 525,
|
||||
},
|
||||
},
|
||||
mem: {
|
||||
current: 2048,
|
||||
recommendedValue: 256,
|
||||
stats: {
|
||||
mean: 140,
|
||||
p99: 215,
|
||||
max: 225,
|
||||
},
|
||||
},
|
||||
},
|
||||
};
|
||||
};
|
||||
|
||||
function contextFactory() {
|
||||
const numberConfig = { range: true, min: 0, max: 1000, step: 1 };
|
||||
return {
|
||||
delayedTruth: DelayedTruth.create(),
|
||||
resource: optionsKnob(
|
||||
'Resource',
|
||||
{ Cpu: 'CPU', Memory: 'MemoryMB' },
|
||||
'CPU',
|
||||
{ display: 'inline-radio' }
|
||||
),
|
||||
current: number('Current', 100, numberConfig),
|
||||
recommendedValue: number('Recommendation', 300, numberConfig),
|
||||
stats: {
|
||||
mean: number('Stat: mean', 150, numberConfig),
|
||||
p99: number('Stat: p99', 600, numberConfig),
|
||||
max: number('Stat: max', 650, numberConfig),
|
||||
},
|
||||
disabled: boolean('Disabled', false),
|
||||
};
|
||||
}
|
||||
@@ -1,125 +0,0 @@
|
||||
/**
|
||||
* Copyright (c) HashiCorp, Inc.
|
||||
* SPDX-License-Identifier: BUSL-1.1
|
||||
*/
|
||||
|
||||
import hbs from 'htmlbars-inline-precompile';
|
||||
|
||||
import EmberObject, { computed } from '@ember/object';
|
||||
import { on } from '@ember/object/evented';
|
||||
import moment from 'moment';
|
||||
|
||||
import DelayedArray from '../utils/delayed-array';
|
||||
|
||||
export default {
|
||||
title: 'Charts/Stats Time Series',
|
||||
};
|
||||
|
||||
let ts = (offset) => moment().subtract(offset, 'm').toDate();
|
||||
|
||||
export let Standard = () => {
|
||||
return {
|
||||
template: hbs`
|
||||
<h5 class="title is-5">Stats Time Series</h5>
|
||||
<div class="block" style="height:100px; width: 400px;">
|
||||
{{#if staticMetrics}}
|
||||
<StatsTimeSeries @data={{staticMetrics}} @chartClass="is-primary" />
|
||||
{{/if}}
|
||||
</div>
|
||||
`,
|
||||
context: {
|
||||
staticMetrics: DelayedArray.create([
|
||||
{ timestamp: ts(20), percent: 0.5 },
|
||||
{ timestamp: ts(18), percent: 0.5 },
|
||||
{ timestamp: ts(16), percent: 0.4 },
|
||||
{ timestamp: ts(14), percent: 0.3 },
|
||||
{ timestamp: ts(12), percent: 0.9 },
|
||||
{ timestamp: ts(10), percent: 0.3 },
|
||||
{ timestamp: ts(8), percent: 0.3 },
|
||||
{ timestamp: ts(6), percent: 0.4 },
|
||||
{ timestamp: ts(4), percent: 0.5 },
|
||||
{ timestamp: ts(2), percent: 0.6 },
|
||||
{ timestamp: ts(0), percent: 0.6 },
|
||||
]),
|
||||
},
|
||||
};
|
||||
};
|
||||
|
||||
export let HighLowComparison = () => {
|
||||
return {
|
||||
template: hbs`
|
||||
<h5 class="title is-5">Stats Time Series high/low comparison</h5>
|
||||
<div class="columns">
|
||||
<div class="block column" style="height:200px; width:400px">
|
||||
{{#if data.metricsHigh}}
|
||||
<StatsTimeSeries @data={{data.metricsHigh}} @chartClass="is-info" />
|
||||
{{/if}}
|
||||
</div>
|
||||
<div class="block column" style="height:200px; width:400px">
|
||||
{{#if data.metricsLow}}
|
||||
<StatsTimeSeries @data={{data.metricsLow}} @chartClass="is-info" />
|
||||
{{/if}}
|
||||
</div>
|
||||
</div>
|
||||
<p class="annotation">Line charts, and therefore stats time series charts, use a letant linear gradient with a height equal to the canvas. This makes the color intensity of the gradient at values consistent across charts as long as those charts have the same y-axis domain.</p>
|
||||
<p class="annotation">This is used to great effect with stats charts since they all have a y-axis domain of 0-100%.</p>
|
||||
`,
|
||||
context: {
|
||||
data: EmberObject.extend({
|
||||
timerTicks: 0,
|
||||
|
||||
startTimer: on('init', function () {
|
||||
this.set(
|
||||
'timer',
|
||||
setInterval(() => {
|
||||
let metricsHigh = this.metricsHigh;
|
||||
let prev = metricsHigh.length
|
||||
? metricsHigh[metricsHigh.length - 1].percent
|
||||
: 0.9;
|
||||
this.appendTSValue(
|
||||
metricsHigh,
|
||||
Math.min(Math.max(prev + Math.random() * 0.05 - 0.025, 0.5), 1)
|
||||
);
|
||||
|
||||
let metricsLow = this.metricsLow;
|
||||
let prev2 = metricsLow.length
|
||||
? metricsLow[metricsLow.length - 1].percent
|
||||
: 0.1;
|
||||
this.appendTSValue(
|
||||
metricsLow,
|
||||
Math.min(Math.max(prev2 + Math.random() * 0.05 - 0.025, 0), 0.5)
|
||||
);
|
||||
}, 1000)
|
||||
);
|
||||
}),
|
||||
|
||||
appendTSValue(array, percent, maxLength = 300) {
|
||||
array.addObject({
|
||||
timestamp: Date.now(),
|
||||
percent,
|
||||
});
|
||||
|
||||
if (array.length > maxLength) {
|
||||
array.splice(0, array.length - maxLength);
|
||||
}
|
||||
},
|
||||
|
||||
willDestroy() {
|
||||
clearInterval(this.timer);
|
||||
},
|
||||
|
||||
metricsHigh: computed(function () {
|
||||
return [];
|
||||
}),
|
||||
|
||||
metricsLow: computed(function () {
|
||||
return [];
|
||||
}),
|
||||
|
||||
secondsFormat() {
|
||||
return (date) => moment(date).format('HH:mm:ss');
|
||||
},
|
||||
}).create(),
|
||||
},
|
||||
};
|
||||
};
|
||||
@@ -1,195 +0,0 @@
|
||||
/**
|
||||
* Copyright (c) HashiCorp, Inc.
|
||||
* SPDX-License-Identifier: BUSL-1.1
|
||||
*/
|
||||
|
||||
import hbs from 'htmlbars-inline-precompile';
|
||||
import DelayedTruth from '../utils/delayed-truth';
|
||||
import { withKnobs, boolean } from '@storybook/addon-knobs';
|
||||
import { getOwner } from '@ember/application';
|
||||
import { tracked } from '@glimmer/tracking';
|
||||
import { scaleLinear } from 'd3-scale';
|
||||
import faker from 'faker';
|
||||
|
||||
export default {
|
||||
title: 'Charts/Topo Viz',
|
||||
decorators: [withKnobs],
|
||||
};
|
||||
|
||||
const nodeGen = (name, datacenter, memory, cpu, allocations = []) => ({
|
||||
datacenter,
|
||||
memory,
|
||||
cpu,
|
||||
node: { name, isEligible: true, isDraining: false },
|
||||
allocations: allocations.map((alloc) => ({
|
||||
memory: alloc.memory,
|
||||
cpu: alloc.cpu,
|
||||
memoryPercent: alloc.memory / memory,
|
||||
cpuPercent: alloc.cpu / cpu,
|
||||
allocation: {
|
||||
id: faker.random.uuid(),
|
||||
isScheduled: true,
|
||||
clientStatus: alloc.clientStatus,
|
||||
},
|
||||
})),
|
||||
});
|
||||
|
||||
const nodeModelGen = (datacenter, id, name, resources = '2000/1000') => {
|
||||
const [cpu, memory] = resources.split('/');
|
||||
return {
|
||||
datacenter,
|
||||
id,
|
||||
name,
|
||||
isEligible: true,
|
||||
isDraining: false,
|
||||
resources: { cpu, memory },
|
||||
};
|
||||
};
|
||||
|
||||
const allocModelGen = (
|
||||
id,
|
||||
taskGroupName,
|
||||
clientStatus,
|
||||
nodeId,
|
||||
jobId,
|
||||
resources = '100/100'
|
||||
) => {
|
||||
const [cpu, memory] = resources.split('/');
|
||||
return {
|
||||
id,
|
||||
taskGroupName,
|
||||
clientStatus,
|
||||
isScheduled: true,
|
||||
allocatedResources: { cpu, memory },
|
||||
belongsTo(t) {
|
||||
return {
|
||||
id() {
|
||||
return t === 'node' ? nodeId : jobId;
|
||||
},
|
||||
};
|
||||
},
|
||||
};
|
||||
};
|
||||
|
||||
export let Node = () => ({
|
||||
template: hbs`
|
||||
<SvgPatterns />
|
||||
{{#if delayedTruth.complete}}
|
||||
<TopoViz::Node
|
||||
@node={{node}}
|
||||
@isDense={{isDense}}
|
||||
@heightScale={{heightScale}}
|
||||
/>
|
||||
{{/if}}
|
||||
`,
|
||||
context: {
|
||||
delayedTruth: DelayedTruth.create(),
|
||||
isDense: boolean('isDense', false),
|
||||
heightScale: scaleLinear().range([15, 40]).domain([100, 1000]),
|
||||
node: nodeGen('Node One', 'dc1', 1000, 1000, [
|
||||
{ memory: 100, cpu: 100, clientStatus: 'pending' },
|
||||
{ memory: 250, cpu: 300, clientStatus: 'running' },
|
||||
{ memory: 300, cpu: 200, clientStatus: 'running' },
|
||||
]),
|
||||
},
|
||||
});
|
||||
|
||||
export let Datacenter = () => ({
|
||||
template: hbs`
|
||||
<SvgPatterns />
|
||||
{{#if delayedTruth.complete}}
|
||||
<TopoViz::Datacenter
|
||||
@datacenter={{dc}}
|
||||
@isSingleColumn={{isSingleColumn}}
|
||||
@isDense={{isDense}}
|
||||
@heightScale={{heightScale}}
|
||||
/>
|
||||
{{/if}}
|
||||
`,
|
||||
context: {
|
||||
delayedTruth: DelayedTruth.create(),
|
||||
isSingleColumn: boolean('isSingleColumn', false),
|
||||
isDense: boolean('isDense', false),
|
||||
heightScale: scaleLinear().range([15, 40]).domain([100, 1000]),
|
||||
dc: {
|
||||
name: 'dc1',
|
||||
nodes: [
|
||||
nodeGen('Node One', 'dc1', 1000, 1000, [
|
||||
{ memory: 100, cpu: 100, clientStatus: 'pending' },
|
||||
{ memory: 250, cpu: 300, clientStatus: 'running' },
|
||||
{ memory: 300, cpu: 200, clientStatus: 'running' },
|
||||
]),
|
||||
nodeGen('And Two', 'dc1', 500, 1000, [
|
||||
{ memory: 100, cpu: 100, clientStatus: 'pending' },
|
||||
{ memory: 250, cpu: 300, clientStatus: 'running' },
|
||||
{ memory: 100, cpu: 100, clientStatus: 'running' },
|
||||
{ memory: 100, cpu: 100, clientStatus: 'running' },
|
||||
{ memory: 100, cpu: 100, clientStatus: 'running' },
|
||||
]),
|
||||
nodeGen('Three', 'dc1', 500, 500, [
|
||||
{ memory: 100, cpu: 300, clientStatus: 'running' },
|
||||
{ memory: 300, cpu: 200, clientStatus: 'pending' },
|
||||
]),
|
||||
],
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
export let FullViz = () => ({
|
||||
template: hbs`
|
||||
{{#if delayedTruth.complete}}
|
||||
<TopoViz
|
||||
@nodes={{nodes}}
|
||||
@allocations={{allocations}}
|
||||
/>
|
||||
{{/if}}
|
||||
`,
|
||||
context: {
|
||||
delayedTruth: DelayedTruth.create(),
|
||||
nodes: [
|
||||
nodeModelGen('dc1', '1', 'pdx-1', '2000/1000'),
|
||||
nodeModelGen('dc1', '2', 'pdx-2', '2000/1000'),
|
||||
nodeModelGen('dc1', '3', 'pdx-3', '2000/3000'),
|
||||
nodeModelGen('dc2', '4', 'yyz-1', '2000/1000'),
|
||||
nodeModelGen('dc2', '5', 'yyz-2', '2000/2000'),
|
||||
],
|
||||
allocations: [
|
||||
allocModelGen('1', 'name', 'running', '1', 'job-1', '200/500'),
|
||||
allocModelGen('1', 'name', 'running', '5', 'job-1', '200/500'),
|
||||
],
|
||||
},
|
||||
});
|
||||
|
||||
export let EmberData = () => ({
|
||||
template: hbs`
|
||||
<div class="notification is-info">
|
||||
<h3 class='title is-4'>This visualization uses data from mirage.</h3>
|
||||
<p>Change the mirage scenario to see different cluster states visualized.</p>
|
||||
</div>
|
||||
{{#if (and delayedTruth.complete nodes allocations)}}
|
||||
<TopoViz
|
||||
@nodes={{nodes}}
|
||||
@allocations={{allocations}}
|
||||
/>
|
||||
{{/if}}
|
||||
`,
|
||||
context: {
|
||||
delayedTruth: DelayedTruth.create(),
|
||||
nodes: tracked([]),
|
||||
allocations: tracked([]),
|
||||
|
||||
async init() {
|
||||
this._super(...arguments);
|
||||
|
||||
const owner = getOwner(this);
|
||||
const store = owner.lookup('service:store');
|
||||
|
||||
this.nodes = await store.query('node', { resources: true });
|
||||
this.allocations = await store.query('allocation', {
|
||||
resources: true,
|
||||
task_states: false,
|
||||
namespace: '*',
|
||||
});
|
||||
},
|
||||
},
|
||||
});
|
||||
@@ -1,89 +0,0 @@
|
||||
/**
|
||||
* Copyright (c) HashiCorp, Inc.
|
||||
* SPDX-License-Identifier: BUSL-1.1
|
||||
*/
|
||||
|
||||
import hbs from 'htmlbars-inline-precompile';
|
||||
import productMetadata from '../../app/utils/styleguide/product-metadata';
|
||||
|
||||
export default {
|
||||
title: 'Components/Accordion',
|
||||
};
|
||||
|
||||
export let Standard = () => {
|
||||
return {
|
||||
template: hbs`
|
||||
<h5 class="title is-5">Accordion</h5>
|
||||
<ListAccordion @source={{products}} @key="name" as |ac|>
|
||||
<ac.head @buttonLabel="details">
|
||||
<div class="columns inline-definitions">
|
||||
<div class="column is-1">{{ac.item.name}}</div>
|
||||
<div class="column is-1">
|
||||
<span class="bumper-left badge is-light">{{ac.item.lang}}</span>
|
||||
</div>
|
||||
</div>
|
||||
</ac.head>
|
||||
<ac.body>
|
||||
<h1 class="title is-4">{{ac.item.name}}</h1>
|
||||
<p>{{ac.item.desc}}</p>
|
||||
<p><a href="{{ac.item.link}}" target="_parent">Learn more...</a></p>
|
||||
</ac.body>
|
||||
</ListAccordion>
|
||||
`,
|
||||
context: {
|
||||
products: productMetadata,
|
||||
},
|
||||
};
|
||||
};
|
||||
|
||||
export let OneItem = () => {
|
||||
return {
|
||||
template: hbs`
|
||||
<h5 class="title is-5">Accordion, one item</h5>
|
||||
<ListAccordion @source={{take 1 products}} @key="name" as |a|>
|
||||
<a.head @buttonLabel="details">
|
||||
<div class="columns inline-definitions">
|
||||
<div class="column is-1">{{a.item.name}}</div>
|
||||
<div class="column is-1">
|
||||
<span class="bumper-left badge is-light">{{a.item.lang}}</span>
|
||||
</div>
|
||||
</div>
|
||||
</a.head>
|
||||
<a.body>
|
||||
<h1 class="title is-4">{{a.item.name}}</h1>
|
||||
<p>{{a.item.desc}}</p>
|
||||
<p><a href="{{a.item.link}}">Learn more...</a></p>
|
||||
</a.body>
|
||||
</ListAccordion>
|
||||
`,
|
||||
context: {
|
||||
products: productMetadata,
|
||||
},
|
||||
};
|
||||
};
|
||||
|
||||
export let NotExpandable = () => {
|
||||
return {
|
||||
template: hbs`
|
||||
<h5 class="title is-5">Accordion, not expandable</h5>
|
||||
<ListAccordion @source={{products}} @key="name" as |a|>
|
||||
<a.head @buttonLabel="details" @isExpandable={{eq a.item.lang "golang"}}>
|
||||
<div class="columns inline-definitions">
|
||||
<div class="column is-1">{{a.item.name}}</div>
|
||||
<div class="column is-1">
|
||||
<span class="bumper-left badge is-light">{{a.item.lang}}</span>
|
||||
</div>
|
||||
</div>
|
||||
</a.head>
|
||||
<a.body>
|
||||
<h1 class="title is-4">{{a.item.name}}</h1>
|
||||
<p>{{a.item.desc}}</p>
|
||||
<p><a href="{{a.item.link}}">Learn more...</a></p>
|
||||
</a.body>
|
||||
</ListAccordion>
|
||||
`,
|
||||
context: {
|
||||
products: productMetadata,
|
||||
},
|
||||
};
|
||||
};
|
||||
@@ -1,107 +0,0 @@
|
||||
/**
|
||||
* Copyright (c) HashiCorp, Inc.
|
||||
* SPDX-License-Identifier: BUSL-1.1
|
||||
*/
|
||||
|
||||
import hbs from 'htmlbars-inline-precompile';
|
||||
|
||||
export default {
|
||||
title: 'Components/Alerts',
|
||||
};
|
||||
|
||||
export let Standard = () => {
|
||||
return {
|
||||
template: hbs`
|
||||
<h5 class="title is-5">Alert</h5>
|
||||
<div class="notification is-info">
|
||||
<h3 class="title is-4">This is an alert</h3>
|
||||
<p>Alerts are used for both situational and reactionary information.</p>
|
||||
</div>
|
||||
<p class="annotation">Alerts use Bulma's notification component.</p>
|
||||
`,
|
||||
};
|
||||
};
|
||||
|
||||
export let Colors = () => {
|
||||
return {
|
||||
template: hbs`
|
||||
<h5 class="title is-5">Alert colors</h5>
|
||||
<div class="notification is-info">
|
||||
<h3 class="title is-4">This is an alert</h3>
|
||||
<p>Alerts are used for both situational and reactionary information.</p>
|
||||
</div>
|
||||
|
||||
<div class="notification is-success">
|
||||
<h3 class="title is-4">This is an alert</h3>
|
||||
<p>Alerts are used for both situational and reactionary information.</p>
|
||||
</div>
|
||||
|
||||
<div class="notification is-warning">
|
||||
<h3 class="title is-4">This is an alert</h3>
|
||||
<p>Alerts are used for both situational and reactionary information.</p>
|
||||
</div>
|
||||
|
||||
<div class="notification is-danger">
|
||||
<h3 class="title is-4">This is an alert</h3>
|
||||
<p>Alerts are used for both situational and reactionary information.</p>
|
||||
</div>
|
||||
|
||||
<p class="annotation">Alerts are always paired with an emotive color. If there is no emotive association with the content of the alert, then an alert is the wrong component to use.</p>
|
||||
`,
|
||||
};
|
||||
};
|
||||
|
||||
export let Dismissal = () => {
|
||||
return {
|
||||
template: hbs`
|
||||
<h5 class="title is-5">Alert dismissal</h5>
|
||||
<div class="notification is-info">
|
||||
<div class="columns">
|
||||
<div class="column">
|
||||
<h3 class="title is-4">This is an alert</h3>
|
||||
<p>Alerts are used for both situational and reactionary information.</p>
|
||||
</div>
|
||||
<div class="column is-centered is-minimum">
|
||||
<button class="button is-info">Okay</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="notification is-success">
|
||||
<div class="columns">
|
||||
<div class="column">
|
||||
<h3 class="title is-4">This is an alert</h3>
|
||||
<p>Alerts are used for both situational and reactionary information.</p>
|
||||
</div>
|
||||
<div class="column is-centered is-minimum">
|
||||
<button class="button is-success">Okay</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="notification is-warning">
|
||||
<div class="columns">
|
||||
<div class="column">
|
||||
<h3 class="title is-4">This is an alert</h3>
|
||||
<p>Alerts are used for both situational and reactionary information.</p>
|
||||
</div>
|
||||
<div class="column is-centered is-minimum">
|
||||
<button class="button is-warning">Okay</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="notification is-danger">
|
||||
<div class="columns">
|
||||
<div class="column">
|
||||
<h3 class="title is-4">This is an alert</h3>
|
||||
<p>Alerts are used for both situational and reactionary information.</p>
|
||||
</div>
|
||||
<div class="column is-centered is-minimum">
|
||||
<button class="button is-danger">Okay</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
`,
|
||||
};
|
||||
};
|
||||
@@ -1,169 +0,0 @@
|
||||
/**
|
||||
* Copyright (c) HashiCorp, Inc.
|
||||
* SPDX-License-Identifier: BUSL-1.1
|
||||
*/
|
||||
|
||||
import hbs from 'htmlbars-inline-precompile';
|
||||
import { withKnobs, optionsKnob } from '@storybook/addon-knobs';
|
||||
|
||||
export default {
|
||||
title: 'Components/Boxed Section',
|
||||
decorators: [withKnobs],
|
||||
};
|
||||
|
||||
export let Standard = () => {
|
||||
return {
|
||||
template: hbs`
|
||||
<h5 class="title is-5">Boxed section</h5>
|
||||
<div class="boxed-section {{variant}}">
|
||||
<div class="boxed-section-head">
|
||||
Boxed Section
|
||||
</div>
|
||||
<div class="boxed-section-body">
|
||||
<div class="mock-content">
|
||||
<div class="mock-image"></div>
|
||||
<div class="mock-copy"></div>
|
||||
<div class="mock-copy"></div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
`,
|
||||
context: contextFactory(),
|
||||
};
|
||||
};
|
||||
|
||||
export let RightHandDetails = () => {
|
||||
return {
|
||||
template: hbs`
|
||||
<h5 class="title is-5">Boxed section with right hand details</h5>
|
||||
<div class="boxed-section {{variant}}">
|
||||
<div class="boxed-section-head">
|
||||
Boxed Section With Right Hand Details
|
||||
<span class="pull-right">{{now interval=1000}}</span>
|
||||
</div>
|
||||
<div class="boxed-section-body">
|
||||
<div class="mock-content">
|
||||
<div class="mock-image"></div>
|
||||
<div class="mock-copy"></div>
|
||||
<div class="mock-copy"></div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
`,
|
||||
context: contextFactory(),
|
||||
};
|
||||
};
|
||||
|
||||
export let TitleDecoration = () => {
|
||||
return {
|
||||
template: hbs`
|
||||
<h5 class="title is-5">Boxed section with title decoration</h5>
|
||||
<div class="boxed-section {{variant}}">
|
||||
<div class="boxed-section-head">
|
||||
Boxed Section With Title Decoration
|
||||
<span class="badge is-white">7</span>
|
||||
</div>
|
||||
<div class="boxed-section-body">
|
||||
<div class="mock-content">
|
||||
<div class="mock-image"></div>
|
||||
<div class="mock-copy"></div>
|
||||
<div class="mock-copy"></div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
`,
|
||||
context: contextFactory(),
|
||||
};
|
||||
};
|
||||
|
||||
export let Foot = () => {
|
||||
return {
|
||||
template: hbs`
|
||||
<h5 class="title is-5">Boxed section with foot</h5>
|
||||
<div class="boxed-section {{variant}}">
|
||||
<div class="boxed-section-head">
|
||||
Boxed Section With Large Header
|
||||
</div>
|
||||
<div class="boxed-section-body with-foot">
|
||||
<div class="mock-content">
|
||||
<div class="mock-image"></div>
|
||||
<div class="mock-copy"></div>
|
||||
<div class="mock-copy"></div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="boxed-section-foot">
|
||||
<span>Left-aligned message</span>
|
||||
<a href="javascript:;" class="pull-right">Toggle or other action</a>
|
||||
</div>
|
||||
</div>
|
||||
`,
|
||||
context: contextFactory(),
|
||||
};
|
||||
};
|
||||
|
||||
export let LargeHeader = () => {
|
||||
return {
|
||||
template: hbs`
|
||||
<h5 class="title is-5">Boxed section with large header</h5>
|
||||
<div class="boxed-section {{variant}}">
|
||||
<div class="boxed-section-head">
|
||||
<div class="boxed-section-row">
|
||||
Boxed Section With Large Header
|
||||
<span class="badge is-white is-subtle bumper-left">Status</span>
|
||||
</div>
|
||||
<div class="boxed-section-row">
|
||||
<span class="tag is-outlined">A tag that goes on a second line because it's rather long</span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="boxed-section-body">
|
||||
<div class="mock-content">
|
||||
<div class="mock-image"></div>
|
||||
<div class="mock-copy"></div>
|
||||
<div class="mock-copy"></div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
`,
|
||||
context: contextFactory(),
|
||||
};
|
||||
};
|
||||
|
||||
export let DarkBody = () => {
|
||||
return {
|
||||
template: hbs`
|
||||
<h5 class="title is-5">Boxed section with dark body</h5>
|
||||
<div class="boxed-section {{variant}}">
|
||||
<div class="boxed-section-head">
|
||||
Boxed Section With Dark Body
|
||||
</div>
|
||||
<div class="boxed-section-body is-dark">
|
||||
<div class="mock-content">
|
||||
<div class="mock-image"></div>
|
||||
<div class="mock-copy"></div>
|
||||
<div class="mock-copy"></div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
`,
|
||||
context: contextFactory(),
|
||||
};
|
||||
};
|
||||
|
||||
function contextFactory() {
|
||||
return {
|
||||
variant: optionsKnob(
|
||||
'Variant',
|
||||
{
|
||||
Normal: '',
|
||||
Info: 'is-info',
|
||||
Warning: 'is-warning',
|
||||
Danger: 'is-danger',
|
||||
},
|
||||
'',
|
||||
{
|
||||
display: 'inline-radio',
|
||||
},
|
||||
'variant-id'
|
||||
),
|
||||
};
|
||||
}
|
||||
@@ -1,50 +0,0 @@
|
||||
/**
|
||||
* Copyright (c) HashiCorp, Inc.
|
||||
* SPDX-License-Identifier: BUSL-1.1
|
||||
*/
|
||||
|
||||
import hbs from 'htmlbars-inline-precompile';
|
||||
|
||||
export default {
|
||||
title: 'Components/Breadcrumbs',
|
||||
};
|
||||
|
||||
export let Standard = () => {
|
||||
return {
|
||||
template: hbs`
|
||||
<h5 class="title is-5">Breadcrumbs</h5>
|
||||
<div class="navbar is-secondary">
|
||||
<div class="navbar-item"></div>
|
||||
<nav class="breadcrumb is-large">
|
||||
<li>
|
||||
<a href="javascript:;">Topic</a>
|
||||
</li>
|
||||
<li>
|
||||
<a href="javascript:;">Sub-topic</a>
|
||||
</li>
|
||||
<li class="is-active">
|
||||
<a href="javascript:;">Active Topic</a>
|
||||
</li>
|
||||
</nav>
|
||||
</div>
|
||||
<p class="annotation">Breadcrumbs are only ever used in the secondary nav of the primary header.</p>
|
||||
`,
|
||||
};
|
||||
};
|
||||
|
||||
export let Single = () => {
|
||||
return {
|
||||
template: hbs`
|
||||
<h5 class="title is-5">Single breadcrumb</h5>
|
||||
<div class="navbar is-secondary">
|
||||
<div class="navbar-item"></div>
|
||||
<nav class="breadcrumb is-large">
|
||||
<li>
|
||||
<a href="javascript:;">Topic</a>
|
||||
</li>
|
||||
</nav>
|
||||
</div>
|
||||
<p class="annotation">Breadcrumbs are given a lot of emphasis and often double as a page title. Since they are also global state, they are important for helping a user keep their bearings.</p>
|
||||
`,
|
||||
};
|
||||
};
|
||||
@@ -1,135 +0,0 @@
|
||||
/**
|
||||
* Copyright (c) HashiCorp, Inc.
|
||||
* SPDX-License-Identifier: BUSL-1.1
|
||||
*/
|
||||
|
||||
import hbs from 'htmlbars-inline-precompile';
|
||||
|
||||
export default {
|
||||
title: 'Components/Buttons',
|
||||
};
|
||||
|
||||
export let Standard = () => {
|
||||
return {
|
||||
template: hbs`
|
||||
<h5 class="title is-5">Buttons</h5>
|
||||
<div class="block">
|
||||
<a class="button">Button</a>
|
||||
<a class="button is-white">White</a>
|
||||
<a class="button is-light">Light</a>
|
||||
<a class="button is-dark">Dark</a>
|
||||
<a class="button is-black">Black</a>
|
||||
<a class="button is-link">Link</a>
|
||||
</div>
|
||||
<div class="block">
|
||||
<a class="button is-primary">Primary</a>
|
||||
<a class="button is-info">Info</a>
|
||||
<a class="button is-success">Success</a>
|
||||
<a class="button is-warning">Warning</a>
|
||||
<a class="button is-danger">Danger</a>
|
||||
</div>
|
||||
`,
|
||||
};
|
||||
};
|
||||
|
||||
export let Outline = () => {
|
||||
return {
|
||||
template: hbs`
|
||||
<h5 class="title is-5">Outline buttons</h5>
|
||||
<div class="block">
|
||||
<a class="button is-outlined">Outlined</a>
|
||||
<a class="button is-primary is-outlined">Primary</a>
|
||||
<a class="button is-info is-outlined">Info</a>
|
||||
<a class="button is-success is-outlined">Success</a>
|
||||
<a class="button is-warning is-outlined">Warning</a>
|
||||
<a class="button is-danger is-outlined is-important">Danger</a>
|
||||
</div>
|
||||
`,
|
||||
};
|
||||
};
|
||||
|
||||
export let Hollow = () => {
|
||||
return {
|
||||
template: hbs`
|
||||
<h5 class="title is-5">Hollow buttons</h5>
|
||||
<div class="block" style="background:#25BA81; padding:30px">
|
||||
<a class="button is-primary is-inverted is-outlined">Primary</a>
|
||||
<a class="button is-info is-inverted is-outlined">Info</a>
|
||||
<a class="button is-success is-inverted is-outlined">Success</a>
|
||||
<a class="button is-warning is-inverted is-outlined">Warning</a>
|
||||
<a class="button is-danger is-inverted is-outlined">Danger</a>
|
||||
</div>
|
||||
`,
|
||||
};
|
||||
};
|
||||
|
||||
export let Sizes = () => {
|
||||
return {
|
||||
template: hbs`
|
||||
<h5 class="title is-5">Button sizes</h5>
|
||||
<div class="block">
|
||||
<a class="button is-small">Small</a>
|
||||
<a class="button">Normal</a>
|
||||
<a class="button is-medium">Medium</a>
|
||||
<a class="button is-large">Large</a>
|
||||
</div>
|
||||
`,
|
||||
};
|
||||
};
|
||||
|
||||
export let Disabled = () => {
|
||||
return {
|
||||
template: hbs`
|
||||
<h5 class="title is-5">Anchor elements as buttons</h5>
|
||||
<div class="block">
|
||||
<a class="button is-disabled">Button</a>
|
||||
<a class="button is-white is-disabled">White</a>
|
||||
<a class="button is-light is-disabled">Light</a>
|
||||
<a class="button is-dark is-disabled">Dark</a>
|
||||
<a class="button is-black is-disabled">Black</a>
|
||||
<a class="button is-link is-disabled">Link</a>
|
||||
</div>
|
||||
<div class="block">
|
||||
<a class="button is-primary is-disabled">Primary</a>
|
||||
<a class="button is-info is-disabled">Info</a>
|
||||
<a class="button is-success is-disabled">Success</a>
|
||||
<a class="button is-warning is-disabled">Warning</a>
|
||||
<a class="button is-danger is-disabled">Danger</a>
|
||||
</div>
|
||||
|
||||
<h5 class="title is-5">Button elements with <code>disabled</code> attribute</h5>
|
||||
<div class="block">
|
||||
<button class="button is-disabled" disabled>Button</button>
|
||||
<button class="button is-white is-disabled" disabled>White</button>
|
||||
<button class="button is-light is-disabled" disabled>Light</button>
|
||||
<button class="button is-dark is-disabled" disabled>Dark</button>
|
||||
<button class="button is-black is-disabled" disabled>Black</button>
|
||||
<button class="button is-link is-disabled" disabled>Link</button>
|
||||
</div>
|
||||
<div class="block">
|
||||
<button class="button is-primary is-disabled" disabled>Primary</button>
|
||||
<button class="button is-info is-disabled" disabled>Info</button>
|
||||
<button class="button is-success is-disabled" disabled>Success</button>
|
||||
<button class="button is-warning is-disabled" disabled>Warning</button>
|
||||
<button class="button is-danger is-disabled" disabled>Danger</button>
|
||||
</div>
|
||||
|
||||
<h5 class="title is-5">Button elements with <code>aria-disabled="true"</code></h5>
|
||||
<div class="block">
|
||||
<button class="button is-disabled" aria-disabled="true">Button</button>
|
||||
<button class="button is-white is-disabled" aria-disabled="true">White</button>
|
||||
<button class="button is-light is-disabled" aria-disabled="true">Light</button>
|
||||
<button class="button is-dark is-disabled" aria-disabled="true">Dark</button>
|
||||
<button class="button is-black is-disabled" aria-disabled="true">Black</button>
|
||||
<button class="button is-link is-disabled" aria-disabled="true">Link</button>
|
||||
</div>
|
||||
<div class="block">
|
||||
<button class="button is-primary is-disabled" aria-disabled="true">Primary</button>
|
||||
<button class="button is-info is-disabled" aria-disabled="true">Info</button>
|
||||
<button class="button is-success is-disabled" aria-disabled="true">Success</button>
|
||||
<button class="button is-warning is-disabled" aria-disabled="true">Warning</button>
|
||||
<button class="button is-danger is-disabled" aria-disabled="true">Danger</button>
|
||||
</div>
|
||||
`,
|
||||
};
|
||||
};
|
||||
@@ -1,34 +0,0 @@
|
||||
/**
|
||||
* Copyright (c) HashiCorp, Inc.
|
||||
* SPDX-License-Identifier: BUSL-1.1
|
||||
*/
|
||||
|
||||
import hbs from 'htmlbars-inline-precompile';
|
||||
import { withKnobs, text } from '@storybook/addon-knobs';
|
||||
|
||||
export default {
|
||||
title: 'Components/Copy Button',
|
||||
decorators: [withKnobs],
|
||||
};
|
||||
|
||||
export let CopyButton = () => {
|
||||
return {
|
||||
template: hbs`
|
||||
<h5 class="title is-5">Copy Button</h5>
|
||||
<span class="tag is-hollow is-small no-text-transform">
|
||||
{{clipboardText}}
|
||||
<CopyButton @clipboardText={{clipboardText}} />
|
||||
</span>
|
||||
<h5 class="title is-5">Copy Button with content</h5>
|
||||
<span class="tag is-hollow is-small no-text-transform">
|
||||
<CopyButton @clipboardText={{clipboardText}}>{{clipboardText}}</CopyButton>
|
||||
</span>
|
||||
`,
|
||||
context: {
|
||||
clipboardText: text(
|
||||
'Clipboard Text',
|
||||
'e8c898a0-794b-9063-7a7f-bf0c4a405f83'
|
||||
),
|
||||
},
|
||||
};
|
||||
};
|
||||
@@ -1,736 +0,0 @@
|
||||
/**
|
||||
* Copyright (c) HashiCorp, Inc.
|
||||
* SPDX-License-Identifier: BUSL-1.1
|
||||
*/
|
||||
|
||||
import hbs from 'htmlbars-inline-precompile';
|
||||
|
||||
export default {
|
||||
title: 'Components/Diff Viewer',
|
||||
};
|
||||
|
||||
export let DiffViewerWithInsertions = () => {
|
||||
return {
|
||||
template: hbs`
|
||||
<h5 class="title is-5">Diff Viewer with insertions</h5>
|
||||
<div class="boxed-section">
|
||||
<div class="boxed-section-body is-dark">
|
||||
<JobDiff @diff={{insertionsOnly}} />
|
||||
</div>
|
||||
</div>
|
||||
`,
|
||||
context: {
|
||||
insertionsOnly: generateDiff([
|
||||
{
|
||||
Annotations: null,
|
||||
Name: 'Attempts',
|
||||
New: '15',
|
||||
Old: '15',
|
||||
Type: 'None',
|
||||
},
|
||||
{
|
||||
Annotations: null,
|
||||
Name: 'Delay',
|
||||
New: '25000000000',
|
||||
Old: '',
|
||||
Type: 'Added',
|
||||
},
|
||||
{
|
||||
Annotations: null,
|
||||
Name: 'Interval',
|
||||
New: '900000000000',
|
||||
Old: '',
|
||||
Type: 'Added',
|
||||
},
|
||||
{
|
||||
Annotations: null,
|
||||
Name: 'Mode',
|
||||
New: 'delay',
|
||||
Old: 'delay',
|
||||
Type: 'None',
|
||||
},
|
||||
]),
|
||||
},
|
||||
};
|
||||
};
|
||||
|
||||
export let DiffViewerWithDeletions = () => {
|
||||
return {
|
||||
template: hbs`
|
||||
<h5 class="title is-5">Diff Viewer with deletions</h5>
|
||||
<div class="boxed-section">
|
||||
<div class="boxed-section-body is-dark">
|
||||
<JobDiff @diff={{deletionsOnly}} />
|
||||
</div>
|
||||
</div>
|
||||
`,
|
||||
context: {
|
||||
deletionsOnly: generateDiff([
|
||||
{
|
||||
Annotations: null,
|
||||
Name: 'Attempts',
|
||||
New: '15',
|
||||
Old: '15',
|
||||
Type: 'None',
|
||||
},
|
||||
{
|
||||
Annotations: null,
|
||||
Name: 'Delay',
|
||||
New: '25000000000',
|
||||
Old: '25000000000',
|
||||
Type: 'None',
|
||||
},
|
||||
{
|
||||
Annotations: null,
|
||||
Name: 'Interval',
|
||||
New: '900000000000',
|
||||
Old: '900000000000',
|
||||
Type: 'None',
|
||||
},
|
||||
{
|
||||
Annotations: null,
|
||||
Name: 'Mode',
|
||||
New: '',
|
||||
Old: 'delay',
|
||||
Type: 'Deleted',
|
||||
},
|
||||
]),
|
||||
},
|
||||
};
|
||||
};
|
||||
|
||||
export let DiffViewerWithEdits = () => {
|
||||
return {
|
||||
template: hbs`
|
||||
<h5 class="title is-5">Diff Viewer with edits</h5>
|
||||
<div class="boxed-section">
|
||||
<div class="boxed-section-body is-dark">
|
||||
<JobDiff @diff={{editsOnly}} />
|
||||
</div>
|
||||
<p class="annotation">Often times a diff will only have a couple lines. Minor tweaks to a job spec result in small diffs.</p>
|
||||
</div>
|
||||
`,
|
||||
context: {
|
||||
editsOnly: generateDiff([
|
||||
{
|
||||
Annotations: null,
|
||||
Name: 'Attempts',
|
||||
New: '15',
|
||||
Old: '15',
|
||||
Type: 'None',
|
||||
},
|
||||
{
|
||||
Annotations: null,
|
||||
Name: 'Delay',
|
||||
New: '25000000000',
|
||||
Old: '25000000000',
|
||||
Type: 'None',
|
||||
},
|
||||
{
|
||||
Annotations: null,
|
||||
Name: 'Interval',
|
||||
New: '900000000000',
|
||||
Old: '250000000000',
|
||||
Type: 'Edited',
|
||||
},
|
||||
{
|
||||
Annotations: null,
|
||||
Name: 'Mode',
|
||||
New: 'delay',
|
||||
Old: 'delay',
|
||||
Type: 'None',
|
||||
},
|
||||
]),
|
||||
},
|
||||
};
|
||||
};
|
||||
|
||||
export let DiffViewerWithManyChanges = () => {
|
||||
return {
|
||||
template: hbs`
|
||||
<h5 class="title is-5">Diff Viewer with many changes</h5>
|
||||
<div class="boxed-section">
|
||||
<div class="boxed-section-body is-dark">
|
||||
<JobDiff @diff={{largeDiff}} />
|
||||
</div>
|
||||
</div>
|
||||
`,
|
||||
context: {
|
||||
largeDiff: {
|
||||
Fields: null,
|
||||
ID: 'example',
|
||||
Objects: null,
|
||||
TaskGroups: [
|
||||
{
|
||||
Fields: null,
|
||||
Name: 'cache',
|
||||
Objects: null,
|
||||
Tasks: [
|
||||
{
|
||||
Annotations: null,
|
||||
Fields: [
|
||||
{
|
||||
Annotations: null,
|
||||
Name: 'Meta[one]',
|
||||
New: "flew over the cuckoo's nest",
|
||||
Old: '',
|
||||
Type: 'Added',
|
||||
},
|
||||
{
|
||||
Annotations: null,
|
||||
Name: 'Meta[two]',
|
||||
New: 'birds on a wire',
|
||||
Old: '',
|
||||
Type: 'Added',
|
||||
},
|
||||
],
|
||||
Name: 'redis',
|
||||
Objects: [
|
||||
{
|
||||
Fields: [
|
||||
{
|
||||
Annotations: null,
|
||||
Name: 'image',
|
||||
New: 'redis:3.4',
|
||||
Old: 'redis:7',
|
||||
Type: 'Edited',
|
||||
},
|
||||
{
|
||||
Annotations: null,
|
||||
Name: 'port_map[0][db]',
|
||||
New: '6380',
|
||||
Old: '6379',
|
||||
Type: 'Edited',
|
||||
},
|
||||
],
|
||||
Name: 'Config',
|
||||
Objects: null,
|
||||
Type: 'Edited',
|
||||
},
|
||||
{
|
||||
Fields: [
|
||||
{
|
||||
Annotations: null,
|
||||
Name: 'CPU',
|
||||
New: '1000',
|
||||
Old: '500',
|
||||
Type: 'Edited',
|
||||
},
|
||||
{
|
||||
Annotations: null,
|
||||
Name: 'DiskMB',
|
||||
New: '0',
|
||||
Old: '0',
|
||||
Type: 'None',
|
||||
},
|
||||
{
|
||||
Annotations: null,
|
||||
Name: 'IOPS',
|
||||
New: '0',
|
||||
Old: '0',
|
||||
Type: 'None',
|
||||
},
|
||||
{
|
||||
Annotations: null,
|
||||
Name: 'MemoryMB',
|
||||
New: '512',
|
||||
Old: '256',
|
||||
Type: 'Edited',
|
||||
},
|
||||
],
|
||||
Name: 'Resources',
|
||||
Objects: [
|
||||
{
|
||||
Fields: [
|
||||
{
|
||||
Annotations: null,
|
||||
Name: 'MBits',
|
||||
New: '100',
|
||||
Old: '',
|
||||
Type: 'Added',
|
||||
},
|
||||
],
|
||||
Name: 'Network',
|
||||
Objects: [
|
||||
{
|
||||
Fields: [
|
||||
{
|
||||
Annotations: null,
|
||||
Name: 'Label',
|
||||
New: 'db',
|
||||
Old: '',
|
||||
Type: 'Added',
|
||||
},
|
||||
],
|
||||
Name: 'Dynamic Port',
|
||||
Objects: null,
|
||||
Type: 'Added',
|
||||
},
|
||||
],
|
||||
Type: 'Added',
|
||||
},
|
||||
{
|
||||
Fields: [
|
||||
{
|
||||
Annotations: null,
|
||||
Name: 'MBits',
|
||||
New: '',
|
||||
Old: '10',
|
||||
Type: 'Deleted',
|
||||
},
|
||||
],
|
||||
Name: 'Network',
|
||||
Objects: [
|
||||
{
|
||||
Fields: [
|
||||
{
|
||||
Annotations: null,
|
||||
Name: 'Label',
|
||||
New: '',
|
||||
Old: 'db',
|
||||
Type: 'Deleted',
|
||||
},
|
||||
],
|
||||
Name: 'Dynamic Port',
|
||||
Objects: null,
|
||||
Type: 'Deleted',
|
||||
},
|
||||
],
|
||||
Type: 'Deleted',
|
||||
},
|
||||
],
|
||||
Type: 'Edited',
|
||||
},
|
||||
{
|
||||
Fields: [
|
||||
{
|
||||
Annotations: null,
|
||||
Name: 'AddressMode',
|
||||
New: 'auto',
|
||||
Old: 'auto',
|
||||
Type: 'None',
|
||||
},
|
||||
{
|
||||
Annotations: null,
|
||||
Name: 'Name',
|
||||
New: 'redis-cache',
|
||||
Old: 'redis-cache',
|
||||
Type: 'None',
|
||||
},
|
||||
{
|
||||
Annotations: null,
|
||||
Name: 'PortLabel',
|
||||
New: 'db',
|
||||
Old: 'db',
|
||||
Type: 'None',
|
||||
},
|
||||
],
|
||||
Name: 'Service',
|
||||
Objects: [
|
||||
{
|
||||
Fields: [
|
||||
{
|
||||
Annotations: null,
|
||||
Name: 'Tags',
|
||||
New: 'redis',
|
||||
Old: '',
|
||||
Type: 'Added',
|
||||
},
|
||||
{
|
||||
Annotations: null,
|
||||
Name: 'Tags',
|
||||
New: 'cache',
|
||||
Old: 'cache',
|
||||
Type: 'None',
|
||||
},
|
||||
{
|
||||
Annotations: null,
|
||||
Name: 'Tags',
|
||||
New: 'global',
|
||||
Old: 'global',
|
||||
Type: 'None',
|
||||
},
|
||||
],
|
||||
Name: 'Tags',
|
||||
Objects: null,
|
||||
Type: 'Added',
|
||||
},
|
||||
{
|
||||
Fields: [
|
||||
{
|
||||
Annotations: null,
|
||||
Name: 'AddressMode',
|
||||
New: '',
|
||||
Old: '',
|
||||
Type: 'None',
|
||||
},
|
||||
{
|
||||
Annotations: null,
|
||||
Name: 'Command',
|
||||
New: '',
|
||||
Old: '',
|
||||
Type: 'None',
|
||||
},
|
||||
{
|
||||
Annotations: null,
|
||||
Name: 'GRPCService',
|
||||
New: '',
|
||||
Old: '',
|
||||
Type: 'None',
|
||||
},
|
||||
{
|
||||
Annotations: null,
|
||||
Name: 'GRPCUseTLS',
|
||||
New: 'false',
|
||||
Old: 'false',
|
||||
Type: 'None',
|
||||
},
|
||||
{
|
||||
Annotations: null,
|
||||
Name: 'InitialStatus',
|
||||
New: '',
|
||||
Old: '',
|
||||
Type: 'None',
|
||||
},
|
||||
{
|
||||
Annotations: null,
|
||||
Name: 'Interval',
|
||||
New: '15000000000',
|
||||
Old: '10000000000',
|
||||
Type: 'Edited',
|
||||
},
|
||||
{
|
||||
Annotations: null,
|
||||
Name: 'Method',
|
||||
New: '',
|
||||
Old: '',
|
||||
Type: 'None',
|
||||
},
|
||||
{
|
||||
Annotations: null,
|
||||
Name: 'Name',
|
||||
New: 'alive',
|
||||
Old: 'alive',
|
||||
Type: 'None',
|
||||
},
|
||||
{
|
||||
Annotations: null,
|
||||
Name: 'Path',
|
||||
New: '',
|
||||
Old: '',
|
||||
Type: 'None',
|
||||
},
|
||||
{
|
||||
Annotations: null,
|
||||
Name: 'PortLabel',
|
||||
New: '',
|
||||
Old: '',
|
||||
Type: 'None',
|
||||
},
|
||||
{
|
||||
Annotations: null,
|
||||
Name: 'Protocol',
|
||||
New: '',
|
||||
Old: '',
|
||||
Type: 'None',
|
||||
},
|
||||
{
|
||||
Annotations: null,
|
||||
Name: 'TLSServerName',
|
||||
New: '',
|
||||
Old: '',
|
||||
Type: 'None',
|
||||
},
|
||||
{
|
||||
Annotations: null,
|
||||
Name: 'TLSSkipVerify',
|
||||
New: 'false',
|
||||
Old: 'false',
|
||||
Type: 'None',
|
||||
},
|
||||
{
|
||||
Annotations: null,
|
||||
Name: 'Timeout',
|
||||
New: '7000000000',
|
||||
Old: '2000000000',
|
||||
Type: 'Edited',
|
||||
},
|
||||
{
|
||||
Annotations: null,
|
||||
Name: 'Type',
|
||||
New: 'tcp',
|
||||
Old: 'tcp',
|
||||
Type: 'None',
|
||||
},
|
||||
],
|
||||
Name: 'Check',
|
||||
Objects: null,
|
||||
Type: 'Edited',
|
||||
},
|
||||
],
|
||||
Type: 'Edited',
|
||||
},
|
||||
],
|
||||
Type: 'Edited',
|
||||
},
|
||||
],
|
||||
Type: 'Edited',
|
||||
Updates: null,
|
||||
},
|
||||
{
|
||||
Fields: [
|
||||
{
|
||||
Annotations: null,
|
||||
Name: 'Count',
|
||||
New: '1',
|
||||
Old: '',
|
||||
Type: 'Added',
|
||||
},
|
||||
{
|
||||
Annotations: null,
|
||||
Name: 'Meta[key]',
|
||||
New: 'value',
|
||||
Old: '',
|
||||
Type: 'Added',
|
||||
},
|
||||
{
|
||||
Annotations: null,
|
||||
Name: 'Meta[red]',
|
||||
New: 'fish',
|
||||
Old: '',
|
||||
Type: 'Added',
|
||||
},
|
||||
],
|
||||
Name: 'cache2',
|
||||
Objects: [
|
||||
{
|
||||
Fields: [
|
||||
{
|
||||
Annotations: null,
|
||||
Name: 'Attempts',
|
||||
New: '2',
|
||||
Old: '',
|
||||
Type: 'Added',
|
||||
},
|
||||
{
|
||||
Annotations: null,
|
||||
Name: 'Delay',
|
||||
New: '15000000000',
|
||||
Old: '',
|
||||
Type: 'Added',
|
||||
},
|
||||
{
|
||||
Annotations: null,
|
||||
Name: 'Interval',
|
||||
New: '1800000000000',
|
||||
Old: '',
|
||||
Type: 'Added',
|
||||
},
|
||||
{
|
||||
Annotations: null,
|
||||
Name: 'Mode',
|
||||
New: 'fail',
|
||||
Old: '',
|
||||
Type: 'Added',
|
||||
},
|
||||
],
|
||||
Name: 'RestartPolicy',
|
||||
Objects: null,
|
||||
Type: 'Added',
|
||||
},
|
||||
{
|
||||
Fields: [
|
||||
{
|
||||
Annotations: null,
|
||||
Name: 'Migrate',
|
||||
New: 'false',
|
||||
Old: '',
|
||||
Type: 'Added',
|
||||
},
|
||||
{
|
||||
Annotations: null,
|
||||
Name: 'SizeMB',
|
||||
New: '300',
|
||||
Old: '',
|
||||
Type: 'Added',
|
||||
},
|
||||
{
|
||||
Annotations: null,
|
||||
Name: 'Sticky',
|
||||
New: 'false',
|
||||
Old: '',
|
||||
Type: 'Added',
|
||||
},
|
||||
],
|
||||
Name: 'EphemeralDisk',
|
||||
Objects: null,
|
||||
Type: 'Added',
|
||||
},
|
||||
],
|
||||
Tasks: [
|
||||
{
|
||||
Annotations: null,
|
||||
Fields: [
|
||||
{
|
||||
Annotations: null,
|
||||
Name: 'Driver',
|
||||
New: 'docker',
|
||||
Old: '',
|
||||
Type: 'Added',
|
||||
},
|
||||
{
|
||||
Annotations: null,
|
||||
Name: 'KillTimeout',
|
||||
New: '5000000000',
|
||||
Old: '',
|
||||
Type: 'Added',
|
||||
},
|
||||
{
|
||||
Annotations: null,
|
||||
Name: 'Leader',
|
||||
New: 'false',
|
||||
Old: '',
|
||||
Type: 'Added',
|
||||
},
|
||||
{
|
||||
Annotations: null,
|
||||
Name: 'ShutdownDelay',
|
||||
New: '0',
|
||||
Old: '',
|
||||
Type: 'Added',
|
||||
},
|
||||
],
|
||||
Name: 'redis',
|
||||
Objects: [
|
||||
{
|
||||
Fields: [
|
||||
{
|
||||
Annotations: null,
|
||||
Name: 'image',
|
||||
New: 'redis:7',
|
||||
Old: '',
|
||||
Type: 'Added',
|
||||
},
|
||||
{
|
||||
Annotations: null,
|
||||
Name: 'port_map[0][db]',
|
||||
New: '6379',
|
||||
Old: '',
|
||||
Type: 'Added',
|
||||
},
|
||||
],
|
||||
Name: 'Config',
|
||||
Objects: null,
|
||||
Type: 'Added',
|
||||
},
|
||||
{
|
||||
Fields: [
|
||||
{
|
||||
Annotations: null,
|
||||
Name: 'CPU',
|
||||
New: '500',
|
||||
Old: '',
|
||||
Type: 'Added',
|
||||
},
|
||||
{
|
||||
Annotations: null,
|
||||
Name: 'DiskMB',
|
||||
New: '0',
|
||||
Old: '',
|
||||
Type: 'Added',
|
||||
},
|
||||
{
|
||||
Annotations: null,
|
||||
Name: 'IOPS',
|
||||
New: '0',
|
||||
Old: '',
|
||||
Type: 'Added',
|
||||
},
|
||||
{
|
||||
Annotations: null,
|
||||
Name: 'MemoryMB',
|
||||
New: '256',
|
||||
Old: '',
|
||||
Type: 'Added',
|
||||
},
|
||||
],
|
||||
Name: 'Resources',
|
||||
Objects: [
|
||||
{
|
||||
Fields: [
|
||||
{
|
||||
Annotations: null,
|
||||
Name: 'MBits',
|
||||
New: '10',
|
||||
Old: '',
|
||||
Type: 'Added',
|
||||
},
|
||||
],
|
||||
Name: 'Network',
|
||||
Objects: [
|
||||
{
|
||||
Fields: [
|
||||
{
|
||||
Annotations: null,
|
||||
Name: 'Label',
|
||||
New: 'db',
|
||||
Old: '',
|
||||
Type: 'Added',
|
||||
},
|
||||
],
|
||||
Name: 'Dynamic Port',
|
||||
Objects: null,
|
||||
Type: 'Added',
|
||||
},
|
||||
],
|
||||
Type: 'Added',
|
||||
},
|
||||
],
|
||||
Type: 'Added',
|
||||
},
|
||||
],
|
||||
Type: 'Added',
|
||||
},
|
||||
],
|
||||
Type: 'Added',
|
||||
Updates: null,
|
||||
},
|
||||
],
|
||||
Type: 'Edited',
|
||||
},
|
||||
},
|
||||
};
|
||||
};
|
||||
|
||||
function generateDiff(changeset) {
|
||||
return {
|
||||
Fields: null,
|
||||
ID: 'insertions-only',
|
||||
Objects: null,
|
||||
TaskGroups: [
|
||||
{
|
||||
Fields: [
|
||||
{
|
||||
Annotations: null,
|
||||
Name: 'Count',
|
||||
New: '2',
|
||||
Old: '2',
|
||||
Type: 'None',
|
||||
},
|
||||
],
|
||||
Name: 'cache',
|
||||
Objects: [
|
||||
{
|
||||
Fields: changeset,
|
||||
Name: 'RestartPolicy',
|
||||
Objects: null,
|
||||
Type: 'Edited',
|
||||
},
|
||||
],
|
||||
Type: 'Edited',
|
||||
Updates: null,
|
||||
},
|
||||
],
|
||||
Type: 'Edited',
|
||||
};
|
||||
}
|
||||
@@ -1,88 +0,0 @@
|
||||
/**
|
||||
* Copyright (c) HashiCorp, Inc.
|
||||
* SPDX-License-Identifier: BUSL-1.1
|
||||
*/
|
||||
|
||||
import hbs from 'htmlbars-inline-precompile';
|
||||
|
||||
export default {
|
||||
title: 'Components/Dropdown',
|
||||
};
|
||||
|
||||
let options = [
|
||||
{ name: 'Consul' },
|
||||
{ name: 'Nomad' },
|
||||
{ name: 'Packer' },
|
||||
{ name: 'Terraform' },
|
||||
{ name: 'Vagrant' },
|
||||
{ name: 'Vault' },
|
||||
];
|
||||
|
||||
export let Standard = () => {
|
||||
return {
|
||||
template: hbs`
|
||||
<h5 class="title is-5">Dropdown</h5>
|
||||
<PowerSelect @options={{options}} @selected={{selectedOption}} @searchField="name" @searchEnabled={{gt options.length 10}} @onChange={{action (mut selectedOption)}} as |option|>
|
||||
{{option.name}}
|
||||
</PowerSelect>
|
||||
<p class="annotation">Power Select currently fulfills all of Nomad's dropdown needs out of the box.</p>
|
||||
`,
|
||||
context: {
|
||||
options,
|
||||
},
|
||||
};
|
||||
};
|
||||
|
||||
export let Resized = () => {
|
||||
return {
|
||||
template: hbs`
|
||||
<h5 class="title is-5">Dropdown resized</h5>
|
||||
<div class="columns">
|
||||
<div class="column is-3">
|
||||
<PowerSelect @options={{options}} @selected={{selectedOption2}} @searchField="name" @searchEnabled={{gt options.length 10}} @onChange={{action (mut selectedOption2)}} as |option|>
|
||||
{{option.name}}
|
||||
</PowerSelect>
|
||||
</div>
|
||||
</div>
|
||||
<p class="annotation">Dropdowns are always 100% wide. To control the width of a dropdown, adjust the dimensions of its container. One way to achieve this is using columns.</p>
|
||||
`,
|
||||
context: {
|
||||
options,
|
||||
},
|
||||
};
|
||||
};
|
||||
|
||||
export let Search = () => {
|
||||
return {
|
||||
template: hbs`
|
||||
<h5 class="title is-5">Dropdown with search</h5>
|
||||
<div class="columns">
|
||||
<div class="column is-3">
|
||||
<PowerSelect @options={{manyOptions}} @selected={{selectedOption3}} @searchField="name" @searchEnabled={{gt manyOptions.length 10}} @onChange={{action (mut selectedOption3)}} as |option|>
|
||||
{{option.name}}
|
||||
</PowerSelect>
|
||||
</div>
|
||||
</div>
|
||||
<p class="annotation">Whether or not the dropdown has a search box is configurable. Typically the default is to show a search once a dropdown has more than 10 options.</p>
|
||||
`,
|
||||
context: {
|
||||
manyOptions: [
|
||||
'One',
|
||||
'Two',
|
||||
'Three',
|
||||
'Four',
|
||||
'Five',
|
||||
'Six',
|
||||
'Seven',
|
||||
'Eight',
|
||||
'Nine',
|
||||
'Ten',
|
||||
'Eleven',
|
||||
'Twelve',
|
||||
'Thirteen',
|
||||
'Fourteen',
|
||||
'Fifteen',
|
||||
].map((name) => ({ name })),
|
||||
},
|
||||
};
|
||||
};
|
||||
@@ -1,212 +0,0 @@
|
||||
/**
|
||||
* Copyright (c) HashiCorp, Inc.
|
||||
* SPDX-License-Identifier: BUSL-1.1
|
||||
*/
|
||||
|
||||
import hbs from 'htmlbars-inline-precompile';
|
||||
|
||||
export default {
|
||||
title: 'Components/Filter Facets',
|
||||
};
|
||||
|
||||
let options1 = [
|
||||
{ key: 'option-1', label: 'Option One' },
|
||||
{ key: 'option-2', label: 'Option Two' },
|
||||
{ key: 'option-3', label: 'Option Three' },
|
||||
{ key: 'option-4', label: 'Option Four' },
|
||||
{ key: 'option-5', label: 'Option Five' },
|
||||
];
|
||||
|
||||
let selection1 = ['option-2', 'option-4', 'option-5'];
|
||||
|
||||
export let MultiSelect = () => {
|
||||
return {
|
||||
template: hbs`
|
||||
<h5 class="title is-5">Multi-Select Dropdown</h5>
|
||||
<MultiSelectDropdown
|
||||
@label="Example Dropdown"
|
||||
@options={{this.options1}}
|
||||
@selection={{this.selection1}}
|
||||
@onSelect={{action (mut selection1)}} />
|
||||
<p class="annotation">A wrapper around basic-dropdown for creating a list of checkboxes and tracking the state thereof.</p>
|
||||
`,
|
||||
context: {
|
||||
options1,
|
||||
selection1,
|
||||
},
|
||||
};
|
||||
};
|
||||
|
||||
export let SingleSelect = () => ({
|
||||
template: hbs`
|
||||
<h5 class="title is-5">Single-Select Dropdown</h5>
|
||||
<SingleSelectDropdown
|
||||
@label="Single"
|
||||
@options={{this.options1}}
|
||||
@selection={{this.selection}}
|
||||
@onSelect={{action (mut this.selection)}} />
|
||||
`,
|
||||
context: {
|
||||
options1,
|
||||
selection: 'option-2',
|
||||
},
|
||||
});
|
||||
|
||||
export let RightAligned = () => {
|
||||
return {
|
||||
template: hbs`
|
||||
<h5 class="title is-5">Multi-Select Dropdown right-aligned</h5>
|
||||
<div style="display:flex; justify-content:flex-end">
|
||||
<MultiSelectDropdown
|
||||
@label="Example right-aligned Dropdown"
|
||||
@options={{this.options1}}
|
||||
@selection={{this.selection1}}
|
||||
@onSelect={{action (mut selection1)}} />
|
||||
</div>
|
||||
`,
|
||||
context: {
|
||||
options1,
|
||||
selection1,
|
||||
},
|
||||
};
|
||||
};
|
||||
|
||||
export let ManyOptionsMulti = () => {
|
||||
return {
|
||||
template: hbs`
|
||||
<h5 class="title is-5">Multi-Select Dropdown with many options</h5>
|
||||
<MultiSelectDropdown
|
||||
@label="Lots of options in here"
|
||||
@options={{this.optionsMany}}
|
||||
@selection={{this.selectionMany}}
|
||||
@onSelect={{action (mut this.selectionMany)}} />
|
||||
<p class="annotation">
|
||||
A strength of the multi-select-dropdown is its simple presentation. It is quick to select options and it is quick to remove options.
|
||||
However, this strength becomes a weakness when there are too many options. Since the selection isn't pinned in any way, removing a selection
|
||||
can become an adventure of scrolling up and down. Also since the selection isn't pinned, this component can't support search, since search would
|
||||
entirely mask the selection.
|
||||
</p>
|
||||
`,
|
||||
context: {
|
||||
optionsMany: Array(100)
|
||||
.fill(null)
|
||||
.map((_, i) => ({ label: `Option ${i}`, key: `option-${i}` })),
|
||||
selectionMany: [],
|
||||
},
|
||||
};
|
||||
};
|
||||
|
||||
export let ManyOptionsSingle = () => {
|
||||
return {
|
||||
template: hbs`
|
||||
<h5 class="title is-5">Single-Select Dropdown with many options</h5>
|
||||
<SingleSelectDropdown
|
||||
@label="Lots of options in here"
|
||||
@options={{this.optionsMany}}
|
||||
@selection={{this.selection}}
|
||||
@onSelect={{action (mut this.selection)}} />
|
||||
<p class="annotation">
|
||||
Single select supports search at a certain option threshold via Ember Power Select.
|
||||
</p>
|
||||
`,
|
||||
context: {
|
||||
optionsMany: Array(100)
|
||||
.fill(null)
|
||||
.map((_, i) => ({ label: `Option ${i}`, key: `option-${i}` })),
|
||||
selection: 'option-1',
|
||||
},
|
||||
};
|
||||
};
|
||||
|
||||
export let Bar = () => {
|
||||
return {
|
||||
template: hbs`
|
||||
<h5 class="title is-5">Multi-Select Dropdown bar</h5>
|
||||
<div class="button-bar">
|
||||
<MultiSelectDropdown
|
||||
@label="Datacenter"
|
||||
@options={{this.optionsDatacenter}}
|
||||
@selection={{this.selectionDatacenter}}
|
||||
@onSelect={{action (mut this.selectionDatacenter)}} />
|
||||
<MultiSelectDropdown
|
||||
@label="Type"
|
||||
@options={{this.optionsType}}
|
||||
@selection={{this.selectionType}}
|
||||
@onSelect={{action (mut this.selectionType)}} />
|
||||
<MultiSelectDropdown
|
||||
@label="Status"
|
||||
@options={{this.optionsStatus}}
|
||||
@selection={{this.selectionStatus}}
|
||||
@onSelect={{action (mut this.selectionStatus)}} />
|
||||
</div>
|
||||
<h5 class="title is-5">Single-Select Dropdown bar</h5>
|
||||
<div class="button-bar">
|
||||
<SingleSelectDropdown
|
||||
@label="Datacenter"
|
||||
@options={{this.optionsDatacenter}}
|
||||
@selection={{this.selectionDatacenterSingle}}
|
||||
@onSelect={{action (mut this.selectionDatacenterSingle)}} />
|
||||
<SingleSelectDropdown
|
||||
@label="Type"
|
||||
@options={{this.optionsType}}
|
||||
@selection={{this.selectionTypeSingle}}
|
||||
@onSelect={{action (mut this.selectionTypeSingle)}} />
|
||||
<SingleSelectDropdown
|
||||
@label="Status"
|
||||
@options={{this.optionsStatus}}
|
||||
@selection={{this.selectionStatusSingle}}
|
||||
@onSelect={{action (mut this.selectionStatusSingle)}} />
|
||||
</div>
|
||||
<h5 class="title is-5">Mixed Dropdown bar</h5>
|
||||
<div class="button-bar">
|
||||
<SingleSelectDropdown
|
||||
@label="Datacenter"
|
||||
@options={{this.optionsDatacenter}}
|
||||
@selection={{this.selectionDatacenterSingle}}
|
||||
@onSelect={{action (mut this.selectionDatacenterSingle)}} />
|
||||
<MultiSelectDropdown
|
||||
@label="Type"
|
||||
@options={{this.optionsType}}
|
||||
@selection={{this.selectionType}}
|
||||
@onSelect={{action (mut this.selectionType)}} />
|
||||
<MultiSelectDropdown
|
||||
@label="Status"
|
||||
@options={{this.optionsStatus}}
|
||||
@selection={{this.selectionStatus}}
|
||||
@onSelect={{action (mut this.selectionStatus)}} />
|
||||
</div>
|
||||
<p class="annotation">
|
||||
Since this is a core component for faceted search, it makes sense to letruct an arrangement of multi-select dropdowns.
|
||||
Do this by wrapping all the options in a <code>.button-bar</code> container.
|
||||
</p>
|
||||
`,
|
||||
context: {
|
||||
optionsDatacenter: [
|
||||
{ key: 'pdx-1', label: 'pdx-1' },
|
||||
{ key: 'jfk-1', label: 'jfk-1' },
|
||||
{ key: 'jfk-2', label: 'jfk-2' },
|
||||
{ key: 'muc-1', label: 'muc-1' },
|
||||
],
|
||||
selectionDatacenter: ['jfk-1'],
|
||||
selectionDatacenterSingle: 'jfk-1',
|
||||
|
||||
optionsType: [
|
||||
{ key: 'batch', label: 'Batch' },
|
||||
{ key: 'service', label: 'Service' },
|
||||
{ key: 'system', label: 'System' },
|
||||
{ key: 'periodic', label: 'Periodic' },
|
||||
{ key: 'parameterized', label: 'Parameterized' },
|
||||
],
|
||||
selectionType: ['system', 'service'],
|
||||
selectionTypeSingle: 'system',
|
||||
|
||||
optionsStatus: [
|
||||
{ key: 'pending', label: 'Pending' },
|
||||
{ key: 'running', label: 'Running' },
|
||||
{ key: 'dead', label: 'Dead' },
|
||||
],
|
||||
selectionStatus: [],
|
||||
selectionStatusSingle: 'dead',
|
||||
},
|
||||
};
|
||||
};
|
||||
@@ -1,212 +0,0 @@
|
||||
/**
|
||||
* Copyright (c) HashiCorp, Inc.
|
||||
* SPDX-License-Identifier: BUSL-1.1
|
||||
*/
|
||||
|
||||
import hbs from 'htmlbars-inline-precompile';
|
||||
|
||||
export default {
|
||||
title: 'Components/Gutter Menu',
|
||||
};
|
||||
|
||||
export let Standard = () => {
|
||||
return {
|
||||
template: hbs`
|
||||
<h5 class="title is-5">Gutter menu</h5>
|
||||
<div class="columns">
|
||||
<div class="column is-4">
|
||||
<div class="gutter">
|
||||
<aside class="menu">
|
||||
<p class="menu-label">Places</p>
|
||||
<ul class="menu-list">
|
||||
<li><a href="javascript:;" class="is-active">Place One</a></li>
|
||||
<li><a href="javascript:;">Place Two</a></li>
|
||||
</ul>
|
||||
|
||||
<p class="menu-label">Features</p>
|
||||
<ul class="menu-list">
|
||||
<li><a href="javascript:;">Feature One</a></li>
|
||||
<li><a href="javascript:;">Feature Two</a></li>
|
||||
</ul>
|
||||
</aside>
|
||||
</div>
|
||||
</div>
|
||||
<div class="column">
|
||||
<div class="mock-content">
|
||||
<div class="mock-vague"></div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
`,
|
||||
};
|
||||
};
|
||||
|
||||
export let RichComponents = () => {
|
||||
return {
|
||||
template: hbs`
|
||||
<h5 class="title is-5">Gutter navigation with rich components</h5>
|
||||
<div class="columns">
|
||||
<div class="column is-4">
|
||||
<div class="gutter">
|
||||
<aside class="menu">
|
||||
<p class="menu-label">Places</p>
|
||||
<ul class="menu-list">
|
||||
<li>
|
||||
<div class="menu-item">
|
||||
<PowerSelect @selected={{or selection "One"}} @options={{array "One" "Two" "Three"}} @onChange={{action (mut selection)}} as |option|>
|
||||
{{option}}
|
||||
</PowerSelect>
|
||||
</div>
|
||||
</li>
|
||||
<li><a href="javascript:;" class="is-active">Place One</a></li>
|
||||
<li><a href="javascript:;">Place Two</a></li>
|
||||
</ul>
|
||||
|
||||
<p class="menu-label">Features</p>
|
||||
<ul class="menu-list">
|
||||
<li><a href="javascript:;">Feature One</a></li>
|
||||
<li><a href="javascript:;">Feature Two</a></li>
|
||||
</ul>
|
||||
</aside>
|
||||
</div>
|
||||
</div>
|
||||
<div class="column">
|
||||
<div class="mock-content">
|
||||
<div class="mock-vague"></div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<p class="annotation">In order to keep the gutter navigation streamlined and easy to navigation, rich components should be avoided when possible. When not possible, they should be kept near the top.</p>
|
||||
`,
|
||||
};
|
||||
};
|
||||
|
||||
export let ManyItems = () => {
|
||||
return {
|
||||
template: hbs`
|
||||
<h5 class="title is-5">Hypothetical gutter navigation with many items</h5>
|
||||
<div class="columns">
|
||||
<div class="column is-4">
|
||||
<div class="gutter">
|
||||
<aside class="menu">
|
||||
<p class="menu-label">Places</p>
|
||||
<ul class="menu-list">
|
||||
{{#each (array "One Two" "Three" "Four" "Five" "Six" "Seven") as |item|}}
|
||||
<li><a href="javascript:;">Place {{item}}</a></li>
|
||||
{{/each}}
|
||||
</ul>
|
||||
|
||||
<p class="menu-label">Features</p>
|
||||
<ul class="menu-list">
|
||||
{{#each (array "One Two" "Three" "Four" "Five" "Six" "Seven") as |item|}}
|
||||
<li><a href="javascript:;">Feature {{item}}</a></li>
|
||||
{{/each}}
|
||||
</ul>
|
||||
|
||||
<p class="menu-label">Other</p>
|
||||
<ul class="menu-list">
|
||||
<li><a href="javascript:;" class="is-active">The one that didn't fit in</a></li>
|
||||
</ul>
|
||||
|
||||
<p class="menu-label">Things</p>
|
||||
<ul class="menu-list">
|
||||
{{#each (array "One Two" "Three" "Four" "Five" "Six" "Seven") as |item|}}
|
||||
<li><a href="javascript:;">Thing {{item}}</a></li>
|
||||
{{/each}}
|
||||
</ul>
|
||||
</aside>
|
||||
</div>
|
||||
</div>
|
||||
<div class="column">
|
||||
<div class="mock-content">
|
||||
<div class="mock-vague"></div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<p class="annotation">There will only ever be one gutter menu in the Nomad UI, but it helps to imagine a situation where there are many navigation items in the gutter.</p>
|
||||
`,
|
||||
};
|
||||
};
|
||||
|
||||
export let IconItems = () => {
|
||||
return {
|
||||
template: hbs`
|
||||
<h5 class="title is-5">Hypothetical gutter navigation with icon items</h5>
|
||||
<div class="columns">
|
||||
<div class="column is-4">
|
||||
<div class="gutter">
|
||||
<aside class="menu">
|
||||
<p class="menu-label">Places</p>
|
||||
<ul class="menu-list">
|
||||
<li><a href="javascript:;">{{x-icon "clock"}} Place One</a></li>
|
||||
<li><a href="javascript:;" class="is-active">{{x-icon "history"}} Place Two</a></li>
|
||||
</ul>
|
||||
|
||||
<p class="menu-label">Features</p>
|
||||
<ul class="menu-list">
|
||||
<li><a href="javascript:;">{{x-icon "alert-triangle"}} Feature One</a></li>
|
||||
<li><a href="javascript:;">{{x-icon "media-pause"}} Feature Two</a></li>
|
||||
</ul>
|
||||
</aside>
|
||||
</div>
|
||||
</div>
|
||||
<div class="column">
|
||||
<div class="mock-content">
|
||||
<div class="mock-vague"></div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<p class="annotation">In the future, the gutter menu may have icons.</p>
|
||||
`,
|
||||
};
|
||||
};
|
||||
|
||||
export let TaggedItems = () => {
|
||||
return {
|
||||
template: hbs`
|
||||
<h5 class="title is-5">Hypothetical gutter navigation with icon items</h5>
|
||||
<div class="columns">
|
||||
<div class="column is-4">
|
||||
<div class="gutter">
|
||||
<aside class="menu">
|
||||
<p class="menu-label">Places</p>
|
||||
<ul class="menu-list">
|
||||
<li><a href="javascript:;">Place One <span class="tag is-small">Beta</span></a></li>
|
||||
<li><a href="javascript:;" class="is-active">{{x-icon "history"}} Place Two</a></li>
|
||||
</ul>
|
||||
|
||||
<p class="menu-label">Features</p>
|
||||
<ul class="menu-list">
|
||||
<li><a href="javascript:;">{{x-icon "alert-triangle"}} Feature One</a></li>
|
||||
<li><a href="javascript:;">{{x-icon "media-pause"}} Feature Two <span class="tag is-small is-warning">3</span></a></li>
|
||||
</ul>
|
||||
</aside>
|
||||
</div>
|
||||
</div>
|
||||
<div class="column">
|
||||
<div class="mock-content">
|
||||
<div class="mock-vague"></div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<p class="annotation">Tags can be used to denote beta features or low-priority notifications.</p>
|
||||
`,
|
||||
};
|
||||
};
|
||||
|
||||
export let Global = () => {
|
||||
return {
|
||||
template: hbs`
|
||||
<h5 class="title is-5">Global gutter navigation</h5>
|
||||
<div class="columns">
|
||||
<div class="column is-4">
|
||||
<GutterMenu>
|
||||
{{!-- Page content here --}}
|
||||
</GutterMenu>
|
||||
</div>
|
||||
</div>
|
||||
<p class="annotation">Since there will only ever be one gutter menu in the UI, it makes sense to express the menu as a singleton component. This is what that singleton component looks like.</p>
|
||||
<p class="annotation"><strong>Note:</strong> Normally the gutter menu is rendered within a page layout and is fixed position. The columns shown in this example are only to imitate the actual width without applying fixed positioning.</p>
|
||||
`,
|
||||
};
|
||||
};
|
||||
@@ -1,33 +0,0 @@
|
||||
/**
|
||||
* Copyright (c) HashiCorp, Inc.
|
||||
* SPDX-License-Identifier: BUSL-1.1
|
||||
*/
|
||||
|
||||
import hbs from 'htmlbars-inline-precompile';
|
||||
|
||||
export default {
|
||||
title: 'Components/Header',
|
||||
};
|
||||
|
||||
export let Header = () => {
|
||||
return {
|
||||
template: hbs`
|
||||
<h5 class="title is-5">Global header</h5>
|
||||
<nav class="navbar is-primary">
|
||||
<div class="navbar-brand">
|
||||
<span class="gutter-toggle" aria-label="menu">
|
||||
<HamburgerMenu />
|
||||
</span>
|
||||
<span class="navbar-item is-logo">
|
||||
<NomadLogo />
|
||||
</span>
|
||||
</div>
|
||||
<div class="navbar-end">
|
||||
<a class="navbar-item">Secondary</a>
|
||||
<a class="navbar-item">Links</a>
|
||||
<a class="navbar-item">Here</a>
|
||||
</div>
|
||||
</nav>
|
||||
`,
|
||||
};
|
||||
};
|
||||
@@ -1,97 +0,0 @@
|
||||
/**
|
||||
* Copyright (c) HashiCorp, Inc.
|
||||
* SPDX-License-Identifier: BUSL-1.1
|
||||
*/
|
||||
|
||||
import hbs from 'htmlbars-inline-precompile';
|
||||
|
||||
export default {
|
||||
title: 'Components/Inline Definitions',
|
||||
};
|
||||
|
||||
export let Standard = () => {
|
||||
return {
|
||||
template: hbs`
|
||||
<h5 class="title is-5">Inline definitions</h5>
|
||||
<div class="boxed-section is-small">
|
||||
<div class="boxed-section-body inline-definitions">
|
||||
<span class="label">Some Label</span>
|
||||
<span class="pair">
|
||||
<span class="term">Term Name</span>
|
||||
<span>Term Value</span>
|
||||
</span>
|
||||
<span class="pair">
|
||||
<span class="term">Running?</span>
|
||||
<span>Yes</span>
|
||||
</span>
|
||||
<span class="pair">
|
||||
<span class="term">Last Updated</span>
|
||||
<span>{{format-ts (now)}}</span>
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
<p class="annotation">A way to tightly display key/value information. Typically seen at the top of pages.</p>
|
||||
`,
|
||||
};
|
||||
};
|
||||
|
||||
export let Variants = () => {
|
||||
return {
|
||||
template: hbs`
|
||||
<h5 class="title is-5">Inline definitions variants</h5>
|
||||
<div class="boxed-section is-small is-success">
|
||||
<div class="boxed-section-body inline-definitions">
|
||||
<span class="label">Success Label</span>
|
||||
<span class="pair">
|
||||
<span class="term">Term Name</span>
|
||||
<span>Term Value</span>
|
||||
</span>
|
||||
<span class="pair">
|
||||
<span class="term">Last Updated</span>
|
||||
<span>{{format-ts (now)}}</span>
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="boxed-section is-small is-warning">
|
||||
<div class="boxed-section-body inline-definitions">
|
||||
<span class="label">Warning Label</span>
|
||||
<span class="pair">
|
||||
<span class="term">Term Name</span>
|
||||
<span>Term Value</span>
|
||||
</span>
|
||||
<span class="pair">
|
||||
<span class="term">Last Updated</span>
|
||||
<span>{{format-ts (now)}}</span>
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="boxed-section is-small is-danger">
|
||||
<div class="boxed-section-body inline-definitions">
|
||||
<span class="label">Danger Label</span>
|
||||
<span class="pair">
|
||||
<span class="term">Term Name</span>
|
||||
<span>Term Value</span>
|
||||
</span>
|
||||
<span class="pair">
|
||||
<span class="term">Last Updated</span>
|
||||
<span>{{format-ts (now)}}</span>
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="boxed-section is-small is-info">
|
||||
<div class="boxed-section-body inline-definitions">
|
||||
<span class="label">Info Label</span>
|
||||
<span class="pair">
|
||||
<span class="term">Term Name</span>
|
||||
<span>Term Value</span>
|
||||
</span>
|
||||
<span class="pair">
|
||||
<span class="term">Last Updated</span>
|
||||
<span>{{format-ts (now)}}</span>
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
<p class="annotation">Inline definitions are meant to pair well with emotive color variations.</p>
|
||||
`,
|
||||
};
|
||||
};
|
||||
@@ -1,195 +0,0 @@
|
||||
/**
|
||||
* Copyright (c) HashiCorp, Inc.
|
||||
* SPDX-License-Identifier: BUSL-1.1
|
||||
*/
|
||||
|
||||
import hbs from 'htmlbars-inline-precompile';
|
||||
|
||||
import DelayedTruth from '../utils/delayed-truth';
|
||||
|
||||
export default {
|
||||
title: 'Components/JSON Viewer',
|
||||
};
|
||||
|
||||
export let Standard = () => {
|
||||
return {
|
||||
template: hbs`
|
||||
<h5 class="title is-5">JSON Viewer</h5>
|
||||
{{#if delayedTruth.complete}}
|
||||
<JsonViewer @json={{jsonSmall}} />
|
||||
{{/if}}
|
||||
`,
|
||||
context: {
|
||||
delayedTruth: DelayedTruth.create(),
|
||||
jsonSmall: {
|
||||
delayedData: {},
|
||||
data: {
|
||||
foo: 'bar',
|
||||
number: 123456789,
|
||||
products: [
|
||||
'Consul',
|
||||
'Nomad',
|
||||
'Packer',
|
||||
'Terraform',
|
||||
'Vagrant',
|
||||
'Vault',
|
||||
],
|
||||
currentTime: '2019-10-16T14:24:12.378Z',
|
||||
nested: {
|
||||
obj: 'ject',
|
||||
},
|
||||
nonexistent: null,
|
||||
isTrue: false,
|
||||
},
|
||||
},
|
||||
},
|
||||
};
|
||||
};
|
||||
|
||||
export let FullDocument = () => {
|
||||
return {
|
||||
template: hbs`
|
||||
<h5 class="title is-5">JSON Viewer for full document</h5>
|
||||
{{#if delayedTruth.complete}}
|
||||
<JsonViewer @json={{jsonLarge}} />
|
||||
{{/if}}
|
||||
`,
|
||||
context: {
|
||||
delayedTruth: DelayedTruth.create(),
|
||||
jsonLarge: {
|
||||
delayedData: {},
|
||||
data: {
|
||||
Stop: false,
|
||||
Region: 'global',
|
||||
Namespace: 'default',
|
||||
ID: 'syslog',
|
||||
ParentID: '',
|
||||
Name: 'syslog',
|
||||
Type: 'system',
|
||||
Priority: 50,
|
||||
AllAtOnce: false,
|
||||
Datacenters: ['dc1', 'dc2'],
|
||||
letraints: null,
|
||||
TaskGroups: [
|
||||
{
|
||||
Name: 'syslog',
|
||||
Count: 1,
|
||||
Update: {
|
||||
Stagger: 10000000000,
|
||||
MaxParallel: 1,
|
||||
HealthCheck: 'checks',
|
||||
MinHealthyTime: 10000000000,
|
||||
HealthyDeadline: 300000000000,
|
||||
ProgressDeadline: 600000000000,
|
||||
AutoRevert: false,
|
||||
Canary: 0,
|
||||
},
|
||||
Migrate: null,
|
||||
letraints: [
|
||||
{
|
||||
LTarget: '',
|
||||
RTarget: '',
|
||||
Operand: 'distinct_hosts',
|
||||
},
|
||||
],
|
||||
RestartPolicy: {
|
||||
Attempts: 10,
|
||||
Interval: 300000000000,
|
||||
Delay: 25000000000,
|
||||
Mode: 'delay',
|
||||
},
|
||||
Tasks: [
|
||||
{
|
||||
Name: 'syslog',
|
||||
Driver: 'docker',
|
||||
User: '',
|
||||
Config: {
|
||||
port_map: [
|
||||
{
|
||||
tcp: 601,
|
||||
udp: 514,
|
||||
},
|
||||
],
|
||||
image: 'balabit/syslog-ng:latest',
|
||||
},
|
||||
Env: null,
|
||||
Services: null,
|
||||
Vault: null,
|
||||
Templates: null,
|
||||
letraints: null,
|
||||
Resources: {
|
||||
CPU: 500,
|
||||
MemoryMB: 256,
|
||||
DiskMB: 0,
|
||||
IOPS: 0,
|
||||
Networks: [
|
||||
{
|
||||
Device: '',
|
||||
CIDR: '',
|
||||
IP: '',
|
||||
MBits: 10,
|
||||
ReservedPorts: [
|
||||
{
|
||||
Label: 'udp',
|
||||
Value: 514,
|
||||
},
|
||||
{
|
||||
Label: 'tcp',
|
||||
Value: 601,
|
||||
},
|
||||
],
|
||||
DynamicPorts: null,
|
||||
},
|
||||
],
|
||||
},
|
||||
DispatchPayload: null,
|
||||
Meta: null,
|
||||
KillTimeout: 5000000000,
|
||||
LogConfig: {
|
||||
MaxFiles: 10,
|
||||
MaxFileSizeMB: 10,
|
||||
},
|
||||
Artifacts: null,
|
||||
Leader: false,
|
||||
ShutdownDelay: 0,
|
||||
KillSignal: '',
|
||||
},
|
||||
],
|
||||
EphemeralDisk: {
|
||||
Sticky: false,
|
||||
SizeMB: 300,
|
||||
Migrate: false,
|
||||
},
|
||||
Meta: null,
|
||||
ReschedulePolicy: null,
|
||||
},
|
||||
],
|
||||
Update: {
|
||||
Stagger: 10000000000,
|
||||
MaxParallel: 1,
|
||||
HealthCheck: '',
|
||||
MinHealthyTime: 0,
|
||||
HealthyDeadline: 0,
|
||||
ProgressDeadline: 0,
|
||||
AutoRevert: false,
|
||||
Canary: 0,
|
||||
},
|
||||
Periodic: null,
|
||||
ParameterizedJob: null,
|
||||
Dispatched: false,
|
||||
Payload: null,
|
||||
Meta: null,
|
||||
VaultToken: '',
|
||||
Status: 'running',
|
||||
StatusDescription: '',
|
||||
Stable: false,
|
||||
Version: 0,
|
||||
SubmitTime: 1530052201331477800,
|
||||
CreateIndex: 27,
|
||||
ModifyIndex: 27,
|
||||
JobModifyIndex: 27,
|
||||
},
|
||||
},
|
||||
},
|
||||
};
|
||||
};
|
||||
@@ -1,71 +0,0 @@
|
||||
/**
|
||||
* Copyright (c) HashiCorp, Inc.
|
||||
* SPDX-License-Identifier: BUSL-1.1
|
||||
*/
|
||||
|
||||
import hbs from 'htmlbars-inline-precompile';
|
||||
|
||||
export default {
|
||||
title: 'Components/Log Stream',
|
||||
};
|
||||
|
||||
export let LogStream = () => {
|
||||
return {
|
||||
template: hbs`
|
||||
<h5 class="title is-5">Log stream</h5>
|
||||
<div class="boxed-section">
|
||||
<div class="boxed-section-head">
|
||||
<span>
|
||||
<button
|
||||
class="button {{if (eq mode1 "stdout") "is-info"}}"
|
||||
onclick={{action (mut mode1) "stdout"}}>stdout</button>
|
||||
<button
|
||||
class="button {{if (eq mode1 "stderr") "is-danger"}}"
|
||||
onclick={{action (mut mode1) "stderr"}}>stderr</button>
|
||||
</span>
|
||||
<span class="pull-right">
|
||||
<button class="button is-white">Head</button>
|
||||
<button class="button is-white">Tail</button>
|
||||
<button class="button is-white" onclick={{toggle "isPlaying1" this}}>
|
||||
{{x-icon (if isPlaying1 "media-play" "media-pause") class="is-text"}}
|
||||
</button>
|
||||
</span>
|
||||
</div>
|
||||
<div class="boxed-section-body is-dark is-full-bleed">
|
||||
<pre class="cli-window"><code>{{if (eq mode1 "stdout") sampleOutput sampleError}}</code></pre>
|
||||
</div>
|
||||
</div>
|
||||
`,
|
||||
context: {
|
||||
mode1: 'stdout',
|
||||
isPlaying1: true,
|
||||
|
||||
sampleOutput: `Sample output
|
||||
> 1
|
||||
> 2
|
||||
> 3
|
||||
[00:12:58] Log output here
|
||||
[00:15:29] [ERR] Uh oh
|
||||
Loading.
|
||||
Loading..
|
||||
Loading...
|
||||
|
||||
>> Done! <<
|
||||
|
||||
`,
|
||||
|
||||
sampleError: `Sample error
|
||||
|
||||
[====|--------------------] 20%
|
||||
|
||||
!!! Unrecoverable error:
|
||||
|
||||
Cannot continue beyond this point. Exception should be caught.
|
||||
This is not a mistake. You did something wrong. Check the code.
|
||||
No, you will not receive any more details or guidance from this
|
||||
error message.
|
||||
|
||||
`,
|
||||
},
|
||||
};
|
||||
};
|
||||
@@ -1,148 +0,0 @@
|
||||
/**
|
||||
* Copyright (c) HashiCorp, Inc.
|
||||
* SPDX-License-Identifier: BUSL-1.1
|
||||
*/
|
||||
|
||||
import hbs from 'htmlbars-inline-precompile';
|
||||
|
||||
export default {
|
||||
title: 'Components/Metrics',
|
||||
};
|
||||
|
||||
export let Standard = () => {
|
||||
return {
|
||||
template: hbs`
|
||||
<h5 class="title is-5">Metrics</h5>
|
||||
<div class="metric-group">
|
||||
<div class="metric">
|
||||
<h3 class="label">Label</h3>
|
||||
<p class="value">12</p>
|
||||
</div>
|
||||
</div>
|
||||
<p class="annotation">Metrics are a way to show simple values (generally numbers). Labels are smaller than numbers to put emphasis on the data.</p>
|
||||
`,
|
||||
};
|
||||
};
|
||||
|
||||
export let Groups = () => {
|
||||
return {
|
||||
template: hbs`
|
||||
<h5 class="title is-5">Metric groups</h5>
|
||||
<div class="metric-group">
|
||||
<div class="metric">
|
||||
<h3 class="label">Label</h3>
|
||||
<p class="value">1 / 2</p>
|
||||
</div>
|
||||
<div class="metric">
|
||||
<h3 class="label">Number</h3>
|
||||
<p class="value">1,300</p>
|
||||
</div>
|
||||
<div class="metric">
|
||||
<h3 class="label">Datacenter</h3>
|
||||
<p class="value">dc1</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="metric-group">
|
||||
<div class="metric">
|
||||
<h3 class="label">Today</h3>
|
||||
<p class="value">81º</p>
|
||||
</div>
|
||||
<div class="metric">
|
||||
<h3 class="label">Tomorrow</h3>
|
||||
<p class="value">73º</p>
|
||||
</div>
|
||||
</div>
|
||||
<p class="annotation">Related metrics should be lumped together in metric groups. All metrics have to be in a metric group. By putting multiple metrics in a single group, they will be visually lumped together.</p>
|
||||
`,
|
||||
};
|
||||
};
|
||||
|
||||
export let Colors = () => {
|
||||
return {
|
||||
template: hbs`
|
||||
<h5 class="title is-5">Metric colors</h5>
|
||||
<div class="metric-group">
|
||||
<div class="metric is-info">
|
||||
<h3 class="label">Info</h3>
|
||||
<p class="value">1</p>
|
||||
</div>
|
||||
<div class="metric is-success">
|
||||
<h3 class="label">Success</h3>
|
||||
<p class="value">2</p>
|
||||
</div>
|
||||
<div class="metric is-warning">
|
||||
<h3 class="label">Warning</h3>
|
||||
<p class="value">3</p>
|
||||
</div>
|
||||
<div class="metric is-danger">
|
||||
<h3 class="label">Danger</h3>
|
||||
<p class="value">4</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="metric-group">
|
||||
<div class="metric is-white">
|
||||
<h3 class="label">White</h3>
|
||||
<p class="value">5</p>
|
||||
</div>
|
||||
<div class="metric is-light">
|
||||
<h3 class="label">Light</h3>
|
||||
<p class="value">6</p>
|
||||
</div>
|
||||
<div class="metric is-primary">
|
||||
<h3 class="label">Primary</h3>
|
||||
<p class="value">7</p>
|
||||
</div>
|
||||
<div class="metric is-dark">
|
||||
<h3 class="label">Dark</h3>
|
||||
<p class="value">8</p>
|
||||
</div>
|
||||
<div class="metric is-black">
|
||||
<h3 class="label">Black</h3>
|
||||
<p class="value">9</p>
|
||||
</div>
|
||||
</div>
|
||||
<p class="annotation">All color-modifiers work for metrics, but some work better than others.</p>
|
||||
<p class="annotation">Emotive colors work well and are put to use when applicable. Other colors have worse support and less utility.</p>
|
||||
`,
|
||||
};
|
||||
};
|
||||
|
||||
export let States = () => {
|
||||
return {
|
||||
template: hbs`
|
||||
<h5 class="title is-5">Metric states</h5>
|
||||
<div class="metric-group">
|
||||
<div class="metric is-primary is-faded">
|
||||
<h3 class="label">One</h3>
|
||||
<p class="value">A</p>
|
||||
</div>
|
||||
<div class="metric is-primary">
|
||||
<h3 class="label">Two</h3>
|
||||
<p class="value">B</p>
|
||||
</div>
|
||||
<div class="metric is-primary is-faded">
|
||||
<h3 class="label">Three</h3>
|
||||
<p class="value">C</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="metric-group">
|
||||
<div class="metric is-danger is-faded">
|
||||
<h3 class="label">One</h3>
|
||||
<p class="value">A</p>
|
||||
</div>
|
||||
<div class="metric is-danger is-faded">
|
||||
<h3 class="label">Two</h3>
|
||||
<p class="value">B</p>
|
||||
</div>
|
||||
<div class="metric is-danger">
|
||||
<h3 class="label">Three</h3>
|
||||
<p class="value">C</p>
|
||||
</div>
|
||||
</div>
|
||||
<p class="annotation">Metrics have a disabled state. This is used when a metric is non-existent or irrelevant. It's just as important to show the lack of value as it is to show a value, so simply not rendering non-existent or irrelevant metrics would be worse.</p>
|
||||
`,
|
||||
};
|
||||
};
|
||||
@@ -1,39 +0,0 @@
|
||||
/**
|
||||
* Copyright (c) HashiCorp, Inc.
|
||||
* SPDX-License-Identifier: BUSL-1.1
|
||||
*/
|
||||
|
||||
import hbs from 'htmlbars-inline-precompile';
|
||||
|
||||
export default {
|
||||
title: 'Components/Page Tabs',
|
||||
};
|
||||
|
||||
export let Standard = () => {
|
||||
return {
|
||||
template: hbs`
|
||||
<h5 class="title is-5">Page tabs</h5>
|
||||
<div class="tabs">
|
||||
<ul>
|
||||
<li><a href="javascript:;">Overview</a></li>
|
||||
<li><a href="javascript:;" class="is-active">Definition</a></li>
|
||||
<li><a href="javascript:;">Versions</a></li>
|
||||
<li><a href="javascript:;">Deployments</a></li>
|
||||
</ul>
|
||||
</div>
|
||||
`,
|
||||
};
|
||||
};
|
||||
|
||||
export let Single = () => {
|
||||
return {
|
||||
template: hbs`
|
||||
<h5 class="title is-5">Single page tab</h5>
|
||||
<div class="tabs">
|
||||
<ul>
|
||||
<li><a href="javascript:;" class="is-active">Overview</a></li>
|
||||
</ul>
|
||||
</div>
|
||||
`,
|
||||
};
|
||||
};
|
||||
@@ -1,74 +0,0 @@
|
||||
/**
|
||||
* Copyright (c) HashiCorp, Inc.
|
||||
* SPDX-License-Identifier: BUSL-1.1
|
||||
*/
|
||||
|
||||
import hbs from 'htmlbars-inline-precompile';
|
||||
|
||||
export default {
|
||||
title: 'Components/Page Title',
|
||||
};
|
||||
|
||||
export let Standard = () => {
|
||||
return {
|
||||
template: hbs`
|
||||
<h5 class="title is-5">Page title</h5>
|
||||
<div class="mock-spacing">
|
||||
<h1 class="title">This is the Page Title</h1>
|
||||
</div>
|
||||
<p class="annotation">In its simplest form, a page title is just an H1.</p>
|
||||
`,
|
||||
};
|
||||
};
|
||||
|
||||
export let AfterElements = () => {
|
||||
return {
|
||||
template: hbs`
|
||||
<h5 class="title is-5">Page title with after elements</h5>
|
||||
<div class="mock-spacing">
|
||||
<h1 class="title">
|
||||
This is the Page Title
|
||||
<span class="bumper-left tag is-running">Running</span>
|
||||
<span class="tag is-hollow is-small no-text-transform">237aedcb8982fe09bcee0877acedd</span>
|
||||
</h1>
|
||||
</div>
|
||||
<p class="annotation">It is common to put high-impact tags and badges to the right of titles. These tags should only ever appear on the right-hand side of the title, and they should be listed in descending weights. Tags with a background are heavier than tags that are hollow. Longer values are heavier than shorter values.</p>
|
||||
`,
|
||||
};
|
||||
};
|
||||
|
||||
export let StatusLight = () => {
|
||||
return {
|
||||
template: hbs`
|
||||
<h5 class="title is-5">Page title with status light</h5>
|
||||
<div class="mock-spacing">
|
||||
<h1 class="title">
|
||||
<span class="node-status-light initializing"></span>
|
||||
This is the Page Title
|
||||
<span class="bumper-left tag is-running">Running</span>
|
||||
<span class="tag is-hollow is-small no-text-transform">237aedcb8982fe09bcee0877acedd</span>
|
||||
</h1>
|
||||
</div>
|
||||
<p class="annotation">A simple color or pattern is faster to scan than a title and can often say more than words can. For pages that have an important status component to them (e.g., client detail page), a status light can be shown to the left of the title where typically eyes will begin to scan a page.</p>
|
||||
`,
|
||||
};
|
||||
};
|
||||
|
||||
export let Actions = () => {
|
||||
return {
|
||||
template: hbs`
|
||||
<h5 class="title is-5">Page title with actions</h5>
|
||||
<div class="mock-spacing">
|
||||
<h1 class="title">
|
||||
<span class="node-status-light initializing"></span>
|
||||
This is the Page Title
|
||||
<span class="bumper-left tag is-running">Running</span>
|
||||
<span class="tag is-hollow is-small no-text-transform">237aedcb8982fe09bcee0877acedd</span>
|
||||
<button class="button is-warning is-small is-inline">If you wish</button>
|
||||
<button class="button is-danger is-outlined is-important is-small is-inline">No Regrets</button>
|
||||
</h1>
|
||||
</div>
|
||||
<p class="annotation">When actions apply to the entire context of a page, (e.g., job actions on the job detail page), buttons for these actions go in the page title. Buttons are always placed on the far right end of a page title. No elements can go to the right of these buttons.</p>
|
||||
`,
|
||||
};
|
||||
};
|
||||
@@ -1,19 +0,0 @@
|
||||
/**
|
||||
* Copyright (c) HashiCorp, Inc.
|
||||
* SPDX-License-Identifier: BUSL-1.1
|
||||
*/
|
||||
|
||||
import hbs from 'htmlbars-inline-precompile';
|
||||
|
||||
export default {
|
||||
title: 'Components/Proxy Tag',
|
||||
};
|
||||
|
||||
export let ProxyTag = () => {
|
||||
return {
|
||||
template: hbs`
|
||||
<h5 class="title is-5">Proxy Tag</h5>
|
||||
<h6 class="title is-6">Some kind of title <ProxyTag/></h6>
|
||||
`,
|
||||
};
|
||||
};
|
||||
@@ -1,36 +0,0 @@
|
||||
/**
|
||||
* Copyright (c) HashiCorp, Inc.
|
||||
* SPDX-License-Identifier: BUSL-1.1
|
||||
*/
|
||||
|
||||
import hbs from 'htmlbars-inline-precompile';
|
||||
|
||||
export default {
|
||||
title: 'Components/Search Box',
|
||||
};
|
||||
|
||||
export let Standard = () => {
|
||||
return {
|
||||
template: hbs`
|
||||
<h5 class="title is-5">Search Box</h5>
|
||||
<SearchBox
|
||||
@searchTerm={{mut searchTerm1}}
|
||||
@placeholder="Search things..." />
|
||||
<p class="annotation">The search box component is a thin wrapper around a simple input. Although the searchTerm passed to it is a mutable reference, internally search term is debounced. This is to prevent potentially expensive code that depends on searchTerm from recomputing many times as a user types.</p>
|
||||
<p class="annotation">There is no form of the search box component that defers updating the searchTerm reference until the user manually clicks a "Search" button. This can be achieved by placing a button next to the search bar component and using it to perform search, but search should be automatic whenever possible.</p>
|
||||
`,
|
||||
};
|
||||
};
|
||||
|
||||
export let Compact = () => {
|
||||
return {
|
||||
template: hbs`
|
||||
<h5 class="title is-5">Compact Search Box</h5>
|
||||
<SearchBox
|
||||
@searchTerm={{mut searchTerm2}}
|
||||
@placeholder="Search things..."
|
||||
@inputClass="is-compact" />
|
||||
<p class="annotation">Search box provides an inputClass property to control the inner input. This is nice for fitting the search box into smaller spaces, such as boxed-section heads.</p>
|
||||
`,
|
||||
};
|
||||
};
|
||||
@@ -1,53 +0,0 @@
|
||||
/**
|
||||
* Copyright (c) HashiCorp, Inc.
|
||||
* SPDX-License-Identifier: BUSL-1.1
|
||||
*/
|
||||
|
||||
import hbs from 'htmlbars-inline-precompile';
|
||||
import { withKnobs, optionsKnob } from '@storybook/addon-knobs';
|
||||
|
||||
export default {
|
||||
title: 'Components/Stepper Input',
|
||||
decorators: [withKnobs],
|
||||
};
|
||||
|
||||
const variantKnob = () =>
|
||||
optionsKnob(
|
||||
'Variant',
|
||||
{
|
||||
Primary: 'is-primary',
|
||||
Info: 'is-info',
|
||||
Warning: 'is-warning',
|
||||
Danger: 'is-danger',
|
||||
},
|
||||
'is-primary',
|
||||
{
|
||||
display: 'inline-radio',
|
||||
},
|
||||
'variant-id'
|
||||
);
|
||||
|
||||
export let Standard = () => {
|
||||
return {
|
||||
template: hbs`
|
||||
<p class="mock-spacing">
|
||||
<StepperInput
|
||||
@value={{value}}
|
||||
@min={{min}}
|
||||
@max={{max}}
|
||||
@class={{variant}}
|
||||
@onChange={{action (mut value)}}>
|
||||
Stepper
|
||||
</StepperInput>
|
||||
<button class="button is-info">Button for Context</button>
|
||||
</p>
|
||||
<p class="mock-spacing"><strong>External Value:</strong> {{value}}</p>
|
||||
`,
|
||||
context: {
|
||||
min: 0,
|
||||
max: 10,
|
||||
value: 5,
|
||||
variant: variantKnob(),
|
||||
},
|
||||
};
|
||||
};
|
||||
@@ -1,45 +0,0 @@
|
||||
/**
|
||||
* Copyright (c) HashiCorp, Inc.
|
||||
* SPDX-License-Identifier: BUSL-1.1
|
||||
*/
|
||||
|
||||
import hbs from 'htmlbars-inline-precompile';
|
||||
|
||||
export default {
|
||||
title: 'Components/Table, Configuration',
|
||||
};
|
||||
|
||||
export let TableConfiguration = () => {
|
||||
return {
|
||||
template: hbs`
|
||||
<h5 class="title is-5">Table, configuration</h5>
|
||||
<AttributesTable @attributePairs={{attributes}} @class="attributes-table" />
|
||||
`,
|
||||
context: {
|
||||
attributes: {
|
||||
key: 'val',
|
||||
deep: {
|
||||
key: 'val',
|
||||
more: 'stuff',
|
||||
},
|
||||
array: ['one', 'two', 'three', 'four'],
|
||||
very: {
|
||||
deep: {
|
||||
key: {
|
||||
incoming: {
|
||||
one: 1,
|
||||
two: 2,
|
||||
three: 3,
|
||||
four: 'surprisingly long value that is unlike the other properties in this object',
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
};
|
||||
};
|
||||
|
||||
TableConfiguration.story = {
|
||||
title: 'Table, Configuration',
|
||||
};
|
||||
@@ -1,763 +0,0 @@
|
||||
/**
|
||||
* Copyright (c) HashiCorp, Inc.
|
||||
* SPDX-License-Identifier: BUSL-1.1
|
||||
*/
|
||||
|
||||
import hbs from 'htmlbars-inline-precompile';
|
||||
import productMetadata from '../../app/utils/styleguide/product-metadata';
|
||||
|
||||
import EmberObject, { computed } from '@ember/object';
|
||||
|
||||
import { getOwner } from '@ember/application';
|
||||
import { on } from '@ember/object/evented';
|
||||
import Controller from '@ember/controller';
|
||||
|
||||
export default {
|
||||
title: 'Components/Table',
|
||||
};
|
||||
|
||||
/**
|
||||
* The Ember integration for Storybook renders a container component with no routing,
|
||||
* which means things that need query parameters, like sorting and pagination, won’t work.
|
||||
|
||||
* This initialiser turns on routing and accepts a controller definition that gets wired up
|
||||
* to a generated `storybook` route. The controller is attached to the Storybook component
|
||||
* as the `controller` property so its query parameters are accessible from the template.
|
||||
*/
|
||||
function injectRoutedController(controllerClass) {
|
||||
return on('init', function () {
|
||||
let container = getOwner(this);
|
||||
container.register('controller:storybook', controllerClass);
|
||||
|
||||
let routerFactory = container.factoryFor('router:main');
|
||||
routerFactory.class.map(function () {
|
||||
this.route('storybook');
|
||||
});
|
||||
|
||||
/* eslint-disable-next-line ember/no-private-routing-service */
|
||||
let router = container.lookup('router:main');
|
||||
router.initialURL = 'storybook';
|
||||
router.startRouting(true);
|
||||
|
||||
this.set('controller', container.lookup('controller:storybook'));
|
||||
});
|
||||
}
|
||||
|
||||
let longList = [
|
||||
{
|
||||
city: 'New York',
|
||||
growth: 0.048,
|
||||
population: '8405837',
|
||||
rank: '1',
|
||||
state: 'New York',
|
||||
},
|
||||
{
|
||||
city: 'Los Angeles',
|
||||
growth: 0.048,
|
||||
population: '3884307',
|
||||
rank: '2',
|
||||
state: 'California',
|
||||
},
|
||||
{
|
||||
city: 'Chicago',
|
||||
growth: -0.061,
|
||||
population: '2718782',
|
||||
rank: '3',
|
||||
state: 'Illinois',
|
||||
},
|
||||
{
|
||||
city: 'Houston',
|
||||
growth: 0.11,
|
||||
population: '2195914',
|
||||
rank: '4',
|
||||
state: 'Texas',
|
||||
},
|
||||
{
|
||||
city: 'Philadelphia',
|
||||
growth: 0.026,
|
||||
population: '1553165',
|
||||
rank: '5',
|
||||
state: 'Pennsylvania',
|
||||
},
|
||||
{
|
||||
city: 'Phoenix',
|
||||
growth: 0.14,
|
||||
population: '1513367',
|
||||
rank: '6',
|
||||
state: 'Arizona',
|
||||
},
|
||||
{
|
||||
city: 'San Antonio',
|
||||
growth: 0.21,
|
||||
population: '1409019',
|
||||
rank: '7',
|
||||
state: 'Texas',
|
||||
},
|
||||
{
|
||||
city: 'San Diego',
|
||||
growth: 0.105,
|
||||
population: '1355896',
|
||||
rank: '8',
|
||||
state: 'California',
|
||||
},
|
||||
{
|
||||
city: 'Dallas',
|
||||
growth: 0.056,
|
||||
population: '1257676',
|
||||
rank: '9',
|
||||
state: 'Texas',
|
||||
},
|
||||
{
|
||||
city: 'San Jose',
|
||||
growth: 0.105,
|
||||
population: '998537',
|
||||
rank: '10',
|
||||
state: 'California',
|
||||
},
|
||||
{
|
||||
city: 'Austin',
|
||||
growth: 0.317,
|
||||
population: '885400',
|
||||
rank: '11',
|
||||
state: 'Texas',
|
||||
},
|
||||
{
|
||||
city: 'Indianapolis',
|
||||
growth: 0.078,
|
||||
population: '843393',
|
||||
rank: '12',
|
||||
state: 'Indiana',
|
||||
},
|
||||
{
|
||||
city: 'Jacksonville',
|
||||
growth: 0.143,
|
||||
population: '842583',
|
||||
rank: '13',
|
||||
state: 'Florida',
|
||||
},
|
||||
{
|
||||
city: 'San Francisco',
|
||||
growth: 0.077,
|
||||
population: '837442',
|
||||
rank: '14',
|
||||
state: 'California',
|
||||
},
|
||||
{
|
||||
city: 'Columbus',
|
||||
growth: 0.148,
|
||||
population: '822553',
|
||||
rank: '15',
|
||||
state: 'Ohio',
|
||||
},
|
||||
{
|
||||
city: 'Charlotte',
|
||||
growth: 0.391,
|
||||
population: '792862',
|
||||
rank: '16',
|
||||
state: 'North Carolina',
|
||||
},
|
||||
{
|
||||
city: 'Fort Worth',
|
||||
growth: 0.451,
|
||||
population: '792727',
|
||||
rank: '17',
|
||||
state: 'Texas',
|
||||
},
|
||||
{
|
||||
city: 'Detroit',
|
||||
growth: -0.271,
|
||||
population: '688701',
|
||||
rank: '18',
|
||||
state: 'Michigan',
|
||||
},
|
||||
{
|
||||
city: 'El Paso',
|
||||
growth: 0.194,
|
||||
population: '674433',
|
||||
rank: '19',
|
||||
state: 'Texas',
|
||||
},
|
||||
{
|
||||
city: 'Memphis',
|
||||
growth: -0.053,
|
||||
population: '653450',
|
||||
rank: '20',
|
||||
state: 'Tennessee',
|
||||
},
|
||||
{
|
||||
city: 'Seattle',
|
||||
growth: 0.156,
|
||||
population: '652405',
|
||||
rank: '21',
|
||||
state: 'Washington',
|
||||
},
|
||||
{
|
||||
city: 'Denver',
|
||||
growth: 0.167,
|
||||
population: '649495',
|
||||
rank: '22',
|
||||
state: 'Colorado',
|
||||
},
|
||||
{
|
||||
city: 'Washington',
|
||||
growth: 0.13,
|
||||
population: '646449',
|
||||
rank: '23',
|
||||
state: 'District of Columbia',
|
||||
},
|
||||
{
|
||||
city: 'Boston',
|
||||
growth: 0.094,
|
||||
population: '645966',
|
||||
rank: '24',
|
||||
state: 'Massachusetts',
|
||||
},
|
||||
{
|
||||
city: 'Nashville-Davidson',
|
||||
growth: 0.162,
|
||||
population: '634464',
|
||||
rank: '25',
|
||||
state: 'Tennessee',
|
||||
},
|
||||
{
|
||||
city: 'Baltimore',
|
||||
growth: -0.04,
|
||||
population: '622104',
|
||||
rank: '26',
|
||||
state: 'Maryland',
|
||||
},
|
||||
{
|
||||
city: 'Oklahoma City',
|
||||
growth: 0.202,
|
||||
population: '610613',
|
||||
rank: '27',
|
||||
state: 'Oklahoma',
|
||||
},
|
||||
{
|
||||
city: 'Louisville/Jefferson County',
|
||||
growth: 0.1,
|
||||
population: '609893',
|
||||
rank: '28',
|
||||
state: 'Kentucky',
|
||||
},
|
||||
{
|
||||
city: 'Portland',
|
||||
growth: 0.15,
|
||||
population: '609456',
|
||||
rank: '29',
|
||||
state: 'Oregon',
|
||||
},
|
||||
{
|
||||
city: 'Las Vegas',
|
||||
growth: 0.245,
|
||||
population: '603488',
|
||||
rank: '30',
|
||||
state: 'Nevada',
|
||||
},
|
||||
{
|
||||
city: 'Milwaukee',
|
||||
growth: 0.003,
|
||||
population: '599164',
|
||||
rank: '31',
|
||||
state: 'Wisconsin',
|
||||
},
|
||||
{
|
||||
city: 'Albuquerque',
|
||||
growth: 0.235,
|
||||
population: '556495',
|
||||
rank: '32',
|
||||
state: 'New Mexico',
|
||||
},
|
||||
{
|
||||
city: 'Tucson',
|
||||
growth: 0.075,
|
||||
population: '526116',
|
||||
rank: '33',
|
||||
state: 'Arizona',
|
||||
},
|
||||
{
|
||||
city: 'Fresno',
|
||||
growth: 0.183,
|
||||
population: '509924',
|
||||
rank: '34',
|
||||
state: 'California',
|
||||
},
|
||||
{
|
||||
city: 'Sacramento',
|
||||
growth: 0.172,
|
||||
population: '479686',
|
||||
rank: '35',
|
||||
state: 'California',
|
||||
},
|
||||
{
|
||||
city: 'Long Beach',
|
||||
growth: 0.015,
|
||||
population: '469428',
|
||||
rank: '36',
|
||||
state: 'California',
|
||||
},
|
||||
{
|
||||
city: 'Kansas City',
|
||||
growth: 0.055,
|
||||
population: '467007',
|
||||
rank: '37',
|
||||
state: 'Missouri',
|
||||
},
|
||||
{
|
||||
city: 'Mesa',
|
||||
growth: 0.135,
|
||||
population: '457587',
|
||||
rank: '38',
|
||||
state: 'Arizona',
|
||||
},
|
||||
{
|
||||
city: 'Virginia Beach',
|
||||
growth: 0.051,
|
||||
population: '448479',
|
||||
rank: '39',
|
||||
state: 'Virginia',
|
||||
},
|
||||
{
|
||||
city: 'Atlanta',
|
||||
growth: 0.062,
|
||||
population: '447841',
|
||||
rank: '40',
|
||||
state: 'Georgia',
|
||||
},
|
||||
{
|
||||
city: 'Colorado Springs',
|
||||
growth: 0.214,
|
||||
population: '439886',
|
||||
rank: '41',
|
||||
state: 'Colorado',
|
||||
},
|
||||
{
|
||||
city: 'Omaha',
|
||||
growth: 0.059,
|
||||
population: '434353',
|
||||
rank: '42',
|
||||
state: 'Nebraska',
|
||||
},
|
||||
{
|
||||
city: 'Raleigh',
|
||||
growth: 0.487,
|
||||
population: '431746',
|
||||
rank: '43',
|
||||
state: 'North Carolina',
|
||||
},
|
||||
{
|
||||
city: 'Miami',
|
||||
growth: 0.149,
|
||||
population: '417650',
|
||||
rank: '44',
|
||||
state: 'Florida',
|
||||
},
|
||||
{
|
||||
city: 'Oakland',
|
||||
growth: 0.013,
|
||||
population: '406253',
|
||||
rank: '45',
|
||||
state: 'California',
|
||||
},
|
||||
{
|
||||
city: 'Minneapolis',
|
||||
growth: 0.045,
|
||||
population: '400070',
|
||||
rank: '46',
|
||||
state: 'Minnesota',
|
||||
},
|
||||
{
|
||||
city: 'Tulsa',
|
||||
growth: 0.013,
|
||||
population: '398121',
|
||||
rank: '47',
|
||||
state: 'Oklahoma',
|
||||
},
|
||||
{
|
||||
city: 'Cleveland',
|
||||
growth: -0.181,
|
||||
population: '390113',
|
||||
rank: '48',
|
||||
state: 'Ohio',
|
||||
},
|
||||
{
|
||||
city: 'Wichita',
|
||||
growth: 0.097,
|
||||
population: '386552',
|
||||
rank: '49',
|
||||
state: 'Kansas',
|
||||
},
|
||||
{
|
||||
city: 'Arlington',
|
||||
growth: 0.133,
|
||||
population: '379577',
|
||||
rank: '50',
|
||||
state: 'Texas',
|
||||
},
|
||||
];
|
||||
|
||||
export let Standard = () => {
|
||||
return {
|
||||
template: hbs`
|
||||
<h5 class="title is-5">Table</h5>
|
||||
<ListTable @source={{shortList}} as |t|>
|
||||
<t.head>
|
||||
<th>Name</th>
|
||||
<th>Language</th>
|
||||
<th>Description</th>
|
||||
</t.head>
|
||||
<t.body @key="model.name" as |row|>
|
||||
<tr>
|
||||
<td>{{row.model.name}}</td>
|
||||
<td>{{row.model.lang}}</td>
|
||||
<td>{{row.model.desc}}</td>
|
||||
</tr>
|
||||
</t.body>
|
||||
</ListTable>
|
||||
<p class="annotation">Tables have airy designs with a minimal amount of borders. This maximizes their utility.</p>
|
||||
`,
|
||||
context: {
|
||||
shortList: productMetadata,
|
||||
},
|
||||
};
|
||||
};
|
||||
|
||||
export let Search = () => {
|
||||
return {
|
||||
template: hbs`
|
||||
<h5 class="title is-5">Table search</h5>
|
||||
<div class="boxed-section">
|
||||
<div class="boxed-section-head">
|
||||
Table Name
|
||||
<SearchBox
|
||||
@searchTerm={{mut controller.searchTerm}}
|
||||
@placeholder="Search..."
|
||||
@class="is-inline pull-right"
|
||||
@inputClass="is-compact" />
|
||||
</div>
|
||||
<div class="boxed-section-body {{if controller.filteredShortList.length "is-full-bleed"}}">
|
||||
{{#if controller.filteredShortList.length}}
|
||||
<ListTable @source={{controller.filteredShortList}} as |t|>
|
||||
<t.head>
|
||||
<th>Name</th>
|
||||
<th>Language</th>
|
||||
<th>Description</th>
|
||||
</t.head>
|
||||
<t.body @key="model.name" as |row|>
|
||||
<tr>
|
||||
<td>{{row.model.name}}</td>
|
||||
<td>{{row.model.lang}}</td>
|
||||
<td>{{row.model.desc}}</td>
|
||||
</tr>
|
||||
</t.body>
|
||||
</ListTable>
|
||||
{{else}}
|
||||
<div class="empty-message">
|
||||
<h3 class="empty-message-headline">No Matches</h3>
|
||||
<p class="empty-message-body">No products match your query.</p>
|
||||
</div>
|
||||
{{/if}}
|
||||
</div>
|
||||
</div>
|
||||
<p class="annotation">Tables compose with boxed-section and boxed-section composes with search box.</p>
|
||||
`,
|
||||
context: {
|
||||
controller: EmberObject.extend({
|
||||
searchTerm: '',
|
||||
|
||||
filteredShortList: computed('searchTerm', function () {
|
||||
let term = this.searchTerm.toLowerCase();
|
||||
return productMetadata.filter((product) =>
|
||||
product.name.toLowerCase().includes(term)
|
||||
);
|
||||
}),
|
||||
}).create(),
|
||||
},
|
||||
};
|
||||
};
|
||||
|
||||
export let SortableColumns = () => {
|
||||
return {
|
||||
template: hbs`
|
||||
<h5 class="title is-5">Table with sortable columns</h5>
|
||||
<ListTable @source={{sortedShortList}} @sortProperty={{controller.sortProperty}} @sortDescending={{controller.sortDescending}} as |t|>
|
||||
<t.head>
|
||||
<t.sort-by @prop="name">Name</t.sort-by>
|
||||
<t.sort-by @prop="lang" @class="is-2">Language</t.sort-by>
|
||||
<th>Description</th>
|
||||
</t.head>
|
||||
<t.body @key="model.name" as |row|>
|
||||
<tr>
|
||||
<td>{{row.model.name}}</td>
|
||||
<td>{{row.model.lang}}</td>
|
||||
<td>{{row.model.desc}}</td>
|
||||
</tr>
|
||||
</t.body>
|
||||
</ListTable>
|
||||
<p class="annotation">The list-table component provides a <code>sort-by</code> contextual component for building <code>link-to</code> components with the appropriate query params.</p>
|
||||
<p class="annotation">This leaves the component stateless, relying on data to be passed down and sending actions back up via the router (via link-to).</p>
|
||||
`,
|
||||
context: {
|
||||
injectRoutedController: injectRoutedController(
|
||||
Controller.extend({
|
||||
queryParams: ['sortProperty', 'sortDescending'],
|
||||
sortProperty: 'name',
|
||||
sortDescending: false,
|
||||
})
|
||||
),
|
||||
|
||||
sortedShortList: computed(
|
||||
'controller.{sortProperty,sortDescending}',
|
||||
function () {
|
||||
let sorted = productMetadata.sortBy(
|
||||
this.get('controller.sortProperty') || 'name'
|
||||
);
|
||||
return this.get('controller.sortDescending')
|
||||
? sorted.reverse()
|
||||
: sorted;
|
||||
}
|
||||
),
|
||||
},
|
||||
};
|
||||
};
|
||||
|
||||
export let MultiRow = () => {
|
||||
return {
|
||||
template: hbs`
|
||||
<h5 class="title is-5">Multi-row Table</h5>
|
||||
<ListTable @source={{sortedShortList}} @sortProperty={{controller.sortProperty}} @sortDescending={{controller.sortDescending}} @class="is-striped" as |t|>
|
||||
<t.head>
|
||||
<t.sort-by @prop="name">Name</t.sort-by>
|
||||
<t.sort-by @prop="lang">Language</t.sort-by>
|
||||
</t.head>
|
||||
<t.body @key="model.name" as |row|>
|
||||
<tr>
|
||||
<td>{{row.model.name}}</td>
|
||||
<td>{{row.model.lang}}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td colspan="2">{{row.model.desc}}</td>
|
||||
</tr>
|
||||
</t.body>
|
||||
</ListTable>
|
||||
<p class="annotation">The list-table component attempts to be as flexible as possible. For this reason, <code>t.body</code> does not provide the typical <code>tr</code> element. It's sometimes desired to have multiple elements per record.</p>
|
||||
`,
|
||||
context: {
|
||||
injectRoutedController: injectRoutedController(
|
||||
Controller.extend({
|
||||
queryParams: ['sortProperty', 'sortDescending'],
|
||||
sortProperty: 'name',
|
||||
sortDescending: false,
|
||||
})
|
||||
),
|
||||
|
||||
sortedShortList: computed(
|
||||
'controller.{sortProperty,sortDescending}',
|
||||
function () {
|
||||
let sorted = productMetadata.sortBy(
|
||||
this.get('controller.sortProperty') || 'name'
|
||||
);
|
||||
return this.get('controller.sortDescending')
|
||||
? sorted.reverse()
|
||||
: sorted;
|
||||
}
|
||||
),
|
||||
},
|
||||
};
|
||||
};
|
||||
|
||||
export let Pagination = () => {
|
||||
return {
|
||||
template: hbs`
|
||||
<h5 class="title is-5">Table pagination</h5>
|
||||
<ListPagination @source={{longList}} @size={{5}} @page={{controller.currentPage}} as |p|>
|
||||
<ListTable @source={{p.list}} @class="with-foot" as |t|>
|
||||
<t.head>
|
||||
<th class="is-1">Rank</th>
|
||||
<th>City</th>
|
||||
<th>State</th>
|
||||
<th>Population</th>
|
||||
<th>Growth</th>
|
||||
</t.head>
|
||||
<t.body @key="model.rank" as |row|>
|
||||
<tr>
|
||||
<td>{{row.model.rank}}</td>
|
||||
<td>{{row.model.city}}</td>
|
||||
<td>{{row.model.state}}</td>
|
||||
<td>{{row.model.population}}</td>
|
||||
<td>{{format-percentage row.model.growth total=1}}</td>
|
||||
</tr>
|
||||
</t.body>
|
||||
</ListTable>
|
||||
<div class="table-foot">
|
||||
<nav class="pagination">
|
||||
<span class="bumper-left">U.S. City population and growth from 2000-2013</span>
|
||||
<div class="pagination-numbers">
|
||||
{{p.startsAt}}–{{p.endsAt}} of {{longList.length}}
|
||||
</div>
|
||||
<p.prev @class="pagination-previous"> < </p.prev>
|
||||
<p.next @class="pagination-next"> > </p.next>
|
||||
<ul class="pagination-list"></ul>
|
||||
</nav>
|
||||
</div>
|
||||
</ListPagination>
|
||||
<p class="annotation">Pagination works like sorting: using <code>link-to</code>s to set a query param.</p>
|
||||
<p class="annotation">Pagination, like Table, is a minimal design. Only a next and previous button are available. The current place in the set of pages is tracked by showing which slice of items is currently shown.</p>
|
||||
<p class="annotation">The pagination component exposes first and last components (for jumping to the beginning and end of a list) as well as pageLinks for generating links around the current page.</p>
|
||||
`,
|
||||
context: {
|
||||
injectRoutedController: injectRoutedController(
|
||||
Controller.extend({
|
||||
queryParams: ['currentPage'],
|
||||
currentPage: 1,
|
||||
})
|
||||
),
|
||||
longList,
|
||||
},
|
||||
};
|
||||
};
|
||||
|
||||
export let RowLinks = () => {
|
||||
return {
|
||||
template: hbs`
|
||||
<h5 class="title is-5">Table row links</h5>
|
||||
<ListTable @source={{shortList}} as |t|>
|
||||
<t.head>
|
||||
<th>Name</th>
|
||||
<th>Language</th>
|
||||
<th>Description</th>
|
||||
</t.head>
|
||||
<t.body @key="model.name" as |row|>
|
||||
<tr class="is-interactive">
|
||||
<td><a href="javascript:;" class="is-primary">{{row.model.name}}</a></td>
|
||||
<td>{{row.model.lang}}</td>
|
||||
<td>{{row.model.desc}}</td>
|
||||
</tr>
|
||||
</t.body>
|
||||
</ListTable>
|
||||
<p class="annotation">It is common for tables to act as lists of links, (e.g., clients list all allocations, each row links to the allocation detail). The helper class <code>is-interactive</code> on the <code>tr</code> makes table rows have a pointer cursor. The helper class <code>is-primary</code> on the <code>a</code> element in a table row makes the link bold and black instead of blue. This makes the link stand out less, since the entire row is a link.</p>
|
||||
<p class="annotation">
|
||||
A few rules for using table row links:
|
||||
<ol>
|
||||
<li>The <code>is-primary</code> cell should always be the first cell</li>
|
||||
<li>The <code>is-primary</code> cell should always contain a link to the destination in the form of an <code>a</code> element. This is to support opening a link in a new tab.</li>
|
||||
<li>The full row should transition to the destination on click. This is to improve the usability of a table by creating a larger click area.</li>
|
||||
</ol>
|
||||
</p>
|
||||
`,
|
||||
context: {
|
||||
shortList: productMetadata,
|
||||
},
|
||||
};
|
||||
};
|
||||
|
||||
export let CellLinks = () => {
|
||||
return {
|
||||
template: hbs`
|
||||
<h5 class="title is-5">Table cell links</h5>
|
||||
<ListTable @source={{shortList}} as |t|>
|
||||
<t.head>
|
||||
<th>Name</th>
|
||||
<th>Language</th>
|
||||
<th>Description</th>
|
||||
</t.head>
|
||||
<t.body @key="model.name" as |row|>
|
||||
<tr>
|
||||
<td><a href={{row.model.link}} target="_parent">{{row.model.name}}</a></td>
|
||||
<td>{{row.model.lang}}</td>
|
||||
<td>{{row.model.desc}}</td>
|
||||
</tr>
|
||||
</t.body>
|
||||
</ListTable>
|
||||
<p class="annotation">Links in table cells are just links.</p>
|
||||
`,
|
||||
context: {
|
||||
shortList: productMetadata,
|
||||
},
|
||||
};
|
||||
};
|
||||
|
||||
export let CellDecorations = () => {
|
||||
return {
|
||||
template: hbs`
|
||||
<h5 class="title is-5">Table cell decorations</h5>
|
||||
<ListTable @source={{shortList}} as |t|>
|
||||
<t.head>
|
||||
<th>Name</th>
|
||||
<th>Language</th>
|
||||
<th>Description</th>
|
||||
</t.head>
|
||||
<t.body @key="model.name" as |row|>
|
||||
<tr>
|
||||
<td><a href={{row.model.link}}>{{row.model.name}}</a></td>
|
||||
<td class="nowrap">
|
||||
<span class="color-swatch
|
||||
{{if (eq row.model.lang "ruby") "swatch-6"}}
|
||||
{{if (eq row.model.lang "golang") "swatch-5"}}" />
|
||||
{{row.model.lang}}
|
||||
</td>
|
||||
<td>{{row.model.desc}}</td>
|
||||
</tr>
|
||||
</t.body>
|
||||
</ListTable>
|
||||
<p class="annotation">Small icons and accents of color make tables easier to scan.</p>
|
||||
`,
|
||||
context: {
|
||||
shortList: productMetadata,
|
||||
},
|
||||
};
|
||||
};
|
||||
|
||||
export let CellIcons = () => {
|
||||
return {
|
||||
template: hbs`
|
||||
<h5 class="title is-5">Table cell icons</h5>
|
||||
<ListPagination @source={{longList}} @size={{5}} @page={{controller.currentPage}} as |p|>
|
||||
<ListTable @source={{p.list}} @class="with-foot" as |t|>
|
||||
<t.head>
|
||||
<th class="is-narrow"></th>
|
||||
<th class="is-1">Rank</th>
|
||||
<th>City</th>
|
||||
<th>State</th>
|
||||
<th>Population</th>
|
||||
<th>Growth</th>
|
||||
</t.head>
|
||||
<t.body @key="model.rank" as |row|>
|
||||
<tr>
|
||||
<td class="is-narrow">
|
||||
{{#if (lt row.model.growth 0)}}
|
||||
{{x-icon "alert-triangle" class="is-warning"}}
|
||||
{{/if}}
|
||||
</td>
|
||||
<td>{{row.model.rank}}</td>
|
||||
<td>{{row.model.city}}</td>
|
||||
<td>{{row.model.state}}</td>
|
||||
<td>{{row.model.population}}</td>
|
||||
<td>{{format-percentage row.model.growth total=1}}</td>
|
||||
</tr>
|
||||
</t.body>
|
||||
</ListTable>
|
||||
<div class="table-foot">
|
||||
<nav class="pagination">
|
||||
<span class="bumper-left">U.S. City population and growth from 2000-2013. Cities with negative growth denoted.</span>
|
||||
<div class="pagination-numbers">
|
||||
{{p.startsAt}}–{{p.endsAt}} of {{longList.length}}
|
||||
</div>
|
||||
<p.prev @class="pagination-previous"> < </p.prev>
|
||||
<p.next @class="pagination-next"> > </p.next>
|
||||
<ul class="pagination-list"></ul>
|
||||
</nav>
|
||||
</div>
|
||||
</ListPagination>
|
||||
`,
|
||||
context: {
|
||||
injectRoutedController: injectRoutedController(
|
||||
Controller.extend({
|
||||
queryParams: ['currentPage'],
|
||||
currentPage: 1,
|
||||
})
|
||||
),
|
||||
longList,
|
||||
},
|
||||
};
|
||||
};
|
||||
@@ -1,248 +0,0 @@
|
||||
/**
|
||||
* Copyright (c) HashiCorp, Inc.
|
||||
* SPDX-License-Identifier: BUSL-1.1
|
||||
*/
|
||||
|
||||
import hbs from 'htmlbars-inline-precompile';
|
||||
import moment from 'moment';
|
||||
|
||||
export default {
|
||||
title: 'Components/Timeline',
|
||||
};
|
||||
|
||||
export let Standard = () => {
|
||||
return {
|
||||
template: hbs`
|
||||
<h5 class="title is-5">Timeline</h5>
|
||||
<ol class="timeline">
|
||||
<li class="timeline-note">
|
||||
{{format-date yesterday}}
|
||||
</li>
|
||||
<li class="timeline-object">
|
||||
<div class="boxed-section">
|
||||
<div class="boxed-section-head is-light">
|
||||
Object number one
|
||||
</div>
|
||||
</div>
|
||||
</li>
|
||||
<li class="timeline-object">
|
||||
<div class="boxed-section">
|
||||
<div class="boxed-section-head is-light">
|
||||
Object number two
|
||||
</div>
|
||||
</div>
|
||||
</li>
|
||||
<li class="timeline-note">
|
||||
{{format-date today}}
|
||||
</li>
|
||||
<li class="timeline-object">
|
||||
<div class="boxed-section">
|
||||
<div class="boxed-section-head is-light">
|
||||
Object number three
|
||||
</div>
|
||||
</div>
|
||||
</li>
|
||||
</ol>
|
||||
<p class="annotation">Timelines are a combination of objects and notes. Objects compose with boxed sections to create structure.</p>
|
||||
<p class="annotation">Timeline notes should be used sparingly when possible. In this example there is a note per day rather than a note per object.</p>
|
||||
`,
|
||||
context: {
|
||||
yesterday: moment().subtract(1, 'd'),
|
||||
today: moment(),
|
||||
},
|
||||
};
|
||||
};
|
||||
|
||||
export let Detailed = () => {
|
||||
return {
|
||||
template: hbs`
|
||||
<h5 class="title is-5">Detailed timeline</h5>
|
||||
<ol class="timeline">
|
||||
<li class="timeline-note">
|
||||
{{format-date today}}
|
||||
</li>
|
||||
<li class="timeline-object">
|
||||
<div class="boxed-section">
|
||||
<div class="boxed-section-head is-light">
|
||||
<span class="tag is-running">Running</span>
|
||||
<span class="bumper-left pair is-faded">
|
||||
<span class="term">Stable</span>
|
||||
<span class="badge is-light is-faded"><code>a387e243</code></span>
|
||||
</span>
|
||||
<span class="bumper-left pair is-faded">
|
||||
<span class="term">Submitted</span>
|
||||
<span class="tooltip" aria-label="{{format-month-ts (now)}}">{{moment-from-now (now)}}</span>
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
</li>
|
||||
<li class="timeline-object">
|
||||
<div class="boxed-section">
|
||||
<div class="boxed-section-head is-light">
|
||||
<span class="tag is-complete">Complete</span>
|
||||
<span class="bumper-left pair is-faded">
|
||||
<span class="term">Expired</span>
|
||||
<span class="badge is-light is-faded"><code>b3220efb</code></span>
|
||||
</span>
|
||||
<span class="bumper-left pair is-faded">
|
||||
<span class="term">Submitted</span>
|
||||
<span>{{format-month-ts yesterday}}</span>
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
</li>
|
||||
<li class="timeline-note">
|
||||
{{format-date yesterday}}
|
||||
</li>
|
||||
<li class="timeline-object">
|
||||
<div class="boxed-section">
|
||||
<div class="boxed-section-head is-light">
|
||||
<span class="tag is-error">Failed</span>
|
||||
<span class="bumper-left pair is-faded">
|
||||
<span class="term">Reverted</span>
|
||||
<span class="badge is-light is-faded"><code>fec9218e</code></span>
|
||||
</span>
|
||||
<span class="bumper-left pair is-faded">
|
||||
<span class="term">Submitted</span>
|
||||
<span>{{format-month-ts yesterday}}</span>
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
</li>
|
||||
</ol>
|
||||
`,
|
||||
context: {
|
||||
yesterday: moment().subtract(1, 'd'),
|
||||
today: moment(),
|
||||
},
|
||||
};
|
||||
};
|
||||
|
||||
export let Toggling = () => {
|
||||
return {
|
||||
template: hbs`
|
||||
<h5 class="title is-5">Toggling timeline objects</h5>
|
||||
<ol class="timeline">
|
||||
<li class="timeline-note">
|
||||
{{format-date today}}
|
||||
</li>
|
||||
<li class="timeline-object">
|
||||
<div class="boxed-section">
|
||||
<div class="boxed-section-head is-light">
|
||||
<span class="tag is-running">Running</span>
|
||||
<span class="bumper-left pair is-faded">
|
||||
<span class="term">Stable</span>
|
||||
<span class="badge is-light is-faded"><code>a387e243</code></span>
|
||||
</span>
|
||||
<button
|
||||
class="button is-light is-compact pull-right"
|
||||
onclick={{action (mut toggle1) (not toggle1)}}>
|
||||
{{if toggle1 "Close" "Open"}}
|
||||
</button>
|
||||
</div>
|
||||
{{#if toggle1}}
|
||||
<div class="boxed-section-body">
|
||||
<p>Some details for the timeline object.</p>
|
||||
</div>
|
||||
{{/if}}
|
||||
</div>
|
||||
</li>
|
||||
<li class="timeline-note">
|
||||
{{format-date yesterday}}
|
||||
</li>
|
||||
<li class="timeline-object">
|
||||
<div class="boxed-section">
|
||||
<div class="boxed-section-head is-light">
|
||||
<span class="tag is-complete">Complete</span>
|
||||
<span class="bumper-left pair is-faded">
|
||||
<span class="term">Expired</span>
|
||||
<span class="badge is-light is-faded"><code>b3220efb</code></span>
|
||||
</span>
|
||||
<button
|
||||
class="button is-light is-compact pull-right"
|
||||
onclick={{action (mut toggle2) (not toggle2)}}>
|
||||
{{if toggle2 "Close" "Open"}}
|
||||
</button>
|
||||
</div>
|
||||
{{#if toggle2}}
|
||||
<div class="boxed-section-body">
|
||||
<p>Some details for the timeline object.</p>
|
||||
</div>
|
||||
{{/if}}
|
||||
</div>
|
||||
</li>
|
||||
</ol>
|
||||
<p class="annotation"></p>
|
||||
`,
|
||||
context: {
|
||||
yesterday: moment().subtract(1, 'd'),
|
||||
today: moment(),
|
||||
},
|
||||
};
|
||||
};
|
||||
|
||||
export let Emphasizing = () => {
|
||||
return {
|
||||
template: hbs`
|
||||
<h5 class="title is-5">Emphasizing timeline objects</h5>
|
||||
<ol class="timeline">
|
||||
<li class="timeline-note">
|
||||
{{format-date today}}
|
||||
</li>
|
||||
<li class="timeline-object">
|
||||
<div class="boxed-section">
|
||||
<div class="boxed-section-head is-light">
|
||||
<span class="pair is-faded">
|
||||
<span class="term">Stable</span>
|
||||
<span class="badge is-light is-faded"><code>a387e243</code></span>
|
||||
</span>
|
||||
<span class="bumper-left pair is-faded">
|
||||
<span class="term">Submitted</span>
|
||||
<span class="tooltip" aria-label="{{format-ts (now)}}">{{moment-from-now (now)}}</span>
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
</li>
|
||||
<li class="timeline-object">
|
||||
<div class="boxed-section">
|
||||
<div class="boxed-section-head">
|
||||
Pay attention here
|
||||
</div>
|
||||
<div class="boxed-section-body">
|
||||
<span class="pair is-faded">
|
||||
<span class="term">Expired</span>
|
||||
<span class="badge is-light is-faded"><code>b3220efb</code></span>
|
||||
</span>
|
||||
<span class="bumper-left pair is-faded">
|
||||
<span class="term">Submitted</span>
|
||||
<span>{{format-ts yesterday}}</span>
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
</li>
|
||||
<li class="timeline-note">
|
||||
{{format-date yesterday}}
|
||||
</li>
|
||||
<li class="timeline-object">
|
||||
<div class="boxed-section">
|
||||
<div class="boxed-section-head is-light">
|
||||
<span class="pair is-faded">
|
||||
<span class="term">Reverted</span>
|
||||
<span class="badge is-light is-faded"><code>fec9218e</code></span>
|
||||
</span>
|
||||
<span class="bumper-left pair is-faded">
|
||||
<span class="term">Submitted</span>
|
||||
<span>{{format-ts yesterday}}</span>
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
</li>
|
||||
</ol>
|
||||
<p class="annotation">By using a full boxed-section for an emphasized timeline object, the object takes up more space and gets more visual weight. It also adheres to existing patterns.</p>
|
||||
`,
|
||||
context: {
|
||||
yesterday: moment().subtract(1, 'd'),
|
||||
today: moment(),
|
||||
},
|
||||
};
|
||||
};
|
||||
@@ -1,111 +0,0 @@
|
||||
/**
|
||||
* Copyright (c) HashiCorp, Inc.
|
||||
* SPDX-License-Identifier: BUSL-1.1
|
||||
*/
|
||||
|
||||
import hbs from 'htmlbars-inline-precompile';
|
||||
|
||||
export default {
|
||||
title: 'Components/Two-Step Button',
|
||||
};
|
||||
|
||||
export let Standard = () => {
|
||||
return {
|
||||
template: hbs`
|
||||
<h5 class="title is-5">Two-Step Button</h5>
|
||||
<br><br>
|
||||
<TwoStepButton
|
||||
@idleText="Scary Action"
|
||||
@cancelText="Nvm"
|
||||
@confirmText="Yep"
|
||||
@confirmationMessage="Wait, really? Like...seriously?"
|
||||
/>
|
||||
`,
|
||||
};
|
||||
};
|
||||
|
||||
export let Styled = () => {
|
||||
return {
|
||||
template: hbs`
|
||||
<h5 class="title is-5">Two-Step Button with class overrides</h5>
|
||||
<br><br>
|
||||
<TwoStepButton
|
||||
@idleText="Scary Action"
|
||||
@cancelText="Nvm"
|
||||
@confirmText="Yep"
|
||||
@confirmationMessage="Wait, really? Like...seriously?"
|
||||
@classes={{hash
|
||||
idleButton="is-danger is-large"
|
||||
confirmationMessage="badge is-warning"
|
||||
confirmButton="is-large"
|
||||
cancelButton="is-hollow"
|
||||
}}
|
||||
/>
|
||||
`,
|
||||
};
|
||||
};
|
||||
|
||||
export let InTitle = () => {
|
||||
return {
|
||||
template: hbs`
|
||||
<h5 class="title is-5">Two-Step Button in title</h5>
|
||||
<br><br>
|
||||
<h1 class="title">
|
||||
This is a page title
|
||||
<TwoStepButton
|
||||
@idleText="Scary Action"
|
||||
@cancelText="Nvm"
|
||||
@confirmText="Yep"
|
||||
@confirmationMessage="Wait, really? Like...seriously?"
|
||||
/>
|
||||
</h1>
|
||||
`,
|
||||
};
|
||||
};
|
||||
|
||||
export let InlineText = () => {
|
||||
return {
|
||||
template: hbs`
|
||||
<h5 class="title is-5">Two-Step Button with inline confirmation message</h5>
|
||||
<br><br>
|
||||
<TwoStepButton
|
||||
@idleText="Scary Action"
|
||||
@cancelText="Nvm"
|
||||
@confirmText="Yep"
|
||||
@confirmationMessage="Really?"
|
||||
@inlineText={{true}}
|
||||
/>
|
||||
<br><br>
|
||||
<span style="padding-left: 4rem"></span>
|
||||
<TwoStepButton
|
||||
@idleText="Scary Action"
|
||||
@cancelText="Nvm"
|
||||
@confirmText="Yep"
|
||||
@confirmationMessage="Really?"
|
||||
@alignRight={{true}}
|
||||
@inlineText={{true}}
|
||||
/>
|
||||
`,
|
||||
};
|
||||
};
|
||||
|
||||
export let LoadingState = () => {
|
||||
return {
|
||||
template: hbs`
|
||||
<h5 class="title is-5">Two-Step Button loading state</h5>
|
||||
<br><br>
|
||||
<h1 class="title">
|
||||
This is a page title
|
||||
<TwoStepButton
|
||||
@idleText="Scary Action"
|
||||
@cancelText="Nvm"
|
||||
@confirmText="Yep"
|
||||
@confirmationMessage="Wait, really? Like...seriously?"
|
||||
@awaitingConfirmation={{true}}
|
||||
@state="prompt"
|
||||
/>
|
||||
</h1>
|
||||
<p class="annotation"> <strong>Note:</strong> the <code>state</code> property is internal state and only used here to bypass the idle state for demonstration purposes.</p>
|
||||
`,
|
||||
};
|
||||
};
|
||||
@@ -1,145 +0,0 @@
|
||||
/**
|
||||
* Copyright (c) HashiCorp, Inc.
|
||||
* SPDX-License-Identifier: BUSL-1.1
|
||||
*/
|
||||
|
||||
import hbs from 'htmlbars-inline-precompile';
|
||||
import { htmlSafe } from '@ember/string';
|
||||
|
||||
export default {
|
||||
title: 'Theme/Colors',
|
||||
};
|
||||
|
||||
export let Colors = () => {
|
||||
return {
|
||||
template: hbs`
|
||||
{{#each palettes as |palette|}}
|
||||
<div class='palette'>
|
||||
<div class='title'>{{palette.title}}</div>
|
||||
<div class='description'>{{palette.description}}</div>
|
||||
{{#each palette.colors as |color|}}
|
||||
<div class='item'>
|
||||
<div class='color' style={{color.style}}></div>
|
||||
<div class='info'>
|
||||
<p class='hex'>{{color.base}}</p>
|
||||
<p class='name'>{{color.name}}</p>
|
||||
</div>
|
||||
</div>
|
||||
{{/each}}
|
||||
</div>
|
||||
{{/each}}
|
||||
`,
|
||||
context: {
|
||||
palettes: [
|
||||
{
|
||||
title: 'Nomad Theme',
|
||||
description: 'Accent and neutrals.',
|
||||
colors: [
|
||||
{
|
||||
name: 'Primary',
|
||||
base: '#25ba81',
|
||||
},
|
||||
{
|
||||
name: 'Primary Dark',
|
||||
base: '#1d9467',
|
||||
},
|
||||
{
|
||||
name: 'Text',
|
||||
base: '#0a0a0a',
|
||||
},
|
||||
{
|
||||
name: 'Link',
|
||||
base: '#1563ff',
|
||||
},
|
||||
{
|
||||
name: 'Gray',
|
||||
base: '#bbc4d1',
|
||||
},
|
||||
{
|
||||
name: 'Off-white',
|
||||
base: '#f5f5f5',
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
title: 'Product Colors',
|
||||
description:
|
||||
'Colors from other HashiCorp products. Often borrowed for alternative accents and color schemes.',
|
||||
colors: [
|
||||
{
|
||||
name: 'Consul Pink',
|
||||
base: '#ff0087',
|
||||
},
|
||||
{
|
||||
name: 'Consul Pink Dark',
|
||||
base: '#c62a71',
|
||||
},
|
||||
{
|
||||
name: 'Packer Blue',
|
||||
base: '#1daeff',
|
||||
},
|
||||
{
|
||||
name: 'Packer Blue Dark',
|
||||
base: '#1d94dd',
|
||||
},
|
||||
{
|
||||
name: 'Terraform Purple',
|
||||
base: '#5c4ee5',
|
||||
},
|
||||
{
|
||||
name: 'Terraform Purple Dark',
|
||||
base: '#4040b2',
|
||||
},
|
||||
{
|
||||
name: 'Vagrant Blue',
|
||||
base: '#1563ff',
|
||||
},
|
||||
{
|
||||
name: 'Vagrant Blue Dark',
|
||||
base: '#104eb2',
|
||||
},
|
||||
{
|
||||
name: 'Nomad Green',
|
||||
base: '#25ba81',
|
||||
},
|
||||
{
|
||||
name: 'Nomad Green Dark',
|
||||
base: '#1d9467',
|
||||
},
|
||||
{
|
||||
name: 'Nomad Green Darker',
|
||||
base: '#16704d',
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
title: 'Emotive Colors',
|
||||
description: 'Colors used in conjunction with an emotional response.',
|
||||
colors: [
|
||||
{
|
||||
name: 'Success',
|
||||
base: '#23d160',
|
||||
},
|
||||
{
|
||||
name: 'Warning',
|
||||
base: '#fa8e23',
|
||||
},
|
||||
{
|
||||
name: 'Danger',
|
||||
base: '#c84034',
|
||||
},
|
||||
{
|
||||
name: 'Info',
|
||||
base: '#1563ff',
|
||||
},
|
||||
],
|
||||
},
|
||||
].map((palette) => {
|
||||
palette.colors.forEach((color) => {
|
||||
color.style = htmlSafe(`background-color: ${color.base}`);
|
||||
});
|
||||
return palette;
|
||||
}),
|
||||
},
|
||||
};
|
||||
};
|
||||
@@ -1,49 +0,0 @@
|
||||
/**
|
||||
* Copyright (c) HashiCorp, Inc.
|
||||
* SPDX-License-Identifier: BUSL-1.1
|
||||
*/
|
||||
|
||||
import hbs from 'htmlbars-inline-precompile';
|
||||
import { htmlSafe } from '@ember/string';
|
||||
|
||||
export default {
|
||||
title: 'Theme/Font Stacks',
|
||||
};
|
||||
|
||||
export let FontStacks = () => {
|
||||
return {
|
||||
template: hbs`
|
||||
<h5 class="title is-5">Font Stacks</h5>
|
||||
|
||||
{{#each fontFamilies as |fontFamily|}}
|
||||
<h6 class="title is-6 with-headroom">{{fontFamily.name}}</h6>
|
||||
<div class="typeface" style={{fontFamily.style}}>
|
||||
<div class="hero">Aa</div>
|
||||
<p class="sample">A B C D E F G H I J K L M N O P Q R S T U V W X Y Z</p>
|
||||
<p class="sample">a b c d e f g h i j k l m n o p q r s t u v w x y z</p>
|
||||
<p class="sample">0 1 2 3 4 5 6 7 8 9</p>
|
||||
</div>
|
||||
<br>
|
||||
{{/each}}
|
||||
`,
|
||||
context: {
|
||||
fontFamilies: [
|
||||
'-apple-system',
|
||||
'BlinkMacSystemFont',
|
||||
'Segoe UI',
|
||||
'Roboto',
|
||||
'Oxygen-Sans',
|
||||
'Ubuntu',
|
||||
'Cantarell',
|
||||
'Helvetica Neue',
|
||||
'sans-serif',
|
||||
'monospace',
|
||||
].map((family) => {
|
||||
return {
|
||||
name: family,
|
||||
style: htmlSafe(`font-family: ${family}`),
|
||||
};
|
||||
}),
|
||||
},
|
||||
};
|
||||
};
|
||||
@@ -1,23 +0,0 @@
|
||||
/**
|
||||
* Copyright (c) HashiCorp, Inc.
|
||||
* SPDX-License-Identifier: BUSL-1.1
|
||||
*/
|
||||
|
||||
import hbs from 'htmlbars-inline-precompile';
|
||||
|
||||
export default {
|
||||
title: 'Theme/Icons',
|
||||
};
|
||||
|
||||
export let Icons = () => ({
|
||||
template: hbs`
|
||||
<ul class="tile-list">
|
||||
{{#each (all-icons) as |icon|}}
|
||||
<li class="icon-tile">
|
||||
{{x-icon icon}}
|
||||
<code>{{icon}}</code>
|
||||
</li>
|
||||
{{/each}}
|
||||
</ul>
|
||||
`,
|
||||
});
|
||||
@@ -1,30 +0,0 @@
|
||||
/**
|
||||
* Copyright (c) HashiCorp, Inc.
|
||||
* SPDX-License-Identifier: BUSL-1.1
|
||||
*/
|
||||
|
||||
import hbs from 'htmlbars-inline-precompile';
|
||||
|
||||
export default {
|
||||
title: 'Theme/Text Sizing',
|
||||
};
|
||||
|
||||
export let TextSizing = () => {
|
||||
return {
|
||||
template: hbs`
|
||||
<h5 class="title is-5">Text sizing</h5>
|
||||
<div class="block">
|
||||
<h1 class="title">Large Title</h1>
|
||||
<p>Some prose to follow the large title. Not necessarily meant for reading.</p>
|
||||
</div>
|
||||
<div class="block">
|
||||
<h2 class="title is-4">Medium Title</h2>
|
||||
<p>Some prose to follow the large title. Not necessarily meant for reading.</p>
|
||||
</div>
|
||||
<div class="block">
|
||||
<h3 class="title is-5">Small Title</h3>
|
||||
<p>Some prose to follow the large title. Not necessarily meant for reading.</p>
|
||||
</div>
|
||||
`,
|
||||
};
|
||||
};
|
||||
@@ -1,27 +0,0 @@
|
||||
/**
|
||||
* Copyright (c) HashiCorp, Inc.
|
||||
* SPDX-License-Identifier: BUSL-1.1
|
||||
*/
|
||||
|
||||
import { A } from '@ember/array';
|
||||
import ArrayProxy from '@ember/array/proxy';
|
||||
import { next } from '@ember/runloop';
|
||||
|
||||
/**
|
||||
* This is an array whose content is empty until the next
|
||||
* tick, which fixes Storybook race condition rendering
|
||||
* problems.
|
||||
*/
|
||||
|
||||
export default ArrayProxy.extend({
|
||||
init(array) {
|
||||
this.set('content', A([]));
|
||||
this._super(...arguments);
|
||||
this[Symbol.iterator] = this.content[Symbol.iterator];
|
||||
|
||||
next(this, () => {
|
||||
this.set('content', A(array));
|
||||
this[Symbol.iterator] = this.content[Symbol.iterator];
|
||||
});
|
||||
},
|
||||
});
|
||||
@@ -1,24 +0,0 @@
|
||||
/**
|
||||
* Copyright (c) HashiCorp, Inc.
|
||||
* SPDX-License-Identifier: BUSL-1.1
|
||||
*/
|
||||
|
||||
import EmberObject from '@ember/object';
|
||||
import { next } from '@ember/runloop';
|
||||
|
||||
/**
|
||||
* This has a `complete` property that turns from false
|
||||
* to true in the next tick, which helps with some
|
||||
* Storybook race condition rendering problems.
|
||||
*/
|
||||
|
||||
export default EmberObject.extend({
|
||||
init() {
|
||||
this._super(...arguments);
|
||||
this.set('complete', false);
|
||||
|
||||
next(this, () => {
|
||||
this.set('complete', true);
|
||||
});
|
||||
},
|
||||
});
|
||||
5455
ui/yarn.lock
5455
ui/yarn.lock
File diff suppressed because it is too large
Load Diff
Reference in New Issue
Block a user