}
interface ActionsDropdownProps extends Omit<Props, 'children' | 'overlay'> {
+ ariaLabel?: string;
buttonSize?: 'small' | 'medium';
children: React.ReactNode;
}
export function ActionsDropdown(props: ActionsDropdownProps) {
- const { children, buttonSize, ...dropdownProps } = props;
+ const { children, buttonSize, ariaLabel, ...dropdownProps } = props;
return (
<Dropdown overlay={children} {...dropdownProps}>
<InteractiveIcon
Icon={MenuIcon}
- aria-label={translate('menu')}
+ aria-label={ariaLabel ?? translate('menu')}
size={buttonSize}
stopPropagation={false}
/>
export interface IconProps extends Omit<Props, 'children'> {
fill?: ThemeColors | CSSColor;
height?: number;
+ transform?: string;
width?: number;
}
export * from './DateRangePicker';
export { DeferredSpinner } from './DeferredSpinner';
export * from './DiscreetSelect';
-export { Dropdown } from './Dropdown';
+export { ActionsDropdown, Dropdown } from './Dropdown';
export * from './DropdownMenu';
export { DropdownToggler } from './DropdownToggler';
export * from './DuplicationsIndicator';
* along with this program; if not, write to the Free Software Foundation,
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
+import { DestructiveIcon, InteractiveIcon, PencilIcon, TrashIcon } from 'design-system';
import * as React from 'react';
import EventInner from '../../../components/activity-graph/EventInner';
-import { DeleteButton, EditButton } from '../../../components/controls/buttons';
+import Tooltip from '../../../components/controls/Tooltip';
import { translate } from '../../../helpers/l10n';
import { AnalysisEvent, ProjectAnalysisEventCategory } from '../../../types/project-activity';
import ChangeEventForm from './forms/ChangeEventForm';
const showActions = canAdmin && (canChange || canDelete);
return (
- <div className="project-activity-event">
+ <div className="it__project-activity-event sw-flex sw-justify-between">
<EventInner event={event} />
{showActions && (
- <span className="nowrap">
+ <div className="sw-grow-0 sw-shrink-0 sw-ml-2">
{canChange && (
- <EditButton
- aria-label={translate('project_activity.events.tooltip.edit')}
- className="button-small"
- data-test="project-activity__edit-event"
- onClick={() => setChanging(true)}
- stopPropagation
- />
+ <Tooltip overlay={translate('project_activity.events.tooltip.edit')}>
+ <InteractiveIcon
+ Icon={PencilIcon}
+ aria-label={translate('project_activity.events.tooltip.edit')}
+ data-test="project-activity__edit-event"
+ onClick={() => setChanging(true)}
+ stopPropagation
+ size="small"
+ />
+ </Tooltip>
)}
{canDelete && (
- <DeleteButton
- aria-label={translate('project_activity.events.tooltip.delete')}
- className="button-small"
- data-test="project-activity__delete-event"
- onClick={() => setDeleting(true)}
- stopPropagation
- />
+ <Tooltip overlay={translate('project_activity.events.tooltip.delete')}>
+ <DestructiveIcon
+ Icon={TrashIcon}
+ aria-label={translate('project_activity.events.tooltip.delete')}
+ data-test="project-activity__delete-event"
+ onClick={() => setDeleting(true)}
+ stopPropagation
+ size="small"
+ />
+ </Tooltip>
)}
- </span>
+ </div>
)}
{changing && props.onChange && (
);
return (
- <div className="big-spacer-top">
+ <div className="sw-flex sw-flex-1 sw-flex-col sw-gap-1">
{sortedEvents.map((event) => (
<Event
analysisKey={analysisKey}
* along with this program; if not, write to the Free Software Foundation,
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
+import styled from '@emotion/styled';
import classNames from 'classnames';
import { isEqual } from 'date-fns';
+import { Badge, DeferredSpinner, themeColor } from 'design-system';
import * as React from 'react';
import Tooltip from '../../../components/controls/Tooltip';
import DateFormatter from '../../../components/intl/DateFormatter';
import { toShortISO8601String } from '../../../helpers/dates';
import { translate } from '../../../helpers/l10n';
+
import { ComponentQualifier } from '../../../types/component';
import { ParsedAnalysis } from '../../../types/project-activity';
-import { activityQueryChanged, getAnalysesByVersionByDay, Query } from '../utils';
+import { Query, activityQueryChanged, getAnalysesByVersionByDay } from '../utils';
import ProjectActivityAnalysis from './ProjectActivityAnalysis';
interface Props {
(byVersionByDay.length === 1 && Object.keys(byVersionByDay[0].byDay).length > 0);
if (this.props.analyses.length === 0 || !hasFilteredData) {
return (
- <div className="boxed-group-inner">
+ <div>
{this.props.initializing ? (
- <div className="text-center">
- <i className="spinner" />
+ <div className="sw-p-4 sw-body-sm">
+ <DeferredSpinner />
</div>
) : (
- <span className="note">{translate('no_results')}</span>
+ <div className="sw-p-4 sw-body-sm">{translate('no_results')}</div>
)}
</div>
);
return (
<ul
- className="project-activity-versions-list"
+ className="it__project-activity-versions-list sw-box-border sw-max-w-abs-400 sw-overflow-auto sw-grow sw-shrink-0 sw-py-0 sw-px-4"
ref={(element) => (this.scrollContainer = element)}
style={{
marginTop:
if (days.length <= 0) {
return null;
}
+
return (
<li key={version.key || 'noversion'}>
{version.version && (
- <div className={classNames('project-activity-version-badge', { first: idx === 0 })}>
+ <VersionTagStyled
+ className={classNames(
+ 'sw-sticky sw-top-0 sw-left-0 sw-pb-1 -sw-ml-4 sw-pt-3 sw-z-normal',
+ {
+ 'sw-top-0 sw-pt-0': idx === 0,
+ }
+ )}
+ >
<Tooltip
mouseEnterDelay={0.5}
overlay={`${translate('version')} ${version.version}`}
>
- <h2 className="analysis-version">{version.version}</h2>
+ <Badge className="sw-p-1">{version.version}</Badge>
</Tooltip>
- </div>
+ </VersionTagStyled>
)}
- <ul className="project-activity-days-list">
+ <ul className="it__project-activity-days-list">
{days.map((day) => (
<li
- className="project-activity-day"
+ className="it__project-activity-day sw-mt-1 sw-mb-4"
data-day={toShortISO8601String(Number(day))}
key={day}
>
- <h3>
+ <div className="sw-body-md-highlight sw-mb-3">
<DateFormatter date={Number(day)} long />
- </h3>
- <ul className="project-activity-analyses-list">
+ </div>
+ <ul className="it__project-activity-analyses-list">
{version.byDay[day] != null &&
version.byDay[day].map((analysis) => this.renderAnalysis(analysis))}
</ul>
);
})}
{this.props.analysesLoading && (
- <li className="text-center">
- <i className="spinner" />
+ <li className="sw-text-center">
+ <DeferredSpinner />
</li>
)}
</ul>
);
}
}
+
+const VersionTagStyled = styled.div`
+ background-color: ${themeColor('backgroundSecondary')};
+`;
* along with this program; if not, write to the Free Software Foundation,
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
+import styled from '@emotion/styled';
import classNames from 'classnames';
+import {
+ ActionsDropdown,
+ ItemButton,
+ ItemDangerButton,
+ ItemDivider,
+ PopupZLevel,
+ themeBorder,
+ themeColor,
+} from 'design-system';
import * as React from 'react';
-import { injectIntl, WrappedComponentProps } from 'react-intl';
-import ActionsDropdown, {
- ActionsDropdownDivider,
- ActionsDropdownItem,
-} from '../../../components/controls/ActionsDropdown';
-import { ButtonPlain } from '../../../components/controls/buttons';
-import ClickEventBoundary from '../../../components/controls/ClickEventBoundary';
+import { WrappedComponentProps, injectIntl } from 'react-intl';
import HelpTooltip from '../../../components/controls/HelpTooltip';
+import Tooltip from '../../../components/controls/Tooltip';
import { formatterOption } from '../../../components/intl/DateTimeFormatter';
import TimeFormatter from '../../../components/intl/TimeFormatter';
-import { PopupPlacement } from '../../../components/ui/popups';
import { parseDate } from '../../../helpers/dates';
import { translate, translateWithParameters } from '../../../helpers/l10n';
import { ParsedAnalysis } from '../../../types/project-activity';
React.useEffect(() => {
if (node && selected) {
- node.scrollIntoView({ behavior: 'smooth' });
+ node.scrollIntoView({ behavior: 'smooth', block: 'center' });
}
});
const canDeleteAnalyses =
props.canDeleteAnalyses && !isFirst && !analysis.manualNewCodePeriodBaseline;
+ let tooltipContent = <TimeFormatter date={parsedDate} long />;
+ if (analysis.buildString) {
+ tooltipContent = (
+ <>
+ {tooltipContent}
+ {translateWithParameters('project_activity.analysis_build_string_X', analysis.buildString)}
+ </>
+ );
+ }
+
return (
- <li
- className={classNames('project-activity-analysis bordered-top bordered-bottom', {
- selected,
- })}
- onClick={() => props.onUpdateSelectedDate(analysis.date)}
- ref={(ref) => (node = ref)}
- >
- <div className="display-flex-center display-flex-space-between">
- <div className="project-activity-time">
- <TimeFormatter date={parsedDate} long={false}>
- {(formattedTime) => (
- <ButtonPlain
- aria-current={selected}
- aria-label={translateWithParameters(
- 'project_activity.show_analysis_X_on_graph',
- analysis.buildString || formatDate(parsedDate, formatterOption)
- )}
- onClick={() => props.onUpdateSelectedDate(analysis.date)}
- >
- <time className="text-middle" dateTime={parsedDate.toISOString()}>
- {formattedTime}
- </time>
- </ButtonPlain>
- )}
- </TimeFormatter>
+ <Tooltip mouseEnterDelay={0.5} overlay={tooltipContent} placement="left">
+ <ActivityAnalysisListItem
+ className={classNames(
+ 'it__project-activity-analysis sw-flex sw-cursor-pointer sw-p-1 sw-relative',
+ {
+ active: selected,
+ }
+ )}
+ aria-label={translateWithParameters(
+ 'project_activity.show_analysis_X_on_graph',
+ analysis.buildString || formatDate(parsedDate, formatterOption)
+ )}
+ onClick={() => props.onUpdateSelectedDate(analysis.date)}
+ ref={(ref) => (node = ref)}
+ >
+ <div className="it__project-activity-time">
+ <ActivityTime className="sw-box-border sw-grow-0 sw-shrink-0 sw-h-page sw-body-sm-highlight sw-text-right sw-mr-2">
+ <TimeFormatter date={parsedDate} long={false}>
+ {(formattedTime) => <time dateTime={parsedDate.toISOString()}>{formattedTime}</time>}
+ </TimeFormatter>
+ </ActivityTime>
</div>
- {analysis.buildString && (
- <div className="flex-shrink small text-muted text-ellipsis">
- {translateWithParameters(
- 'project_activity.analysis_build_string_X',
- analysis.buildString
+ {(canAddVersion || canAddEvent || canDeleteAnalyses) && (
+ <div className="sw-h-page sw-grow-0 sw-shrink-0 sw-mr-4 sw-relative">
+ <ActionsDropdown
+ ariaLabel={translateWithParameters(
+ 'project_activity.analysis_X_actions',
+ analysis.buildString || formatDate(parsedDate, formatterOption)
+ )}
+ buttonSize="small"
+ id="it__analysis-actions"
+ zLevel={PopupZLevel.Absolute}
+ >
+ {canAddVersion && (
+ <ItemButton className="js-add-version" onClick={() => setAddVersionForm(true)}>
+ {translate('project_activity.add_version')}
+ </ItemButton>
+ )}
+ {canAddEvent && (
+ <ItemButton className="js-add-event" onClick={() => setAddEventForm(true)}>
+ {translate('project_activity.add_custom_event')}
+ </ItemButton>
+ )}
+ {(canAddVersion || canAddEvent) && canDeleteAnalyses && <ItemDivider />}
+ {canDeleteAnalyses && (
+ <ItemDangerButton
+ className="js-delete-analysis"
+ onClick={() => setRemoveAnalysisForm(true)}
+ >
+ {translate('project_activity.delete_analysis')}
+ </ItemDangerButton>
+ )}
+ </ActionsDropdown>
+
+ {addVersionForm && (
+ <AddEventForm
+ addEvent={props.onAddVersion}
+ addEventButtonText="project_activity.add_version"
+ analysis={analysis}
+ onClose={() => setAddVersionForm(false)}
+ />
+ )}
+
+ {addEventForm && (
+ <AddEventForm
+ addEvent={props.onAddCustomEvent}
+ addEventButtonText="project_activity.add_custom_event"
+ analysis={analysis}
+ onClose={() => setAddEventForm(false)}
+ />
+ )}
+
+ {removeAnalysisForm && (
+ <RemoveAnalysisForm
+ analysis={analysis}
+ deleteAnalysis={props.onDeleteAnalysis}
+ onClose={() => setRemoveAnalysisForm(false)}
+ />
)}
</div>
)}
- {(canAddVersion || canAddEvent || canDeleteAnalyses) && (
- <ClickEventBoundary>
- <div className="project-activity-analysis-actions big-spacer-left">
- <ActionsDropdown
- label={translateWithParameters(
- 'project_activity.analysis_X_actions',
- analysis.buildString || formatDate(parsedDate, formatterOption)
- )}
- overlayPlacement={PopupPlacement.BottomRight}
- small
- toggleClassName="js-analysis-actions"
- >
- {canAddVersion && (
- <ActionsDropdownItem
- className="js-add-version"
- onClick={() => setAddVersionForm(true)}
- >
- {translate('project_activity.add_version')}
- </ActionsDropdownItem>
- )}
- {canAddEvent && (
- <ActionsDropdownItem
- className="js-add-event"
- onClick={() => setAddEventForm(true)}
- >
- {translate('project_activity.add_custom_event')}
- </ActionsDropdownItem>
- )}
- {(canAddVersion || canAddEvent) && canDeleteAnalyses && <ActionsDropdownDivider />}
- {canDeleteAnalyses && (
- <ActionsDropdownItem
- className="js-delete-analysis"
- destructive
- onClick={() => setRemoveAnalysisForm(true)}
- >
- {translate('project_activity.delete_analysis')}
- </ActionsDropdownItem>
- )}
- </ActionsDropdown>
-
- {addVersionForm && (
- <AddEventForm
- addEvent={props.onAddVersion}
- addEventButtonText="project_activity.add_version"
- analysis={analysis}
- onClose={() => setAddVersionForm(false)}
- />
- )}
-
- {addEventForm && (
- <AddEventForm
- addEvent={props.onAddCustomEvent}
- addEventButtonText="project_activity.add_custom_event"
- analysis={analysis}
- onClose={() => setAddEventForm(false)}
- />
- )}
+ {analysis.events.length > 0 && (
+ <Events
+ analysisKey={analysis.key}
+ canAdmin={canAdmin}
+ events={analysis.events}
+ isFirst={isFirst}
+ onChange={props.onChangeEvent}
+ onDelete={props.onDeleteEvent}
+ />
+ )}
- {removeAnalysisForm && (
- <RemoveAnalysisForm
- analysis={analysis}
- deleteAnalysis={props.onDeleteAnalysis}
- onClose={() => setRemoveAnalysisForm(false)}
- />
- )}
+ {isBaseline && (
+ <div className="baseline-marker">
+ <div className="wedge" />
+ <hr />
+ <div className="label display-flex-center">
+ {translate('project_activity.new_code_period_start')}
+ <HelpTooltip
+ className="little-spacer-left"
+ overlay={translate('project_activity.new_code_period_start.help')}
+ placement="top"
+ />
</div>
- </ClickEventBoundary>
- )}
- </div>
-
- {analysis.events.length > 0 && (
- <Events
- analysisKey={analysis.key}
- canAdmin={canAdmin}
- events={analysis.events}
- isFirst={isFirst}
- onChange={props.onChangeEvent}
- onDelete={props.onDeleteEvent}
- />
- )}
-
- {isBaseline && (
- <div className="baseline-marker">
- <div className="wedge" />
- <hr />
- <div className="label display-flex-center">
- {translate('project_activity.new_code_period_start')}
- <HelpTooltip
- className="little-spacer-left"
- overlay={translate('project_activity.new_code_period_start.help')}
- placement="top"
- />
</div>
- </div>
- )}
- </li>
+ )}
+ </ActivityAnalysisListItem>
+ </Tooltip>
);
}
+const ActivityTime = styled.div`
+ width: 4.5rem;
+`;
+
+const ActivityAnalysisListItem = styled.li`
+ border-bottom: ${themeBorder('default')};
+ border-left: ${themeBorder('active', 'transparent')};
+
+ &:first-of-type {
+ border-top: ${themeBorder('default')};
+ }
+
+ &:focus {
+ outline: none;
+ }
+
+ &:hover,
+ &:focus,
+ &.active {
+ background-color: ${themeColor('subnavigationHover')};
+ }
+
+ &.active {
+ border-left: ${themeBorder('active')};
+ }
+`;
+
export default injectIntl(ProjectActivityAnalysis);
await ui.appLoaded();
await ui.addVersionEvent('1.1.0.1', initialValue);
- expect(screen.getAllByText(initialValue)[0]).toBeInTheDocument();
+ // should appear 3x, one for the list, one for the graph and one for the tooltip
+ expect(screen.getAllByText(initialValue).length).toEqual(3);
await act(async () => {
await ui.updateEvent(1, updatedValue);
- expect(screen.getAllByText(updatedValue)[0]).toBeInTheDocument();
+ // should appear 3x, one for the list, one for the graph and one for the tooltip
+ expect(screen.getAllByText(updatedValue).length).toEqual(3);
});
await ui.deleteEvent(0);
await act(async () => {
await ui.addCustomEvent('1.1.0.1', initialValue);
- expect(screen.getByText(initialValue)).toBeInTheDocument();
+ expect(screen.getAllByText(initialValue)[0]).toBeInTheDocument();
});
await act(async () => {
await ui.updateEvent(1, updatedValue);
- expect(screen.getByText(updatedValue)).toBeInTheDocument();
+ expect(screen.getAllByText(updatedValue)[0]).toBeInTheDocument();
});
await ui.deleteEvent(0);
activityItem: byLabelText(/project_activity.show_analysis_X_on_graph/),
cogBtn: (id: string) => byRole('button', { name: `project_activity.analysis_X_actions.${id}` }),
seeDetailsBtn: (time: string) =>
- byRole('button', { name: `project_activity.show_analysis_X_on_graph.${time}` }),
- addCustomEventBtn: byRole('button', { name: 'project_activity.add_custom_event' }),
- addVersionEvenBtn: byRole('button', { name: 'project_activity.add_version' }),
- deleteAnalysisBtn: byRole('button', { name: 'project_activity.delete_analysis' }),
+ byLabelText(`project_activity.show_analysis_X_on_graph.${time}`),
+ addCustomEventBtn: byRole('menuitem', { name: 'project_activity.add_custom_event' }),
+ addVersionEvenBtn: byRole('menuitem', { name: 'project_activity.add_version' }),
+ deleteAnalysisBtn: byRole('menuitem', { name: 'project_activity.delete_analysis' }),
editEventBtn: byRole('button', { name: 'project_activity.events.tooltip.edit' }),
deleteEventBtn: byRole('button', { name: 'project_activity.events.tooltip.delete' }),
* along with this program; if not, write to the Free Software Foundation,
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
+import { ButtonPrimary, InputField, Modal } from 'design-system';
import * as React from 'react';
-import ConfirmModal from '../../../../components/controls/ConfirmModal';
import { translate } from '../../../../helpers/l10n';
import { ParsedAnalysis } from '../../../../types/project-activity';
};
handleSubmit = () => {
- return this.props.addEvent(this.props.analysis.key, this.state.name);
+ this.props.addEvent(this.props.analysis.key, this.state.name);
+ this.props.onClose();
};
render() {
return (
- <ConfirmModal
- confirmButtonText={translate('save')}
- confirmDisable={!this.state.name}
- header={translate(this.props.addEventButtonText)}
+ <Modal
+ headerTitle={translate(this.props.addEventButtonText)}
onClose={this.props.onClose}
- onConfirm={this.handleSubmit}
- size="small"
- >
- <div className="modal-field">
- <label htmlFor="name">{translate('name')}</label>
- <input
- id="name"
- autoFocus
- onChange={this.handleNameChange}
- type="text"
- value={this.state.name}
- />
- </div>
- </ConfirmModal>
+ body={
+ <div>
+ <label htmlFor="name">{translate('name')}</label>
+ <InputField
+ id="name"
+ autoFocus
+ onChange={this.handleNameChange}
+ type="text"
+ value={this.state.name}
+ size="full"
+ />
+ </div>
+ }
+ primaryButton={
+ <ButtonPrimary
+ id="add-event-submit"
+ form="add-event-form"
+ type="submit"
+ disabled={!this.state.name}
+ onClick={this.handleSubmit}
+ >
+ {translate('save')}
+ </ButtonPrimary>
+ }
+ secondaryButtonLabel={translate('cancel')}
+ />
);
}
}
* along with this program; if not, write to the Free Software Foundation,
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
+import { ButtonPrimary, InputField, Modal } from 'design-system';
import * as React from 'react';
-import ConfirmModal from '../../../../components/controls/ConfirmModal';
import { translate } from '../../../../helpers/l10n';
import { AnalysisEvent } from '../../../../types/project-activity';
};
handleSubmit = () => {
- return this.props.changeEvent(this.props.event.key, this.state.name);
+ this.props.changeEvent(this.props.event.key, this.state.name);
+ this.props.onClose();
};
render() {
const { name } = this.state;
return (
- <ConfirmModal
- confirmButtonText={translate('change_verb')}
- confirmDisable={!name || name === this.props.event.name}
- header={this.props.header}
+ <Modal
+ headerTitle={this.props.header}
onClose={this.props.onClose}
- onConfirm={this.handleSubmit}
- size="small"
- >
- <div className="modal-field">
- <label htmlFor="name">{translate('name')}</label>
- <input id="name" autoFocus onChange={this.changeInput} type="text" value={name} />
- </div>
- </ConfirmModal>
+ body={
+ <div>
+ <label htmlFor="name">{translate('name')}</label>
+ <InputField
+ id="name"
+ autoFocus
+ onChange={this.changeInput}
+ type="text"
+ value={name}
+ size="full"
+ />
+ </div>
+ }
+ primaryButton={
+ <ButtonPrimary
+ id="change-event-submit"
+ form="change-event-form"
+ type="submit"
+ disabled={!name || name === this.props.event.name}
+ onClick={this.handleSubmit}
+ >
+ {translate('change_verb')}
+ </ButtonPrimary>
+ }
+ secondaryButtonLabel={translate('cancel')}
+ />
);
}
}
* along with this program; if not, write to the Free Software Foundation,
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
+import { DangerButtonPrimary, Modal } from 'design-system';
import * as React from 'react';
-import ConfirmModal from '../../../../components/controls/ConfirmModal';
import { translate } from '../../../../helpers/l10n';
import { ParsedAnalysis } from '../../../../types/project-activity';
export default function RemoveAnalysisForm({ analysis, deleteAnalysis, onClose }: Props) {
return (
- <ConfirmModal
- confirmButtonText={translate('delete')}
- confirmData={analysis.key}
- header={translate('project_activity.delete_analysis')}
- isDestructive
+ <Modal
+ headerTitle={translate('project_activity.delete_analysis')}
onClose={onClose}
- onConfirm={deleteAnalysis}
- >
- {translate('project_activity.delete_analysis.question')}
- </ConfirmModal>
+ body={<p>{translate('project_activity.delete_analysis.question')}</p>}
+ primaryButton={
+ <DangerButtonPrimary onClick={() => deleteAnalysis(analysis.key)} type="submit">
+ {translate('delete')}
+ </DangerButtonPrimary>
+ }
+ secondaryButtonLabel={translate('cancel')}
+ />
);
}
* along with this program; if not, write to the Free Software Foundation,
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
+import { DangerButtonPrimary, Modal } from 'design-system';
import * as React from 'react';
-import ConfirmModal from '../../../../components/controls/ConfirmModal';
import { translate } from '../../../../helpers/l10n';
import { AnalysisEvent } from '../../../../types/project-activity';
export default function RemoveEventForm(props: RemoveEventFormProps) {
const { analysisKey, event, header, removeEventQuestion } = props;
return (
- <ConfirmModal
- confirmButtonText={translate('delete')}
- header={header}
- isDestructive
+ <Modal
+ headerTitle={header}
onClose={props.onClose}
- onConfirm={() => props.onConfirm(analysisKey, event.key)}
- >
- {removeEventQuestion}
- </ConfirmModal>
+ body={<p>{removeEventQuestion}</p>}
+ primaryButton={
+ <DangerButtonPrimary onClick={() => props.onConfirm(analysisKey, event.key)}>
+ {translate('delete')}
+ </DangerButtonPrimary>
+ }
+ secondaryButtonLabel={translate('cancel')}
+ />
);
}
* along with this program; if not, write to the Free Software Foundation,
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
+import { BareButton, ChevronDownIcon, StandoutLink } from 'design-system';
import * as React from 'react';
import { FormattedMessage } from 'react-intl';
import { isMainBranch } from '../../helpers/branch-like';
ApplicationAnalysisEventCategory,
DefinitionChangeType,
} from '../../types/project-activity';
-import Link from '../common/Link';
-import { ButtonLink } from '../controls/buttons';
import ClickEventBoundary from '../controls/ClickEventBoundary';
import BranchIcon from '../icons/BranchIcon';
-import DropdownIcon from '../icons/DropdownIcon';
export type DefinitionChangeEvent = AnalysisEvent &
Required<Pick<AnalysisEvent, 'definitionChange'>>;
renderProjectLink = (project: { key: string; name: string }, branch: string | undefined) => (
<ClickEventBoundary>
- <Link title={project.name} to={getProjectUrl(project.key, branch)}>
+ <StandoutLink title={project.name} to={getProjectUrl(project.key, branch)}>
{limitComponentName(project.name, NAME_MAX_LENGTH)}
- </Link>
+ </StandoutLink>
</ClickEventBoundary>
);
? 'event.definition_change.added'
: 'event.definition_change.branch_added';
return (
- <div className="text-ellipsis">
- <FormattedMessage
- defaultMessage={translate(message)}
- id={message}
- values={{
- project: this.renderProjectLink(project, project.branch),
- branch: this.renderBranch(project.branch),
- }}
- />
- </div>
+ <FormattedMessage
+ defaultMessage={translate(message)}
+ id={message}
+ values={{
+ project: this.renderProjectLink(project, project.branch),
+ branch: this.renderBranch(project.branch),
+ }}
+ />
);
}
? 'event.definition_change.removed'
: 'event.definition_change.branch_removed';
return (
- <div className="text-ellipsis">
- <FormattedMessage
- defaultMessage={translate(message)}
- id={message}
- values={{
- project: this.renderProjectLink(project, project.branch),
- branch: this.renderBranch(project.branch),
- }}
- />
- </div>
+ <FormattedMessage
+ defaultMessage={translate(message)}
+ id={message}
+ values={{
+ project: this.renderProjectLink(project, project.branch),
+ branch: this.renderBranch(project.branch),
+ }}
+ />
);
}
const { event, readonly } = this.props;
const { expanded } = this.state;
return (
- <>
- <span className="note">
- {translate('event.category', event.category)}
- {!readonly && ':'}
- </span>
-
- {!readonly && (
- <div>
- <ButtonLink
- className="project-activity-event-inner-more-link"
- onClick={this.toggleProjectsList}
- stopPropagation
- >
- {expanded ? translate('hide') : translate('more')}
- <DropdownIcon className="little-spacer-left" turned={expanded} />
- </ButtonLink>
- </div>
- )}
+ <div className="sw-flex sw-basis-full sw-flex-col">
+ <div className="sw-flex sw-justify-between">
+ <span className="sw-mr-1">{translate('event.category', event.category)}</span>
+
+ {!readonly && (
+ <div>
+ <BareButton onClick={this.toggleProjectsList}>
+ {expanded ? translate('hide') : translate('more')}
+ <ChevronDownIcon transform={expanded ? 'rotate(180)' : undefined} />
+ </BareButton>
+ </div>
+ )}
+ </div>
{expanded && (
- <ul className="spacer-left spacer-top">
+ <ul className="sw-mt-2">
{event.definitionChange.projects.map((project) => (
- <li className="display-flex-center spacer-top" key={project.key}>
+ <li className="sw-p-1 sw-text-ellipsis sw-overflow-hidden" key={project.key}>
{this.renderProjectChange(project)}
</li>
))}
</ul>
)}
- </>
+ </div>
);
}
}
* along with this program; if not, write to the Free Software Foundation,
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
+import { Note } from 'design-system';
import * as React from 'react';
import { ComponentContext } from '../../app/components/componentContext/ComponentContext';
import { translate } from '../../helpers/l10n';
import { AnalysisEvent } from '../../types/project-activity';
import Tooltip from '../controls/Tooltip';
import { DefinitionChangeEventInner, isDefinitionChangeEvent } from './DefinitionChangeEventInner';
-import { isRichQualityGateEvent, RichQualityGateEventInner } from './RichQualityGateEventInner';
+import { RichQualityGateEventInner, isRichQualityGateEvent } from './RichQualityGateEventInner';
export interface EventInnerProps {
event: AnalysisEvent;
}
return (
<Tooltip overlay={event.description}>
- <span className="text-middle">
- <span className="note little-spacer-right">
- {translate('event.category', event.category)}:
- </span>
- <strong className="spacer-right">{event.name}</strong>
- </span>
+ <div className="sw-min-w-0 sw-flex-1">
+ <div className="sw-flex sw-items-start">
+ <span>
+ <Note className="sw-mr-1 sw-body-sm-highlight">
+ {translate('event.category', event.category)}
+ {event.category === 'VERSION' && ':'}
+ </Note>
+ <Note className="sw-body-sm" title={event.description}>
+ {event.name}
+ </Note>
+ </span>
+ </div>
+ </div>
</Tooltip>
);
}
* along with this program; if not, write to the Free Software Foundation,
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
+import { BareButton, ChevronDownIcon, QualityGateIndicator, StandoutLink } from 'design-system';
import * as React from 'react';
import { FormattedMessage } from 'react-intl';
import { translate, translateWithParameters } from '../../helpers/l10n';
import { getProjectUrl } from '../../helpers/urls';
import { AnalysisEvent } from '../../types/project-activity';
-import Link from '../common/Link';
-import { ResetButtonLink } from '../controls/buttons';
import ClickEventBoundary from '../controls/ClickEventBoundary';
-import DropdownIcon from '../icons/DropdownIcon';
-import Level from '../ui/Level';
export type RichQualityGateEvent = AnalysisEvent & Required<Pick<AnalysisEvent, 'qualityGate'>>;
const { event, readonly } = this.props;
const { expanded } = this.state;
return (
- <>
- <span className="note spacer-right">{translate('event.category', event.category)}:</span>
- {event.qualityGate.stillFailing ? (
- <FormattedMessage
- defaultMessage={translate('event.quality_gate.still_x')}
- id="event.quality_gate.still_x"
- values={{ status: <Level level={event.qualityGate.status} small /> }}
- />
- ) : (
- <Level level={event.qualityGate.status} small />
- )}
+ <div className="sw-flex sw-basis-full sw-flex-col">
+ <div className="sw-flex sw-justify-between">
+ <div className="sw-flex sw-justify-between">
+ <span className="sw-mr-1">{translate('event.category', event.category)}:</span>
+ <div className="sw-mx-2">
+ {event.qualityGate.stillFailing ? (
+ <FormattedMessage
+ defaultMessage={translate('event.quality_gate.still_x')}
+ id="event.quality_gate.still_x"
+ values={{
+ status: <QualityGateIndicator status={event.qualityGate.status} size="sm" />,
+ }}
+ />
+ ) : (
+ <QualityGateIndicator status={event.qualityGate.status} size="sm" />
+ )}
+ </div>
+ <span>{translate(`event.quality_gate.${event.qualityGate.status}`)}</span>
+ </div>
- <div>
{!readonly && event.qualityGate.failing.length > 0 && (
- <ResetButtonLink
- className="project-activity-event-inner-more-link"
- onClick={this.toggleProjectsList}
- stopPropagation
- >
- {expanded ? translate('hide') : translate('more')}
- <DropdownIcon className="little-spacer-left" turned={expanded} />
- </ResetButtonLink>
+ <div>
+ <BareButton onClick={this.toggleProjectsList}>
+ {expanded ? translate('hide') : translate('more')}
+ <ChevronDownIcon transform={expanded ? 'rotate(180)' : undefined} />
+ </BareButton>
+ </div>
)}
</div>
{expanded && (
- <ul className="spacer-left spacer-top">
+ <ul className="sw-flex sw-flex-col sw-mt-2">
{event.qualityGate.failing.map((project) => (
- <li className="display-flex-center spacer-top" key={project.key}>
- <Level
- aria-label={translate('quality_gates.status')}
- className="spacer-right"
- level={event.qualityGate.status}
- small
- />
- <div className="flex-1 text-ellipsis">
+ <li className="sw-flex sw-p-1" key={project.key}>
+ <div>
<ClickEventBoundary>
- <Link title={project.name} to={getProjectUrl(project.key, project.branch)}>
+ <StandoutLink
+ title={project.name}
+ to={getProjectUrl(project.key, project.branch)}
+ >
<span aria-label={translateWithParameters('project_x', project.name)}>
{project.name}
</span>
- </Link>
+ </StandoutLink>
</ClickEventBoundary>
</div>
+ <div className="sw-shrink sw-flex">
+ <div className="sw-items-top">
+ <QualityGateIndicator status={event.qualityGate.status} size="sm" />
+ </div>
+ <span className="sw-ml-2">
+ {translate(`event.quality_gate.${event.qualityGate.status}`)}
+ </span>
+ </div>
</li>
))}
</ul>
)}
- </>
+ </div>
);
}
}