feat(ui/labels): display labels in task row

pull/10616/head
Iris Scholten 2019-01-08 15:05:48 -08:00
parent 21c996383c
commit 4f96333b52
9 changed files with 281 additions and 48 deletions

View File

@ -3951,6 +3951,7 @@ components:
description: The Flux script to run for this task.
type: string
Task:
type: object
properties:
id:
readOnly: true
@ -3970,6 +3971,8 @@ components:
- inactive
owner:
$ref: "#/components/schemas/User"
labels:
$ref: "#/components/schemas/Labels"
flux:
description: The Flux script to run for this task.
type: string
@ -3980,8 +3983,13 @@ components:
description: A task repetition schedule in the form '* * * * * *'; parsed from Flux.
type: string
offset:
description: How long to wait before running the task
description: Duration to delay after the schedule, before executing the task; parsed from flux.
type: string
latest_completed:
description: Timestamp of latest scheduled, completed run, RFC3339.
type: string
format: date-time
readOnly: true
links:
type: object
readOnly: true

View File

@ -9,7 +9,7 @@ import {
TemplateValueType,
} from 'src/types'
import {Links} from 'src/types/v2/links'
import {Task, TaskStatus} from 'src/types/v2/tasks'
import {Task} from 'src/types/v2/tasks'
import {OnboardingStepProps} from 'src/onboarding/containers/OnboardingWizard'
import {WithRouterProps} from 'react-router'
import {ConfigurationState} from 'src/types/v2/dataLoaders'
@ -24,6 +24,8 @@ import {
TelegrafPluginInputNet,
TelegrafPluginInputProcstat,
TelegrafPluginInputDocker,
Task as TaskApi,
Label,
} from 'src/api'
export const links: Links = {
@ -215,6 +217,25 @@ export const dashboard: Dashboard = {
labels: [],
}
export const labels: Label[] = [
{
resourceID: 'dashboard-mock-label-a',
name: 'Trogdor',
properties: {
color: '#44ffcc',
description: 'Burninating the countryside',
},
},
{
resourceID: 'dashboard-mock-label-b',
name: 'Strawberry',
properties: {
color: '#ff0054',
description: 'It is a great fruit',
},
},
]
export const dashboardWithLabels: Dashboard = {
id: '1',
cells: [],
@ -227,24 +248,7 @@ export const dashboardWithLabels: Dashboard = {
createdAt: '2019-01-08T11:57:31.562044-08:00',
updatedAt: '2019-01-08T12:57:31.562048-08:00',
},
labels: [
{
resourceID: 'dashboard-mock-label-a',
name: 'Trogdor',
properties: {
color: '#44ffcc',
description: 'Burninating the countryside',
},
},
{
resourceID: 'dashboard-mock-label-b',
name: 'Strawberry',
properties: {
color: '#ff0054',
description: 'It is a great fruit',
},
},
],
labels,
}
export const cell: Cell = {
@ -265,7 +269,7 @@ export const tasks: Task[] = [
id: '02ef9deff2141000',
organizationID: '02ee9e2a29d73000',
name: 'pasdlak',
status: TaskStatus.Active,
status: TaskApi.StatusEnum.Active,
owner: {id: '02ee9e2a19d73000', name: ''},
flux:
'option task = {\n name: "pasdlak",\n cron: "2 0 * * *"\n}\nfrom(bucket: "inbucket") \n|> range(start: -1h)',
@ -282,12 +286,13 @@ export const tasks: Task[] = [
id: '02ee9e2a29d73000',
name: 'RadicalOrganization',
},
labels: [],
},
{
id: '02f12c50dba72000',
organizationID: '02ee9e2a29d73000',
name: 'somename',
status: TaskStatus.Active,
status: TaskApi.StatusEnum.Active,
owner: {id: '02ee9e2a19d73000', name: ''},
flux:
'option task = {\n name: "somename",\n every: 1m,\n}\nfrom(bucket: "inbucket") \n|> range(start: -task.every)',
@ -304,6 +309,7 @@ export const tasks: Task[] = [
id: '02ee9e2a29d73000',
name: 'RadicalOrganization',
},
labels,
},
]

View File

@ -2672,6 +2672,12 @@ export interface Task {
* @memberof Task
*/
owner?: User;
/**
*
* @type {Array<Label>}
* @memberof Task
*/
labels?: Array<Label>;
/**
* The Flux script to run for this task.
* @type {string}
@ -2691,11 +2697,17 @@ export interface Task {
*/
cron?: string;
/**
* How long to wait before running the task
* Duration to delay after the schedule, before executing the task; parsed from flux.
* @type {string}
* @memberof Task
*/
offset?: string;
/**
* Timestamp of latest scheduled, completed run, RFC3339.
* @type {Date}
* @memberof Task
*/
latestCompleted?: Date;
/**
*
* @type {TaskLinks}

View File

@ -0,0 +1,47 @@
// Libraries
import React from 'react'
import {shallow} from 'enzyme'
// Components
import {TaskRow} from 'src/tasks/components/TaskRow'
// Types
import {Label} from 'src/clockface'
// Constants
import {tasks, withRouterProps} from 'mocks/dummyData'
const setup = (override = {}) => {
const props = {
...withRouterProps,
task: tasks[0],
onActivate: jest.fn(),
onDelete: jest.fn(),
onSelect: jest.fn(),
...override,
}
const wrapper = shallow(<TaskRow {...props} />)
return {wrapper}
}
describe('Tasks.Components.TaskRow', () => {
it('renders', () => {
const {wrapper} = setup()
expect(wrapper.exists()).toBe(true)
expect(wrapper).toMatchSnapshot()
})
describe('if task has labels', () => {
it('renders with labels', () => {
const {wrapper} = setup({task: tasks[1]})
const labelContainer = wrapper.find(Label.Container)
const labels = wrapper.find(Label)
expect(labelContainer.exists()).toBe(true)
expect(labels.length).toBe(tasks[1].labels.length)
})
})
})

View File

@ -11,16 +11,16 @@ import {
SlideToggle,
IndexList,
ConfirmationButton,
Stack,
Label,
} from 'src/clockface'
// Utils
import {downloadTextFile} from 'src/shared/utils/download'
import {Task as TaskAPI, User, Organization} from 'src/api'
import {Task as TaskAPI, Organization} from 'src/api'
interface Task extends TaskAPI {
organization: Organization
owner?: User
offset?: string
}
// Constants
@ -33,16 +33,19 @@ interface Props {
onSelect: (task: Task) => void
}
class TaskRow extends PureComponent<Props & WithRouterProps> {
export class TaskRow extends PureComponent<Props & WithRouterProps> {
public render() {
const {task, onDelete} = this.props
return (
<IndexList.Row disabled={!this.isTaskActive}>
<IndexList.Cell>
<a href="#" onClick={this.handleClick}>
{task.name}
</a>
<ComponentSpacer stackChildren={Stack.Rows} align={Alignment.Left}>
<a href="#" onClick={this.handleClick}>
{task.name}
</a>
{this.labels}
</ComponentSpacer>
</IndexList.Cell>
<IndexList.Cell>
<SlideToggle
@ -94,6 +97,27 @@ class TaskRow extends PureComponent<Props & WithRouterProps> {
router.push(`/organizations/${task.organization.id}/members_tab`)
}
private get labels(): JSX.Element {
const {task} = this.props
if (!task.labels.length) {
return
}
return (
<Label.Container limitChildCount={4}>
{task.labels.map(label => (
<Label
key={label.resourceID}
id={label.resourceID}
colorHex={label.properties.color}
name={label.name}
description={label.properties.description}
/>
))}
</Label.Container>
)
}
private get isTaskActive(): boolean {
const {task} = this.props
if (task.status === TaskAPI.StatusEnum.Active) {

View File

@ -0,0 +1,114 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP
exports[`Tasks.Components.TaskRow renders 1`] = `
<IndexListRow
disabled={false}
>
<IndexListRowCell
alignment="left"
revealOnHover={false}
>
<ComponentSpacer
align="left"
stackChildren="rows"
>
<a
href="#"
onClick={[Function]}
>
pasdlak
</a>
</ComponentSpacer>
</IndexListRowCell>
<IndexListRowCell
alignment="left"
revealOnHover={false}
>
<SlideToggle
active={true}
color="primary"
disabled={false}
onChange={[Function]}
size="xs"
tooltipText=""
/>
</IndexListRowCell>
<IndexListRowCell
alignment="left"
revealOnHover={false}
>
2 0 * * *
</IndexListRowCell>
<IndexListRowCell
alignment="left"
revealOnHover={false}
>
<a
href=""
onClick={[Function]}
>
RadicalOrganization
</a>
</IndexListRowCell>
<IndexListRowCell
alignment="right"
revealOnHover={true}
>
<ComponentSpacer
align="right"
>
<Button
active={false}
color="default"
icon="export"
onClick={[Function]}
shape="none"
size="xs"
status="default"
text="Export"
type="button"
/>
<ConfirmationButton
confirmText="Confirm"
onConfirm={[MockFunction]}
returnValue={
Object {
"cron": "2 0 * * *",
"flux": "option task = {
name: \\"pasdlak\\",
cron: \\"2 0 * * *\\"
}
from(bucket: \\"inbucket\\")
|> range(start: -1h)",
"id": "02ef9deff2141000",
"labels": Array [],
"name": "pasdlak",
"organization": Object {
"id": "02ee9e2a29d73000",
"links": Object {
"buckets": "/api/v2/buckets?org=RadicalOrganization",
"dashboards": "/api/v2/dashboards?org=RadicalOrganization",
"log": "/api/v2/orgs/02ee9e2a29d73000/log",
"members": "/api/v2/orgs/02ee9e2a29d73000/members",
"self": "/api/v2/orgs/02ee9e2a29d73000",
"tasks": "/api/v2/tasks?org=RadicalOrganization",
},
"name": "RadicalOrganization",
},
"organizationID": "02ee9e2a29d73000",
"owner": Object {
"id": "02ee9e2a19d73000",
"name": "",
},
"status": "active",
}
}
shape="none"
size="xs"
status="default"
text="Delete"
/>
</ComponentSpacer>
</IndexListRowCell>
</IndexListRow>
`;

View File

@ -63,6 +63,7 @@ exports[`TasksList rendering renders 1`] = `
from(bucket: \\"inbucket\\")
|> range(start: -1h)",
"id": "02ef9deff2141000",
"labels": Array [],
"name": "pasdlak",
"organization": Object {
"id": "02ee9e2a29d73000",
@ -92,6 +93,24 @@ from(bucket: \\"inbucket\\")
from(bucket: \\"inbucket\\")
|> range(start: -task.every)",
"id": "02f12c50dba72000",
"labels": Array [
Object {
"name": "Trogdor",
"properties": Object {
"color": "#44ffcc",
"description": "Burninating the countryside",
},
"resourceID": "dashboard-mock-label-a",
},
Object {
"name": "Strawberry",
"properties": Object {
"color": "#ff0054",
"description": "It is a great fruit",
},
"resourceID": "dashboard-mock-label-b",
},
],
"name": "somename",
"organization": Object {
"id": "02ee9e2a29d73000",

View File

@ -63,6 +63,7 @@ exports[`TasksList rendering renders 1`] = `
from(bucket: \\"inbucket\\")
|> range(start: -1h)",
"id": "02ef9deff2141000",
"labels": Array [],
"name": "pasdlak",
"organization": Object {
"id": "02ee9e2a29d73000",
@ -92,6 +93,24 @@ from(bucket: \\"inbucket\\")
from(bucket: \\"inbucket\\")
|> range(start: -task.every)",
"id": "02f12c50dba72000",
"labels": Array [
Object {
"name": "Trogdor",
"properties": Object {
"color": "#44ffcc",
"description": "Burninating the countryside",
},
"resourceID": "dashboard-mock-label-a",
},
Object {
"name": "Strawberry",
"properties": Object {
"color": "#ff0054",
"description": "It is a great fruit",
},
"resourceID": "dashboard-mock-label-b",
},
],
"name": "somename",
"organization": Object {
"id": "02ee9e2a29d73000",

View File

@ -1,22 +1,6 @@
import {Organization} from 'src/types/v2/orgs'
import {Task as TaskAPI} from 'src/api'
export interface Task {
every?: string
cron?: string
offset?: string
id: string
name: string
status: TaskStatus
organizationID: string
export interface Task extends TaskAPI {
organization: Organization
owner: {
id: string
name: string
}
flux?: string
}
export enum TaskStatus {
Active = 'active',
Inactive = 'inactive',
}