Skip to content

Commit

Permalink
Schedules 2nd iteration updates (#1720)
Browse files Browse the repository at this point in the history
# What this PR does

Features and bugs related to [[Q1 2023] Iteration with
Schedules](https://github.com/grafana/oncall-private/issues/1660)
milestone

## Which issue(s) this PR fixes

## Checklist

- [x] Unit, integration, and e2e (if applicable) tests updated
- [ ] Documentation added (or `pr:no public docs` PR label added if not
required)
- [x] `CHANGELOG.md` updated (or `pr:no changelog` PR label added if not
required)

---------

Co-authored-by: Innokentii Konstantinov <innokenty.konstantinov@grafana.com>
Co-authored-by: Matias Bordese <mbordese@gmail.com>
  • Loading branch information
3 people authored Jun 20, 2023
1 parent 9f7bdac commit 5c19610
Show file tree
Hide file tree
Showing 57 changed files with 2,358 additions and 957 deletions.
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
.root {
border: var(--border);
width: 100%;
}

.header {
Expand All @@ -25,4 +26,10 @@

.icon {
color: var(--secondary-text-color);
transform-origin: center;
transition: transform 0.2s;

&--rotated {
transform: rotate(90deg);
}
}
4 changes: 2 additions & 2 deletions src/components/Collapse/Collapse.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ import React, { FC, useCallback, useState } from 'react';
import { Icon } from '@grafana/ui';
import cn from 'classnames/bind';

import styles from 'components/Collapse/Collapse.module.css';
import styles from 'components/Collapse/Collapse.module.scss';

export interface CollapseProps {
label: React.ReactNode;
Expand Down Expand Up @@ -48,7 +48,7 @@ const Collapse: FC<CollapseProps> = (props) => {
onClick={onHeaderClickCallback}
data-testid="test__toggle"
>
<Icon name={isOpen ? 'angle-down' : 'angle-right'} size="xl" className={cx('icon')} />
<Icon name={'angle-right'} size="xl" className={cx('icon', { 'icon--rotated': isOpen })} />
<div className={cx('label')}> {label}</div>
</div>
{isOpen && (
Expand Down
3 changes: 3 additions & 0 deletions src/components/GForm/GForm.module.scss
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
.collapse {
margin-bottom: 16px;
}
22 changes: 20 additions & 2 deletions src/components/GForm/GForm.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,11 +2,17 @@ import React from 'react';

import { Field, Form, Input, InputControl, Select, Switch, TextArea } from '@grafana/ui';
import { capitalCase } from 'change-case';
import cn from 'classnames/bind';

import Collapse from 'components/Collapse/Collapse';
import { FormItem, FormItemType } from 'components/GForm/GForm.types';
import GSelect from 'containers/GSelect/GSelect';
import RemoteSelect from 'containers/RemoteSelect/RemoteSelect';

import styles from './GForm.module.scss';

const cx = cn.bind(styles);

interface GFormProps {
form: { name: string; fields: FormItem[] };
data: any;
Expand Down Expand Up @@ -113,10 +119,13 @@ class GForm extends React.Component<GFormProps, {}> {
render() {
const { form, data } = this.props;

const openFields = form.fields.filter((field) => !field.collapsed);
const collapsedfields = form.fields.filter((field) => field.collapsed);

return (
<Form maxWidth="none" id={form.name} defaultValues={data} onSubmit={this.handleSubmit}>
{({ register, errors, control, getValues, setValue }) => {
return form.fields.map((formItem: FormItem, formIndex: number) => {
const renderField = (formItem: FormItem, formIndex: number) => {
if (formItem.isVisible && !formItem.isVisible(getValues())) {
setValue(formItem.name, undefined); // clear input value on hide
return null;
Expand All @@ -137,7 +146,16 @@ class GForm extends React.Component<GFormProps, {}> {
})}
</Field>
);
});
};

return (
<>
{openFields.map(renderField)}
<Collapse isOpen={false} label="Notification settings" className={cx('collapse')}>
{collapsedfields.map(renderField)}
</Collapse>
</>
);
}}
</Form>
);
Expand Down
1 change: 1 addition & 0 deletions src/components/GForm/GForm.types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -22,4 +22,5 @@ export interface FormItem {
validation?: (v: any) => boolean;
};
extra?: any;
collapsed?: boolean;
}
3 changes: 2 additions & 1 deletion src/components/Modal/Modal.module.css
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,8 @@
box-shadow: var(--shadows-z3);
border-radius: 2px;
z-index: 10;
overflow: scroll;

/* overflow: scroll; */
}

/*
Expand Down
6 changes: 4 additions & 2 deletions src/components/Modal/Modal.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -17,12 +17,13 @@ export interface ModalProps {
width: string;
contentElement?: (props, children: React.ReactNode) => React.ReactNode;
isOpen: boolean;
top?: string;
}

const cx = cn.bind(styles);

const Modal: FC<PropsWithChildren<ModalProps>> = (props) => {
const { title, children, onDismiss, width = '600px', contentElement, isOpen = true } = props;
const { title, children, onDismiss, width = '600px', contentElement, isOpen = true, top, className } = props;

return (
<ReactModal
Expand All @@ -31,13 +32,14 @@ const Modal: FC<PropsWithChildren<ModalProps>> = (props) => {
overlay: {},
content: {
width,
top,
},
}}
isOpen={isOpen}
onAfterOpen={() => {}}
onRequestClose={onDismiss}
contentLabel={title}
className={cx('root')}
className={cx('root', className)}
overlayClassName={cx('overlay')}
overlayElement={(_props, contentElement) => contentElement} // render without overlay to allow body scroll
contentElement={contentElement}
Expand Down
3 changes: 3 additions & 0 deletions src/components/ScheduleFilters/ScheduleFilters.module.scss
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
.root {
display: block;
}
50 changes: 50 additions & 0 deletions src/components/ScheduleFilters/ScheduleFilters.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
import React, { useCallback } from 'react';

import { InlineSwitch } from '@grafana/ui';
import cn from 'classnames/bind';

import { User } from 'models/user/user.types';

import styles from './ScheduleFilters.module.scss';
import { ScheduleFiltersType } from './ScheduleFilters.types';

const cx = cn.bind(styles);

interface SchedulesFiltersProps {
value: ScheduleFiltersType;
currentUserPk: User['pk'];
onChange: (filters: ScheduleFiltersType) => void;
}

const SchedulesFilters = (props: SchedulesFiltersProps) => {
const { value, currentUserPk, onChange } = props;

const handleShowMyShiftsOnlyClick = useCallback(
(event: React.ChangeEvent<HTMLInputElement>) => {
const newUsers = [...value.users];

if (event.target.checked && !value.users.includes(currentUserPk)) {
newUsers.push(currentUserPk);
} else {
const index = value.users.findIndex((pk) => pk === currentUserPk);
newUsers.splice(index, 1);
}

onChange({ ...value, users: newUsers });
},
[value]
);

return (
<div className={cx('root')}>
<InlineSwitch
showLabel
label="Highlight my shifts"
value={value.users.includes(currentUserPk)}
onChange={handleShowMyShiftsOnlyClick}
/>
</div>
);
};

export default SchedulesFilters;
5 changes: 5 additions & 0 deletions src/components/ScheduleFilters/ScheduleFilters.types.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
import { User } from 'models/user/user.types';

export interface ScheduleFiltersType {
users: Array<User['pk']>;
}
4 changes: 3 additions & 1 deletion src/components/ScheduleQuality/ScheduleQuality.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,8 @@ const ScheduleQuality: FC<ScheduleQualityProps> = ({ schedule, lastUpdated }) =>
<div className={cx('root')} data-testid="schedule-quality">
{relatedEscalationChains?.length > 0 && schedule?.number_of_escalation_chains > 0 && (
<TooltipBadge
borderType="link"
borderType="success"
icon="link"
addPadding
text={schedule.number_of_escalation_chains}
tooltipTitle="Used in escalations"
Expand All @@ -62,6 +63,7 @@ const ScheduleQuality: FC<ScheduleQualityProps> = ({ schedule, lastUpdated }) =>
{schedule.warnings?.length > 0 && (
<TooltipBadge
borderType="warning"
icon="exclamation-triangle"
addPadding
text={schedule.warnings.length}
tooltipTitle="Warnings"
Expand Down
25 changes: 0 additions & 25 deletions src/components/SchedulesFilters/SchedulesFilters.helpers.ts

This file was deleted.

13 changes: 0 additions & 13 deletions src/components/SchedulesFilters/SchedulesFilters.module.scss

This file was deleted.

13 changes: 12 additions & 1 deletion src/components/Text/Text.module.scss
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,10 @@
&--large {
font-size: 20px;
}

&--clickable {
cursor: pointer;
}
}

.no-wrap {
Expand All @@ -67,10 +71,17 @@
}

.icon-button {
margin-left: 4px;
margin-left: 8px;
display: none;
}

.root:hover .icon-button {
display: inline-block;
}

.with-maxWidth {
text-overflow: ellipsis;
white-space: nowrap;
overflow: hidden;
display: block;
}
11 changes: 8 additions & 3 deletions src/components/Text/Text.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,8 @@ interface TextProps extends HTMLAttributes<HTMLElement> {
clearBeforeEdit?: boolean;
hidden?: boolean;
editModalTitle?: string;
maxWidth?: string;
clickable?: boolean;
}

interface TextInterface extends React.FC<TextProps> {
Expand Down Expand Up @@ -52,6 +54,8 @@ const Text: TextInterface = (props) => {
hidden = false,
editModalTitle = 'New value',
style,
maxWidth,
clickable,
...rest
} = props;

Expand Down Expand Up @@ -84,27 +88,28 @@ const Text: TextInterface = (props) => {
'root',
'text',
{
'with-maxWidth': Boolean(maxWidth),
[`text--${type}`]: true,
[`text--${size}`]: true,
'text--strong': strong,
'text--underline': underline,
'text--clickable': clickable,
'no-wrap': !wrap,
keyboard,
},
className
)}
style={style}
style={{ ...style, maxWidth }}
{...rest}
>
{hidden ? PLACEHOLDER : children}
{editable && (
<IconButton
onClick={handleEditClick}
variant="primary"
className={cx('icon-button')}
tooltip="Edit"
tooltipPlacement="top"
name="edit"
name="pen"
/>
)}
{copyable && (
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,16 @@
display: flex;
flex-direction: column;
justify-content: space-between;

&--weekend {
background: repeating-linear-gradient(
-45deg,
var(--background-canvas),
var(--background-canvas) 5px,
transparent 5px,
transparent 8px
);
}
}

.weekday-title {
Expand Down
14 changes: 10 additions & 4 deletions src/components/TimelineMarks/TimelineMarks.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,20 +4,23 @@ import cn from 'classnames/bind';
import dayjs from 'dayjs';

import Text from 'components/Text/Text';
import { Timezone } from 'models/timezone/timezone.types';
import { getNow } from 'pages/schedule/Schedule.helpers';

import styles from './TimelineMarks.module.css';
import styles from './TimelineMarks.module.scss';

interface TimelineMarksProps {
startMoment: dayjs.Dayjs;
timezone: Timezone;
debug?: boolean;
}

const cx = cn.bind(styles);

const TimelineMarks: FC<TimelineMarksProps> = (props) => {
const { startMoment, debug } = props;
const { startMoment, timezone, debug } = props;

const currentMoment = useMemo(() => dayjs(), []);
const currentMoment = useMemo(() => getNow(timezone), []);

const momentsToRender = useMemo(() => {
const hoursToSplit = 12;
Expand Down Expand Up @@ -62,11 +65,14 @@ const TimelineMarks: FC<TimelineMarksProps> = (props) => {
))}
</svg>
)}

{momentsToRender.map((m, i) => {
const isCurrentDay = currentMoment.isSame(m.moment, 'day');

// const isWeekend = m.moment.day() == 0 || m.moment.day() === 6;

return (
<div key={i} className={cx('weekday')}>
<div key={i} className={cx('weekday' /* , { 'weekday--weekend': isWeekend } */)}>
<div className={cx('weekday-title')}>
<Text type="secondary" strong={isCurrentDay}>
{m.moment.format('ddd D MMM')}
Expand Down
Loading

0 comments on commit 5c19610

Please sign in to comment.