Unverified Commit d2f222ea authored by Allison Suarez Miranda's avatar Allison Suarez Miranda Committed by GitHub

feat: Update 'table view', 'alumni' and SLA status badges (#595)

* removed badges section for users cause we dont need that moving forward
Signed-off-by: 's avatarAllison Suarez Miranda <asuarezmiranda@lyft.com>

* removed ALumni badge from user profile and added it to header-bullets instead
Signed-off-by: 's avatarAllison Suarez Miranda <asuarezmiranda@lyft.com>

* removed Alumni badge checks for user profiles
Signed-off-by: 's avatarAllison Suarez Miranda <asuarezmiranda@lyft.com>

* not working for some reason
Signed-off-by: 's avatarAllison Suarez Miranda <asuarezmiranda@lyft.com>

* lint and missed test update
Signed-off-by: 's avatarAllison Suarez Miranda <asuarezmiranda@lyft.com>

* fixed conflict
Signed-off-by: 's avatarAllison Suarez Miranda <asuarezmiranda@lyft.com>

* updated DashboardPage tests
Signed-off-by: 's avatarAllison Suarez Miranda <asuarezmiranda@lyft.com>

* added icons
Signed-off-by: 's avatarAllison Suarez Miranda <asuarezmiranda@lyft.com>

* lint
Signed-off-by: 's avatarAllison Suarez Miranda <asuarezmiranda@lyft.com>

* fixed alignment issue
Signed-off-by: 's avatarAllison Suarez Miranda <asuarezmiranda@lyft.com>

* fixed table view
Signed-off-by: 's avatarAllison Suarez Miranda <asuarezmiranda@lyft.com>

* added variables for style
Signed-off-by: 's avatarAllison Suarez Miranda <asuarezmiranda@lyft.com>

* made component more genric, still need to rename and fix icon issue
Signed-off-by: 's avatarAllison Suarez Miranda <asuarezmiranda@lyft.com>

* updated text to use typography defined
Signed-off-by: 's avatarAllison Suarez Miranda <asuarezmiranda@lyft.com>

* variable
Signed-off-by: 's avatarAllison Suarez Miranda <asuarezmiranda@lyft.com>

* hit icon broken
Signed-off-by: 's avatarAllison Suarez Miranda <asuarezmiranda@lyft.com>

* fixed hit icon a bit
Signed-off-by: 's avatarAllison Suarez Miranda <asuarezmiranda@lyft.com>

* fixed tets
Signed-off-by: 's avatarAllison Suarez Miranda <asuarezmiranda@lyft.com>

* moved setup into it clause
Signed-off-by: 's avatarAllison Suarez Miranda <asuarezmiranda@lyft.com>

* added resource badges back in
Signed-off-by: 's avatarAllison Suarez Miranda <asuarezmiranda@lyft.com>

* lint + added back tests
Signed-off-by: 's avatarAllison Suarez Miranda <asuarezmiranda@lyft.com>

* fixes
Signed-off-by: 's avatarAllison Suarez Miranda <asuarezmiranda@lyft.com>

* lint
Signed-off-by: 's avatarAllison Suarez Miranda <asuarezmiranda@lyft.com>

* fixed icon for hit
Signed-off-by: 's avatarAllison Suarez Miranda <asuarezmiranda@lyft.com>

* bettered updated file
Signed-off-by: 's avatarAllison Suarez Miranda <asuarezmiranda@lyft.com>

* removed text prop
Signed-off-by: 's avatarAllison Suarez Miranda <asuarezmiranda@lyft.com>

* fixed text issue
Signed-off-by: 's avatarAllison Suarez Miranda <asuarezmiranda@lyft.com>

* small fix
Signed-off-by: 's avatarAllison Suarez Miranda <asuarezmiranda@lyft.com>

* cleanup
Signed-off-by: 's avatarAllison Suarez Miranda <asuarezmiranda@lyft.com>
parent 7c9a29a7
...@@ -29,6 +29,10 @@ $users: ( ...@@ -29,6 +29,10 @@ $users: (
users: '/static/images/icons/users.svg', users: '/static/images/icons/users.svg',
); );
$check: (
check: '/static/images/icons/check.svg',
);
// Given a Map of key/value pairs, generates a new class // Given a Map of key/value pairs, generates a new class
@mixin iconBackgrounds($map) { @mixin iconBackgrounds($map) {
@each $name, $url in $map { @each $name, $url in $map {
...@@ -43,6 +47,7 @@ span.icon { ...@@ -43,6 +47,7 @@ span.icon {
@include iconBackgrounds($data-stores); @include iconBackgrounds($data-stores);
@include iconBackgrounds($dashboards); @include iconBackgrounds($dashboards);
@include iconBackgrounds($users); @include iconBackgrounds($users);
@include iconBackgrounds($check);
background-color: $icon-bg; background-color: $icon-bg;
border: none; border: none;
...@@ -55,6 +60,8 @@ span.icon { ...@@ -55,6 +60,8 @@ span.icon {
} }
img.icon { img.icon {
/*DEPRECATED: follow behavior above to generate
icons*/
background-color: $icon-bg; background-color: $icon-bg;
border: none; border: none;
height: $icon-size; height: $icon-size;
......
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="#F2F2F2" stroke-width="3" stroke-linecap="round" stroke-linejoin="round" class="feather feather-check"><polyline points="20 6 9 17 4 12"></polyline></svg>
\ No newline at end of file
...@@ -7,4 +7,3 @@ export const ADD_DESC_TEXT = 'Add Description in'; ...@@ -7,4 +7,3 @@ export const ADD_DESC_TEXT = 'Add Description in';
export const EDIT_DESC_TEXT = 'Click to edit description in'; export const EDIT_DESC_TEXT = 'Click to edit description in';
export const LAST_RUN_SUCCEEDED = 'succeeded'; export const LAST_RUN_SUCCEEDED = 'succeeded';
export const LAST_RUN_FAILED = 'failed';
...@@ -10,13 +10,11 @@ import { shallow } from 'enzyme'; ...@@ -10,13 +10,11 @@ import { shallow } from 'enzyme';
import LoadingSpinner from 'components/common/LoadingSpinner'; import LoadingSpinner from 'components/common/LoadingSpinner';
import Breadcrumb from 'components/common/Breadcrumb'; import Breadcrumb from 'components/common/Breadcrumb';
import BookmarkIcon from 'components/common/Bookmark/BookmarkIcon'; import BookmarkIcon from 'components/common/Bookmark/BookmarkIcon';
import Flag from 'components/common/Flag';
import ResourceList from 'components/common/ResourceList'; import ResourceList from 'components/common/ResourceList';
import TabsComponent from 'components/common/TabsComponent'; import TabsComponent from 'components/common/TabsComponent';
import { dashboardMetadata } from 'fixtures/metadata/dashboard'; import { dashboardMetadata } from 'fixtures/metadata/dashboard';
import { NO_TIMESTAMP_TEXT } from 'components/constants'; import { NO_TIMESTAMP_TEXT } from 'components/constants';
import { ResourceType } from 'interfaces'; import { ResourceType } from 'interfaces';
import { BadgeStyle } from 'config/config-types';
import ChartList from './ChartList'; import ChartList from './ChartList';
import DashboardOwnerEditor from './DashboardOwnerEditor'; import DashboardOwnerEditor from './DashboardOwnerEditor';
import ImagePreview from './ImagePreview'; import ImagePreview from './ImagePreview';
...@@ -139,26 +137,6 @@ describe('DashboardPage', () => { ...@@ -139,26 +137,6 @@ describe('DashboardPage', () => {
}); });
}); });
describe('mapStatusToStyle', () => {
let wrapper;
beforeAll(() => {
({ wrapper } = setup());
});
it('returns BadgeStyle.SUCCESS if status === LAST_RUN_SUCCEEDED', () => {
expect(
wrapper.instance().mapStatusToStyle(Constants.LAST_RUN_SUCCEEDED)
).toBe(BadgeStyle.SUCCESS);
});
it('returns BadgeStyle.DANGER if status !== LAST_RUN_SUCCEEDED', () => {
expect(wrapper.instance().mapStatusToStyle('anythingelse')).toBe(
BadgeStyle.DANGER
);
});
});
describe('render', () => { describe('render', () => {
const { props, wrapper } = setup(); const { props, wrapper } = setup();
...@@ -215,19 +193,13 @@ describe('DashboardPage', () => { ...@@ -215,19 +193,13 @@ describe('DashboardPage', () => {
expect(wrapper.find(DashboardOwnerEditor).exists()).toBe(true); expect(wrapper.find(DashboardOwnerEditor).exists()).toBe(true);
}); });
it('renders a Flag for last run state', () => { it('renders a ResourceStatusMarker for last run state', () => {
const mockStyle = BadgeStyle.DANGER; const expected = 1;
const mapStatusToStyleSpy = jest const actual = wrapper
.spyOn(wrapper.instance(), 'mapStatusToStyle') .find('.last-run-state')
.mockImplementationOnce(() => mockStyle); .find('ResourceStatusMarker').length;
wrapper.instance().forceUpdate();
const element = wrapper.find('.last-run-state').find(Flag); expect(actual).toEqual(expected);
expect(element.props().text).toBe(props.dashboard.last_run_state);
expect(mapStatusToStyleSpy).toHaveBeenCalledWith(
props.dashboard.last_run_state
);
expect(element.props().labelStyle).toBe(mockStyle);
}); });
it('renders an ImagePreview with correct props', () => { it('renders an ImagePreview with correct props', () => {
......
...@@ -13,7 +13,6 @@ import * as ReactMarkdown from 'react-markdown'; ...@@ -13,7 +13,6 @@ import * as ReactMarkdown from 'react-markdown';
import AvatarLabel from 'components/common/AvatarLabel'; import AvatarLabel from 'components/common/AvatarLabel';
import Breadcrumb from 'components/common/Breadcrumb'; import Breadcrumb from 'components/common/Breadcrumb';
import BookmarkIcon from 'components/common/Bookmark/BookmarkIcon'; import BookmarkIcon from 'components/common/Bookmark/BookmarkIcon';
import Flag from 'components/common/Flag';
import EditableSection from 'components/common/EditableSection'; import EditableSection from 'components/common/EditableSection';
import LoadingSpinner from 'components/common/LoadingSpinner'; import LoadingSpinner from 'components/common/LoadingSpinner';
import TabsComponent from 'components/common/TabsComponent'; import TabsComponent from 'components/common/TabsComponent';
...@@ -25,15 +24,16 @@ import { DashboardMetadata } from 'interfaces/Dashboard'; ...@@ -25,15 +24,16 @@ import { DashboardMetadata } from 'interfaces/Dashboard';
import DashboardOwnerEditor from 'components/DashboardPage/DashboardOwnerEditor'; import DashboardOwnerEditor from 'components/DashboardPage/DashboardOwnerEditor';
import QueryList from 'components/DashboardPage/QueryList'; import QueryList from 'components/DashboardPage/QueryList';
import ChartList from 'components/DashboardPage/ChartList'; import ChartList from 'components/DashboardPage/ChartList';
import ResourceStatusMarker from 'components/common/ResourceStatusMarker';
import { formatDateTimeShort } from 'utils/dateUtils'; import { formatDateTimeShort } from 'utils/dateUtils';
import ResourceList from 'components/common/ResourceList'; import ResourceList from 'components/common/ResourceList';
import { import {
ADD_DESC_TEXT, ADD_DESC_TEXT,
EDIT_DESC_TEXT, EDIT_DESC_TEXT,
DASHBOARD_SOURCE,
LAST_RUN_SUCCEEDED,
OWNER_HEADER_TEXT, OWNER_HEADER_TEXT,
DASHBOARD_SOURCE,
TABLES_PER_PAGE, TABLES_PER_PAGE,
LAST_RUN_SUCCEEDED,
} from 'components/DashboardPage/constants'; } from 'components/DashboardPage/constants';
import TagInput from 'components/common/Tags/TagInput'; import TagInput from 'components/common/Tags/TagInput';
import { ResourceType } from 'interfaces'; import { ResourceType } from 'interfaces';
...@@ -109,11 +109,11 @@ export class DashboardPage extends React.Component< ...@@ -109,11 +109,11 @@ export class DashboardPage extends React.Component<
} }
} }
mapStatusToStyle = (status: string): BadgeStyle => { mapStatusToBoolean = (status: string): boolean => {
if (status === LAST_RUN_SUCCEEDED) { if (status === LAST_RUN_SUCCEEDED) {
return BadgeStyle.SUCCESS; return true;
} }
return BadgeStyle.DANGER; return false;
}; };
renderTabs() { renderTabs() {
...@@ -304,10 +304,9 @@ export class DashboardPage extends React.Component< ...@@ -304,10 +304,9 @@ export class DashboardPage extends React.Component<
: NO_TIMESTAMP_TEXT} : NO_TIMESTAMP_TEXT}
</time> </time>
<div className="last-run-state"> <div className="last-run-state">
<Flag <ResourceStatusMarker
caseType="sentenceCase" stateText={dashboard.last_run_state}
text={dashboard.last_run_state} succeeded={this.mapStatusToBoolean(
labelStyle={this.mapStatusToStyle(
dashboard.last_run_state dashboard.last_run_state
)} )}
/> />
......
...@@ -18,3 +18,5 @@ export const GITHUB_LINK_TEXT = 'Github'; ...@@ -18,3 +18,5 @@ export const GITHUB_LINK_TEXT = 'Github';
export const EMPTY_TEXT_PREFIX = 'User has no'; export const EMPTY_TEXT_PREFIX = 'User has no';
export const FOOTER_TEXT_PREFIX = 'View all'; export const FOOTER_TEXT_PREFIX = 'View all';
export const NOT_ACTIVE_USER_TEXT = 'Alumni';
...@@ -335,23 +335,6 @@ describe('ProfilePage', () => { ...@@ -335,23 +335,6 @@ describe('ProfilePage', () => {
); );
}); });
it('renders Flag with correct props if user not active', () => {
const userCopy = {
...globalState.user.profile.user,
is_active: false,
};
const { wrapper } = setup({
user: userCopy,
});
expect(
wrapper.find('.header-title-text').find(Flag).props()
).toMatchObject({
caseType: 'sentenceCase',
labelStyle: BadgeStyle.DANGER,
text: 'Alumni',
});
});
it('renders user role', () => { it('renders user role', () => {
expect(wrapper.find('#user-role').text()).toEqual('Tester'); expect(wrapper.find('#user-role').text()).toEqual('Tester');
}); });
...@@ -366,6 +349,18 @@ describe('ProfilePage', () => { ...@@ -366,6 +349,18 @@ describe('ProfilePage', () => {
); );
}); });
it('renders alumni bullet is user not active', () => {
const userCopy = {
...globalState.user.profile.user,
is_active: false,
};
const { wrapper } = setup({
user: userCopy,
});
const expected = 1;
expect(wrapper.find('#alumni').length).toEqual(expected);
});
it('renders github link with correct href', () => { it('renders github link with correct href', () => {
expect(wrapper.find('#github-link').props().href).toEqual( expect(wrapper.find('#github-link').props().href).toEqual(
'https://github.com/githubName' 'https://github.com/githubName'
......
...@@ -43,6 +43,7 @@ import { ...@@ -43,6 +43,7 @@ import {
FOOTER_TEXT_PREFIX, FOOTER_TEXT_PREFIX,
GITHUB_LINK_TEXT, GITHUB_LINK_TEXT,
ITEMS_PER_PAGE, ITEMS_PER_PAGE,
NOT_ACTIVE_USER_TEXT,
OWNED_LABEL, OWNED_LABEL,
OWNED_SOURCE, OWNED_SOURCE,
OWNED_TITLE_PREFIX, OWNED_TITLE_PREFIX,
...@@ -213,16 +214,7 @@ export class ProfilePage extends React.Component< ...@@ -213,16 +214,7 @@ export class ProfilePage extends React.Component<
); );
} else { } else {
userName = ( userName = (
<h1 className="h3 header-title-text truncated"> <h1 className="h3 header-title-text truncated">{user.display_name}</h1>
{user.display_name}
{!user.is_active && (
<Flag
caseType="sentenceCase"
labelStyle={BadgeStyle.DANGER}
text="Alumni"
/>
)}
</h1>
); );
} }
...@@ -238,6 +230,7 @@ export class ProfilePage extends React.Component< ...@@ -238,6 +230,7 @@ export class ProfilePage extends React.Component<
{user.manager_fullname && ( {user.manager_fullname && (
<li id="user-manager">{`Manager: ${user.manager_fullname}`}</li> <li id="user-manager">{`Manager: ${user.manager_fullname}`}</li>
)} )}
{!user.is_active && <li id="alumni">{NOT_ACTIVE_USER_TEXT}</li>}
</ul> </ul>
</div> </div>
); );
......
...@@ -15,6 +15,7 @@ import TableHeaderBullets, { TableHeaderBulletsProps } from '.'; ...@@ -15,6 +15,7 @@ import TableHeaderBullets, { TableHeaderBulletsProps } from '.';
const MOCK_RESOURCE_DISPLAY_NAME = 'Test'; const MOCK_RESOURCE_DISPLAY_NAME = 'Test';
const MOCK_DB_DISPLAY_NAME = 'AlsoTest'; const MOCK_DB_DISPLAY_NAME = 'AlsoTest';
const TABLE_VIEW_TEXT = 'table view';
jest.mock('config/config-utils', () => ({ jest.mock('config/config-utils', () => ({
getDisplayNameByResource: jest.fn(), getDisplayNameByResource: jest.fn(),
...@@ -26,6 +27,7 @@ describe('TableHeaderBullets', () => { ...@@ -26,6 +27,7 @@ describe('TableHeaderBullets', () => {
const props: TableHeaderBulletsProps = { const props: TableHeaderBulletsProps = {
database: 'hive', database: 'hive',
cluster: 'main', cluster: 'main',
isView: true,
...propOverrides, ...propOverrides,
}; };
const wrapper = shallow(<TableHeaderBullets {...props} />); const wrapper = shallow(<TableHeaderBullets {...props} />);
...@@ -71,5 +73,9 @@ describe('TableHeaderBullets', () => { ...@@ -71,5 +73,9 @@ describe('TableHeaderBullets', () => {
it('renders a list with cluster', () => { it('renders a list with cluster', () => {
expect(listElement.find('li').at(2).text()).toEqual(props.cluster); expect(listElement.find('li').at(2).text()).toEqual(props.cluster);
}); });
it('renders a list with table view', () => {
expect(listElement.find('li').at(3).text()).toEqual(TABLE_VIEW_TEXT);
});
}); });
}); });
...@@ -10,20 +10,25 @@ import { ...@@ -10,20 +10,25 @@ import {
import { ResourceType } from 'interfaces/Resources'; import { ResourceType } from 'interfaces/Resources';
import { TABLE_VIEW_TEXT } from './constants';
export interface TableHeaderBulletsProps { export interface TableHeaderBulletsProps {
cluster: string; cluster: string;
database: string; database: string;
isView: boolean;
} }
const TableHeaderBullets: React.FC<TableHeaderBulletsProps> = ({ const TableHeaderBullets: React.FC<TableHeaderBulletsProps> = ({
cluster, cluster,
database, database,
isView,
}: TableHeaderBulletsProps) => { }: TableHeaderBulletsProps) => {
return ( return (
<ul className="header-bullets"> <ul className="header-bullets">
<li>{getDisplayNameByResource(ResourceType.table)}</li> <li>{getDisplayNameByResource(ResourceType.table)}</li>
<li>{getSourceDisplayName(database, ResourceType.table)}</li> <li>{getSourceDisplayName(database, ResourceType.table)}</li>
<li>{cluster}</li> <li>{cluster}</li>
{isView && <li>{TABLE_VIEW_TEXT}</li>}
</ul> </ul>
); );
}; };
...@@ -31,6 +36,7 @@ const TableHeaderBullets: React.FC<TableHeaderBulletsProps> = ({ ...@@ -31,6 +36,7 @@ const TableHeaderBullets: React.FC<TableHeaderBulletsProps> = ({
TableHeaderBullets.defaultProps = { TableHeaderBullets.defaultProps = {
cluster: '', cluster: '',
database: '', database: '',
isView: false,
}; };
export default TableHeaderBullets; export default TableHeaderBullets;
...@@ -27,7 +27,6 @@ import TabsComponent from 'components/common/TabsComponent'; ...@@ -27,7 +27,6 @@ import TabsComponent from 'components/common/TabsComponent';
import TagInput from 'components/common/Tags/TagInput'; import TagInput from 'components/common/Tags/TagInput';
import EditableText from 'components/common/EditableText'; import EditableText from 'components/common/EditableText';
import LoadingSpinner from 'components/common/LoadingSpinner'; import LoadingSpinner from 'components/common/LoadingSpinner';
import Flag from 'components/common/Flag';
import ColumnList from 'components/TableDetail/ColumnList'; import ColumnList from 'components/TableDetail/ColumnList';
import DataPreviewButton from 'components/TableDetail/DataPreviewButton'; import DataPreviewButton from 'components/TableDetail/DataPreviewButton';
...@@ -238,11 +237,9 @@ export class TableDetail extends React.Component< ...@@ -238,11 +237,9 @@ export class TableDetail extends React.Component<
<TableHeaderBullets <TableHeaderBullets
database={data.database} database={data.database}
cluster={data.cluster} cluster={data.cluster}
isView={data.is_view}
/> />
{data.badges.length > 0 && <BadgeList badges={data.badges} />} {data.badges.length > 0 && <BadgeList badges={data.badges} />}
{data.is_view && (
<Flag text="table view" labelStyle={BadgeStyle.WARNING} />
)}
</div> </div>
</div> </div>
<div className="header-section header-links"> <div className="header-section header-links">
......
...@@ -86,9 +86,10 @@ $card-copy-max-lines: 3; ...@@ -86,9 +86,10 @@ $card-copy-max-lines: 3;
@each $line in $shimmer-loader-items { @each $line in $shimmer-loader-items {
.shimmer-row-line--#{$line} { .shimmer-row-line--#{$line} {
width: ( width:
(
random($shimmer-loader-row-max-width - $shimmer-loader-row-min-width) + random($shimmer-loader-row-max-width - $shimmer-loader-row-min-width) +
$shimmer-loader-row-min-width $shimmer-loader-row-min-width
) + ) +
px; px;
} }
......
...@@ -182,38 +182,6 @@ describe('UserListItem', () => { ...@@ -182,38 +182,6 @@ describe('UserListItem', () => {
expect(resourceBadges.exists()).toBe(true); expect(resourceBadges.exists()).toBe(true);
}); });
it('does not render Alumni flag if user is active', () => {
expect(resourceBadges.find(Flag).exists()).toBe(false);
});
it('renders Alumni flag if user not active', () => {
const { wrapper } = setup({
user: {
type: ResourceType.user,
display_name: 'firstname lastname',
email: 'test@test.com',
employee_type: 'fulltime',
first_name: 'firstname',
full_name: 'firstname lastname',
github_username: 'githubName',
is_active: false,
last_name: 'lastname',
manager_fullname: 'Test Manager',
profile_url: 'www.test.com',
role_name: 'Tester',
slack_id: 'www.slack.com',
team_name: 'QA',
user_id: 'test0',
},
});
const flagComponent = wrapper.find('.resource-badges').find(Flag);
expect(flagComponent.exists()).toBe(true);
expect(flagComponent.props()).toMatchObject({
text: 'Alumni',
labelStyle: BadgeStyle.DANGER,
});
});
it('renders correct end icon', () => { it('renders correct end icon', () => {
const expectedClassName = 'icon icon-right'; const expectedClassName = 'icon icon-right';
expect(resourceBadges.find('img').props().className).toEqual( expect(resourceBadges.find('img').props().className).toEqual(
......
...@@ -56,9 +56,6 @@ class UserListItem extends React.Component<UserListItemProps, {}> { ...@@ -56,9 +56,6 @@ class UserListItem extends React.Component<UserListItemProps, {}> {
</div> </div>
<div className="resource-type">User</div> <div className="resource-type">User</div>
<div className="resource-badges"> <div className="resource-badges">
{!user.is_active && (
<Flag text="Alumni" labelStyle={BadgeStyle.DANGER} />
)}
<img className="icon icon-right" alt="" /> <img className="icon icon-right" alt="" />
</div> </div>
</Link> </Link>
......
// Copyright Contributors to the Amundsen project.
// SPDX-License-Identifier: Apache-2.0
import * as React from 'react';
import { mount } from 'enzyme';
import ResourceStatusMarker, { StatusMarkerProps } from '.';
const setup = (propsOverrides?: Partial<StatusMarkerProps>) => {
const props = {
stateText: '',
succeeded: false,
...propsOverrides,
};
const wrapper = mount<typeof ResourceStatusMarker>(
<ResourceStatusMarker {...props} />
);
return { props, wrapper };
};
describe('RunStateContainer', () => {
describe('Succeded', () => {
it('renders SuccessState when lastRunState successful', () => {
const { wrapper } = setup({
stateText: 'Succeeded',
succeeded: true,
});
const expected = 1;
const actual = wrapper.find('.success').length;
expect(actual).toEqual(expected);
});
});
describe('Failed', () => {
const { wrapper } = setup({
stateText: 'Failed',
});
it('renders MissedState when lastRunState failed', () => {
const expected = 1;
const actual = wrapper.find('.failure').length;
expect(actual).toEqual(expected);
});
it('renders failure icon', () => {
const expected = 1;
const actual = wrapper.find('.failure-icon').length;
expect(expected).toEqual(actual);
});
});
});
// Copyright Contributors to the Amundsen project.
// SPDX-License-Identifier: Apache-2.0
import * as React from 'react';
import './styles.scss';
export interface StatusMarkerProps {
stateText: string;
succeeded: boolean;
}
export interface StateProps {
stateText: string;
}
const FailureState: React.FC<StateProps> = ({ stateText }: StateProps) => {
return (
<div className="failure">
<div className="failure-icon">
<div className="exclamation-top" />
<div className="exclamation-bottom" />
</div>
<span className="status-text">{stateText}</span>
</div>
);
};
const SuccessState: React.FC<StateProps> = ({ stateText }: StateProps) => {
return (
<div className="success">
<div className="success-icon">
<span className="icon icon-check" />
</div>
<span className="status-text">{stateText}</span>
</div>
);
};
const ResourceStatusMarker: React.FC<StatusMarkerProps> = ({
stateText,
succeeded,
}: StatusMarkerProps) => {
const state = stateText.charAt(0).toUpperCase() + stateText.slice(1);
if (succeeded) {
return <SuccessState stateText={state} />;
}
return <FailureState stateText={state} />;
};
export default ResourceStatusMarker;
// Copyright Contributors to the Amundsen project.
// SPDX-License-Identifier: Apache-2.0
@import 'variables';
@import 'typography-default';
$icon-status-color: #f2f2f2;
$icon-status-missed: $sunset60;
$icon-status-hit: #00824c;
$icon-size: 14px;
.failure-icon,
.success-icon {
width: $icon-size;
height: $icon-size;
border-radius: 100%;
display: inline-block;
vertical-align: middle;
}
.failure-icon {
background-color: $icon-status-missed;
.exclamation-top {
width: 2px;
height: 4px;
background-color: $icon-status-color;
margin: 4px 6px 3px;
}
.exclamation-bottom {
width: 2px;
height: 2px;
border-radius: 100%;
background-color: $icon-status-color;
margin: -2px 6px;
}
}
.success-icon {
background-color: $icon-status-hit;
.icon-check {
background-color: $icon-status-color;
width: 10px;
height: 10px;
margin-top: -6px;
margin-left: 2px;
min-width: 0;
}
}
.status-text {
@extend %text-body-w2;
display: inline-block;
margin-left: $spacer-1;
vertical-align: middle;
}
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment