Arvados-DCO-1.1-Signed-off-by: Lucas Di Pentima <ldipentima@veritasgenetics.com>
import { Button, Grid, StyleRulesCallback, WithStyles, Typography, Tooltip } from '@material-ui/core';
import ChevronRightIcon from '@material-ui/icons/ChevronRight';
import { withStyles } from '@material-ui/core';
+import { IllegalNamingWarning } from '../warning/warning';
export interface Breadcrumb {
label: string;
{
items.map((item, index) => {
const isLastItem = index === items.length - 1;
+ const isFirstItem = index === 0;
return (
<React.Fragment key={index}>
+ {isFirstItem ? null : <IllegalNamingWarning name={item.label} />}
<Tooltip title={item.label}>
<Button
color="inherit"
--- /dev/null
+// Copyright (C) The Arvados Authors. All rights reserved.
+//
+// SPDX-License-Identifier: AGPL-3.0
+
+import * as React from "react";
+import { WarningIcon } from "~/components/icon/icon";
+import { Tooltip } from "@material-ui/core";
+
+interface WarningComponentProps {
+ text: string;
+ rules: RegExp[];
+ message: string;
+}
+
+export const WarningComponent = ({ text, rules, message }: WarningComponentProps) =>
+ rules.find(aRule => text.match(aRule) !== null)
+ ? message
+ ? <Tooltip title={message}><WarningIcon /></Tooltip>
+ : <WarningIcon />
+ : null;
+
+interface IllegalNamingWarningProps {
+ name: string;
+}
+
+export const IllegalNamingWarning = ({ name }: IllegalNamingWarningProps) =>
+ <WarningComponent
+ text={name} rules={[/\//, /^\.{1,2}$/]}
+ message="Names being '.', '..' or including '/' cause issues with WebDAV, please edit it to something different." />;
\ No newline at end of file
+++ /dev/null
-// Copyright (C) The Arvados Authors. All rights reserved.
-//
-// SPDX-License-Identifier: AGPL-3.0
-
-import * as React from 'react';
-import * as Enzyme from 'enzyme';
-import { mount } from 'enzyme';
-import * as Adapter from 'enzyme-adapter-react-16';
-import ListItemIcon from '@material-ui/core/ListItemIcon';
-import { Collapse } from '@material-ui/core';
-import CircularProgress from '@material-ui/core/CircularProgress';
-
-import { ProjectTree } from './project-tree';
-import { TreeItem, TreeItemStatus } from '../../components/tree/tree';
-import { ProjectResource } from '../../models/project';
-import { mockProjectResource } from '../../models/test-utils';
-
-Enzyme.configure({ adapter: new Adapter() });
-
-describe("ProjectTree component", () => {
-
- it("should render ListItemIcon", () => {
- const project: TreeItem<ProjectResource> = {
- data: mockProjectResource(),
- id: "3",
- open: true,
- active: true,
- status: TreeItemStatus.PENDING
- };
- const wrapper = mount(<ProjectTree
- projects={[project]}
- toggleOpen={jest.fn()}
- toggleActive={jest.fn()}
- onContextMenu={jest.fn()} />);
-
- expect(wrapper.find(ListItemIcon)).toHaveLength(2);
- });
-
- it("should render Collapse", () => {
- const project: Array<TreeItem<ProjectResource>> = [
- {
- data: mockProjectResource(),
- id: "3",
- open: true,
- active: true,
- status: TreeItemStatus.LOADED,
- items: [
- {
- data: mockProjectResource(),
- id: "3",
- open: true,
- active: true,
- status: TreeItemStatus.PENDING
- }
- ]
- }
- ];
- const wrapper = mount(<ProjectTree
- projects={project}
- toggleOpen={jest.fn()}
- toggleActive={jest.fn()}
- onContextMenu={jest.fn()} />);
-
- expect(wrapper.find(Collapse)).toHaveLength(1);
- });
-
- it("should render CircularProgress", () => {
- const project: TreeItem<ProjectResource> = {
- data: mockProjectResource(),
- id: "3",
- open: false,
- active: true,
- status: TreeItemStatus.PENDING
- };
- const wrapper = mount(<ProjectTree
- projects={[project]}
- toggleOpen={jest.fn()}
- toggleActive={jest.fn()}
- onContextMenu={jest.fn()} />);
-
- expect(wrapper.find(CircularProgress)).toHaveLength(1);
- });
-});
+++ /dev/null
-// Copyright (C) The Arvados Authors. All rights reserved.
-//
-// SPDX-License-Identifier: AGPL-3.0
-
-import * as React from 'react';
-import { ReactElement } from 'react';
-import { StyleRulesCallback, WithStyles, withStyles } from '@material-ui/core/styles';
-import { Tree, TreeItem } from '~/components/tree/tree';
-import { ProjectResource } from '~/models/project';
-import { ProjectIcon } from '~/components/icon/icon';
-import { ArvadosTheme } from '~/common/custom-theme';
-import { ListItemTextIcon } from '~/components/list-item-text-icon/list-item-text-icon';
-
-type CssRules = 'root';
-
-const styles: StyleRulesCallback<CssRules> = (theme: ArvadosTheme) => ({
- root: {
- marginLeft: `${theme.spacing.unit * 1.5}px`,
- }
-});
-
-export interface ProjectTreeProps<T> {
- projects: Array<TreeItem<ProjectResource>>;
- toggleOpen: (event: React.MouseEvent<HTMLElement>, item: TreeItem<T>) => void;
- toggleActive: (event: React.MouseEvent<HTMLElement>, item: TreeItem<T>) => void;
- onContextMenu: (event: React.MouseEvent<HTMLElement>, item: TreeItem<ProjectResource>) => void;
-}
-
-export const ProjectTree = withStyles(styles)(
- class ProjectTreeGeneric<T> extends React.Component<ProjectTreeProps<T> & WithStyles<CssRules>> {
- render(): ReactElement<any> {
- const { classes, projects, toggleOpen, toggleActive, onContextMenu } = this.props;
- return (
- <div className={classes.root}>
- <Tree items={projects}
- onContextMenu={onContextMenu}
- toggleItemOpen={toggleOpen}
- toggleItemActive={toggleActive}
- render={
- (project: TreeItem<ProjectResource>) =>
- <ListItemTextIcon
- icon={ProjectIcon}
- name={project.data.name}
- isActive={project.active}
- hasMargin={true} />
- } />
- </div>
- );
- }
- }
-);
import { openDetailsPanel } from '~/store/details-panel/details-panel-action';
import { snackbarActions, SnackbarKind } from '~/store/snackbar/snackbar-actions';
import { PropertyChipComponent } from '~/views-components/resource-properties-form/property-chip';
+import { IllegalNamingWarning } from '~/components/warning/warning';
type CssRules = 'card' | 'iconHeader' | 'tag' | 'label' | 'value' | 'link';
</IconButton>
</Tooltip>
}
- title={item && item.name}
+ title={item && <span><IllegalNamingWarning name={item.name}/>{item.name}</span>}
titleTypographyProps={this.titleProps}
subheader={item && item.description}
subheaderTypographyProps={this.titleProps} />