Added components to Storybook library (#2768)

* added storybook

* added avatar component

* added button story

* added dropdown button story

* removed unused ellipsis component

* cleaned up modal path

* reorganized enroll secrets table file

* added flash story; removed unused persistent flash

* added fleet ace story

* added checkbox story

* added dropdown story

* added input story

* fixed storybook build

* fixed avatar

* added input with icon story

* added radio button story

* added select targets dropdown story

* added slider story

* added tooltip story

* added info banner story

* removed unused loaders; added spinner story

* added modal story

* removed unused NumberPill

* added pagination story

* lint fixes

* added documentation to run

* modified documentation

* fixed corelayout test

* fixed format for date-fns

* fixed date format that breaks tests

* wait for page
This commit is contained in:
Martavis Parker 2021-11-06 23:41:09 -07:00 committed by GitHub
parent c945485b81
commit bcfac603f0
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
120 changed files with 7423 additions and 4390 deletions

43
.storybook/main.js Normal file
View File

@ -0,0 +1,43 @@
const path = require("path");
const bourbon = require("node-bourbon").includePaths;
const MiniCssExtractPlugin = require("mini-css-extract-plugin");
module.exports = {
webpackFinal: async (config) => {
config.module.rules.push({
test: /\.scss$/,
use: [
{
loader: MiniCssExtractPlugin.loader,
options: {
publicPath: "./",
hmr: process.env.NODE_ENV === "development",
},
},
{ loader: "css-loader" },
{ loader: "postcss-loader" },
{
loader: "sass-loader",
options: {
sourceMap: true,
includePaths: [bourbon],
},
},
{ loader: "import-glob-loader" },
],
});
config.plugins.push(new MiniCssExtractPlugin({ filename: '[name].css' }))
config.resolve.modules.push(path.resolve(__dirname, '../frontend'));
return config;
},
"stories": [
"../frontend/components/**/*.stories.mdx",
"../frontend/components/**/*.stories.@(js|jsx|ts|tsx)"
],
"addons": [
"@storybook/addon-links",
"@storybook/addon-essentials"
]
}

9
.storybook/preview.js Normal file
View File

@ -0,0 +1,9 @@
export const parameters = {
actions: { argTypesRegex: "^on[A-Z].*" },
controls: {
matchers: {
color: /(background|color)$/i,
date: /Date$/,
},
},
}

View File

@ -48,6 +48,7 @@ describe("Teams flow", () => {
// Check team in schedules
cy.visit("/queries/manage");
cy.wait(1000); // eslint-disable-line cypress/no-unnecessary-waiting
cy.findByRole("button", { name: /create new query/i }).click();

View File

@ -4,6 +4,7 @@ The Fleet front-end is a Single Page Application using React with Typescript and
## Table of Contents
- [Running the Fleet web app](#running-the-fleet-web-app)
- [Storybook](#storybook)
- [Directory Structure](#directory-structure)
- [Deprecated](#deprecated)
- [Patterns](#patterns)
@ -19,6 +20,33 @@ The Fleet front-end is a Single Page Application using React with Typescript and
For details instruction on building and serving the Fleet web application
consult the [Contributing documentation](../docs/03-Contributing/README.md).
## Storybook
[Storybook](https://storybook.js.org/) is a tool to document and visualize components, and we
use it to capture our global components used across Fleet. Storybook is key when developing new
features and testing components before release. It runs a separate server exposed on port `6006`.
To run this server, do the following:
- Go to your root fleet project directory
- Run `make deps`
- Run `yarn storybook`
The URL `localhost:6006` should automatically show in your browser. If not, visit it manually.
As mentioned, there are two key times Storybook should be used:
1. When building new features
As we create new features, we re-use Fleet components often. Running Storybook before implementing
new UI elements can clarify if new components need to be created or already exist. This helps us
avoid duplicating code.
2. Testing components
After creating a component, create a new file, `component.stories.tsx`, within its directory. Then,
fill it with the appropriate Storybook code to create a new Storybook entry. You will be able to visualize
the component within Storybook to determine if it looks and behaves as expected.
## Directory Structure
Component directories in the Fleet front-end application encapsulate the entire

View File

@ -0,0 +1,21 @@
import React from "react";
import { Meta } from "@storybook/react";
import { DEFAULT_GRAVATAR_LINK } from "utilities/constants";
import Avatar from ".";
// import "./_styles.scss";
import "../../index.scss";
export default {
component: Avatar,
title: "Components/Avatar",
} as Meta;
export const Default = () => (
<Avatar user={{ gravatarURL: DEFAULT_GRAVATAR_LINK }} />
);
export const Small = () => (
<Avatar user={{ gravatarURL: DEFAULT_GRAVATAR_LINK }} size="small" />
);

View File

@ -5,7 +5,7 @@ interface IAvatarUserInterface {
gravatarURL: string;
}
interface IAvatarInterface {
export interface IAvatarInterface {
className?: string;
size?: string;
user: IAvatarUserInterface;
@ -31,7 +31,7 @@ const Avatar = ({ className, size, user }: IAvatarInterface): JSX.Element => {
const { gravatarURL } = user;
return (
<div className={avatarClasses}>
<div>
<img
alt={!isLoading && !isError ? "User avatar" : ""}
className={`${avatarClasses} ${isLoading || isError ? "default" : ""}`}

View File

@ -1,4 +1,7 @@
.avatar {
@include size(120px);
border: solid 1px $ui-fleet-blue-15;
display: block;
background-size: cover;
border-radius: 50%;

View File

@ -7,8 +7,8 @@ import enrollSecretInterface from "interfaces/enroll_secret";
import InputField from "components/forms/fields/InputField";
import FleetIcon from "components/icons/FleetIcon";
import { stringToClipboard } from "utilities/copy_text";
import EyeIcon from "../../../../assets/images/icon-eye-16x16@2x.png";
import DownloadIcon from "../../../../assets/images/icon-download-12x12@2x.png";
import EyeIcon from "../../../assets/images/icon-eye-16x16@2x.png";
import DownloadIcon from "../../../assets/images/icon-download-12x12@2x.png";
const baseClass = "enroll-secrets";

View File

@ -2,9 +2,7 @@ import React from "react";
import { shallow, mount } from "enzyme";
import * as copy from "utilities/copy_text";
import EnrollSecretTable, {
EnrollSecretRow,
} from "components/config/EnrollSecretTable/EnrollSecretTable";
import EnrollSecretTable, { EnrollSecretRow } from "./EnrollSecretTable";
describe("EnrollSecretTable", () => {
const defaultProps = {

View File

@ -0,0 +1,36 @@
import React from "react";
import { Meta, Story } from "@storybook/react";
import { noop } from "lodash";
import FlashMessage from ".";
import { IFlashMessage } from "./FlashMessage";
import "../../index.scss";
export default {
component: FlashMessage,
title: "Components/FlashMessage",
argTypes: {
fullWidth: {
control: "boolean",
},
isPersistent: {
control: "boolean",
},
},
args: {
fullWidth: true,
isPersistent: true,
notification: {
message: "I am a message. Hear me roar!",
alertType: "success",
isVisible: true,
undoAction: noop,
},
},
} as Meta;
const Template: Story<IFlashMessage> = (props) => <FlashMessage {...props} />;
export const Default = Template.bind({});

View File

@ -7,14 +7,15 @@ import { INotifications } from "interfaces/notification";
import FleetIcon from "components/icons/FleetIcon";
import Button from "components/buttons/Button";
import CloseIcon from "../../../../assets/images/icon-close-white-16x16@2x.png";
import CloseIconBlack from "../../../../assets/images/icon-close-fleet-black-16x16@2x.png";
import CloseIcon from "../../../assets/images/icon-close-white-16x16@2x.png";
import CloseIconBlack from "../../../assets/images/icon-close-fleet-black-16x16@2x.png";
const baseClass = "flash-message";
interface IFlashMessage {
export interface IFlashMessage {
fullWidth: boolean;
notification: INotifications;
isPersistent?: boolean;
onRemoveFlash: () => void;
onUndoActionClick: (
value: () => void
@ -24,6 +25,7 @@ interface IFlashMessage {
const FlashMessage = ({
fullWidth,
notification,
isPersistent,
onRemoveFlash,
onUndoActionClick,
}: IFlashMessage) => {
@ -41,7 +43,7 @@ const FlashMessage = ({
// using this same component instance will be visible).
setHide(false);
if (alertType === "success" && isVisible) {
if (!isPersistent && alertType === "success" && isVisible) {
// After 4 seconds, set hide to true.
const timer = setTimeout(() => {
setHide(true);
@ -56,7 +58,7 @@ const FlashMessage = ({
}, [notification, alertType, isVisible, setHide]);
if (hide || !isVisible) {
return false;
return null;
}
const alertIcon =

View File

@ -0,0 +1,33 @@
import React from "react";
import { Meta, Story } from "@storybook/react";
import { noop } from "lodash";
import FleetAce from ".";
import { IFleetAceProps } from "./FleetAce";
import "../../index.scss";
export default {
component: FleetAce,
title: "Components/FleetAce",
args: {
label: "Type some SQL here...",
value: "SELECT 1 FROM TABLE_NAME;",
readOnly: false,
showGutter: false,
wrapEnabled: false,
fontSize: 16,
name: "",
error: "",
wrapperClassName: "",
hint: "",
labelActionComponent: <></>,
onLoad: noop,
onChange: noop,
handleSubmit: noop,
},
} as Meta;
const Template: Story<IFleetAceProps> = (props) => <FleetAce {...props} />;
export const Default = Template.bind({});

View File

@ -8,12 +8,12 @@ import "ace-builds/src-noconflict/ext-linking";
import "ace-builds/src-noconflict/ext-language_tools";
import { noop } from "lodash";
import Spinner from "components/loaders/Spinner";
import Spinner from "components/Spinner";
import "./mode";
import "./theme";
interface IFleetAceProps {
export interface IFleetAceProps {
error?: string;
fontSize?: number;
label?: string;

View File

@ -0,0 +1,24 @@
import React from "react";
import { Meta, Story } from "@storybook/react";
// @ts-ignore
import IconToolTip from ".";
import { IIconToolTipProps } from "./IconToolTip";
import "../../index.scss";
export default {
component: IconToolTip,
title: "Components/IconToolTip",
args: {
text: "This is a tooltip",
isHtml: false,
issue: false,
},
} as Meta;
const Template: Story<IIconToolTipProps> = (props) => (
<IconToolTip {...props} />
);
export const Default = Template.bind({});

View File

@ -1,7 +1,7 @@
import React from "react";
import ReactTooltip from "react-tooltip";
interface IIconToolTipProps {
export interface IIconToolTipProps {
text: string;
isHtml?: boolean;
issue?: boolean;

View File

@ -0,0 +1,21 @@
import React from "react";
import { Meta, Story } from "@storybook/react";
// @ts-ignore
import InfoBanner from ".";
import { IInfoBannerProps } from "./InfoBanner";
import "../../index.scss";
export default {
component: InfoBanner,
title: "Components/InfoBanner",
} as Meta;
const Template: Story<IInfoBannerProps> = (props) => (
<InfoBanner {...props}>
<div>This is an Info Banner.</div>
</InfoBanner>
);
export const Default = Template.bind({});

View File

@ -3,7 +3,7 @@ import classNames from "classnames";
const baseClass = "info-banner";
interface IInfoBannerProps {
export interface IInfoBannerProps {
children: React.ReactNode;
className?: string;
}

View File

@ -0,0 +1,27 @@
import React from "react";
import { Meta, Story } from "@storybook/react";
import { noop } from "lodash";
// @ts-ignore
import Modal from ".";
import { IModalProps } from "./Modal";
import "../../index.scss";
export default {
component: Modal,
title: "Components/Modal",
args: {
title: "Test modal",
className: "",
onExit: noop,
},
} as Meta;
const Template: Story<IModalProps> = (props) => (
<Modal {...props}>
<div>This is a test description with lots of information.</div>
</Modal>
);
export const Default = Template.bind({});

View File

@ -3,7 +3,7 @@ import classnames from "classnames";
const baseClass = "modal";
interface IModalProps {
export interface IModalProps {
children: JSX.Element;
onExit: () => void;
title: string | JSX.Element;

View File

@ -1,12 +0,0 @@
import React from "react";
import PropTypes from "prop-types";
const NumberPill = ({ number }) => {
return <span className="number-pill">{number}</span>;
};
NumberPill.propTypes = {
number: PropTypes.number,
};
export default NumberPill;

View File

@ -1,14 +0,0 @@
.number-pill {
background-color: $core-vibrant-blue;
border-radius: 14px;
color: $core-white;
display: inline-block;
line-height: 26px;
height: 26px;
text-align: center;
font-size: 15px;
font-weight: 600;
letter-spacing: -0.5px;
padding: 0 14px;
vertical-align: text-bottom;
}

View File

@ -1 +0,0 @@
export { default } from "./NumberPill";

View File

@ -0,0 +1,30 @@
import React from "react";
import { Meta, Story } from "@storybook/react";
import { noop } from "lodash";
// @ts-ignore
import Pagination from ".";
import "../../index.scss";
interface IPaginationProps {
currentPage: number;
resultsPerPage: number;
resultsOnCurrentPage: number;
onPaginationChange: () => void;
}
export default {
component: Pagination,
title: "Components/Pagination",
args: {
currentPage: 1,
resultsPerPage: 10,
resultsOnCurrentPage: 10,
onPaginationChange: noop,
},
} as Meta;
const Template: Story<IPaginationProps> = (props) => <Pagination {...props} />;
export const Default = Template.bind({});

View File

@ -0,0 +1,20 @@
import React from "react";
import { Meta, Story } from "@storybook/react";
// @ts-ignore
import Spinner from ".";
import { ISpinnerProps } from "./Spinner";
import "../../index.scss";
export default {
component: Spinner,
title: "Components/Spinner",
args: {
isInButton: false,
},
} as Meta;
const Template: Story<ISpinnerProps> = (props) => <Spinner {...props} />;
export const Default = Template.bind({});

View File

@ -1,6 +1,6 @@
import React from "react";
interface ISpinnerProps {
export interface ISpinnerProps {
isInButton?: boolean;
}

View File

@ -17,7 +17,7 @@ import sort from "utilities/sort";
import Button from "components/buttons/Button";
// @ts-ignore
import FleetIcon from "components/icons/FleetIcon";
import Spinner from "components/loaders/Spinner";
import Spinner from "components/Spinner";
import { ButtonVariant } from "components/buttons/Button/Button";
import ActionButton, { IActionButtonProps } from "./ActionButton";

View File

@ -0,0 +1,59 @@
import React from "react";
import { Meta, Story } from "@storybook/react";
import { noop } from "lodash";
import Button from ".";
import { IButtonProps } from "./Button";
import "../../../index.scss";
export default {
component: Button,
title: "Components/Button",
argTypes: {
variant: {
options: [
"brand",
"success",
"alert",
"blue-green",
"grey",
"warning",
"link",
"label",
"text-link",
"text-icon",
"inverse",
"inverse-alert",
"block",
"unstyled",
"unstyled-modal-query",
"contextual-nav-item",
"small-text-icon",
],
control: "select",
},
type: {
options: ["button", "submit", "reset"],
control: "select",
},
},
args: {
autofocus: false,
className: "",
size: "",
tabIndex: 0,
title: "",
onClick: noop,
},
} as Meta;
const Template: Story<IButtonProps> = (props) => (
<Button {...props}>Click Here</Button>
);
export const Default = Template.bind({});
Default.args = { variant: "brand", type: "button" };
export const Disabled = Template.bind({});
Disabled.args = { ...Default.args, disabled: true };

View File

@ -17,15 +17,13 @@ export type ButtonVariant =
| "inverse"
| "inverse-alert"
| "block"
| "disabled"
| "unstyled"
| "unstyled-modal-query"
| "contextual-nav-item"
| "small-text-icon";
interface IButtonProps {
export interface IButtonProps {
autofocus?: boolean;
block?: boolean;
children: React.ReactChild;
className?: string;
disabled?: boolean;
@ -48,7 +46,6 @@ interface Inputs {
class Button extends React.Component<IButtonProps, IButtonState> {
static defaultProps = {
block: false,
size: "",
type: "button",
variant: "default",
@ -90,7 +87,6 @@ class Button extends React.Component<IButtonProps, IButtonState> {
render(): JSX.Element {
const { handleClick, setRef } = this;
const {
block,
children,
className,
disabled,
@ -105,7 +101,6 @@ class Button extends React.Component<IButtonProps, IButtonState> {
`${baseClass}--${variant}`,
className,
{
[`${baseClass}--block`]: block,
[`${baseClass}--disabled`]: disabled,
[`${baseClass}--${size}`]: size !== undefined,
}

View File

@ -0,0 +1,107 @@
import React from "react";
import { Meta, Story } from "@storybook/react";
import { noop } from "lodash";
import { DEFAULT_GRAVATAR_LINK } from "utilities/constants";
import Avatar from "components/Avatar"; // @ts-ignore
import DropdownButton from ".";
import "../../../index.scss";
interface IOptions {
disabled: boolean;
label: string;
onClick: (evt: React.MouseEvent<HTMLButtonElement>) => void;
}
interface IDropdownButtonProps {
children: React.ReactChild;
className?: string;
disabled?: boolean;
options: IOptions[];
size?: string;
tabIndex?: number;
type?: string;
variant?: string;
}
const options = [
{
label: "My account",
onClick: noop,
},
{
label: "Documentation",
onClick: () =>
window.open(
"https://github.com/fleetdm/fleet/blob/main/docs/README.md",
"_blank"
),
},
{
label: "Sign out",
onClick: noop,
},
];
export default {
component: DropdownButton,
title: "Components/DropdownButton",
argTypes: {
variant: {
options: [
"brand",
"success",
"alert",
"blue-green",
"grey",
"warning",
"link",
"label",
"text-link",
"text-icon",
"inverse",
"inverse-alert",
"block",
"unstyled",
"unstyled-modal-query",
"contextual-nav-item",
"small-text-icon",
],
control: "select",
},
type: {
options: ["button", "submit", "reset"],
control: "select",
},
},
parameters: {
backgrounds: {
default: "header",
values: [
{
name: "header",
value: "linear-gradient(270deg, #201e43 0%, #353d62 100%)",
},
],
},
},
args: {
variant: "unstyled",
className: "story",
size: "",
tabIndex: 0,
options,
},
} as Meta;
const Template: Story<IDropdownButtonProps> = (props) => (
<DropdownButton {...props}>
<Avatar user={{ gravatarURL: DEFAULT_GRAVATAR_LINK }} size="small" />
</DropdownButton>
);
export const Default = Template.bind({});
export const Disabled = Template.bind({});
Disabled.args = { ...Default.args, disabled: true };

View File

@ -51,6 +51,10 @@
}
}
}
&.story {
padding-left: 100px;
}
}
.downcarat {

View File

@ -1,108 +0,0 @@
import React, { Component } from "react";
import PropTypes from "prop-types";
import { calculateTooltipDirection } from "./helpers";
import ClickOutside from "../../ClickOutside";
const baseClass = "ellipsis-menu";
export class EllipsisMenu extends Component {
static propTypes = {
children: PropTypes.node,
positionStyles: PropTypes.object, // eslint-disable-line react/forbid-prop-types
};
constructor(props) {
super(props);
this.state = {
showChildren: false,
};
}
componentDidMount() {
const { setTooltipDirection } = this;
global.window.addEventListener("resize", setTooltipDirection);
return setTooltipDirection();
}
componentWillUnmount() {
const { setTooltipDirection } = this;
global.window.removeEventListener("resize", setTooltipDirection);
return false;
}
onToggleChildren = () => {
const { showChildren } = this.state;
this.setState({ showChildren: !showChildren });
return false;
};
setDOMNode = (DOMNode) => {
this.DOMNode = DOMNode;
};
setTooltipDirection = () => {
if (this.DOMNode) {
const tooltipDirection = calculateTooltipDirection(this.DOMNode);
this.setState({ tooltipDirection });
}
return false;
};
renderChildren = () => {
const { children } = this.props;
const { showChildren, tooltipDirection } = this.state;
const triangleDirection = tooltipDirection === "left" ? "right" : "left";
if (!showChildren) {
return false;
}
return (
<div
className={`container-triangle ${triangleDirection} ${baseClass}__triangle ${baseClass}__triangle--${tooltipDirection}`}
>
{children}
</div>
);
};
render() {
const { onToggleChildren, renderChildren, setDOMNode } = this;
const { positionStyles } = this.props;
return (
<div ref={setDOMNode} className={baseClass} style={positionStyles}>
<button
onClick={onToggleChildren}
className={`${baseClass}__btn button button--unstyled`}
>
&bull; &bull; &bull;
</button>
{renderChildren()}
</div>
);
}
}
export default ClickOutside(EllipsisMenu, {
getDOMNode: (component) => {
return component.DOMNode;
},
onOutsideClick: (component) => {
return () => {
component.setState({ showChildren: false });
return false;
};
},
});

View File

@ -1,22 +0,0 @@
import React from "react";
import { mount } from "enzyme";
import { EllipsisMenu } from "./EllipsisMenu";
describe("EllipsisMenu - component", () => {
it("Displays children on click", () => {
const component = mount(
<EllipsisMenu>
<span>EllipsisMenu Children</span>
</EllipsisMenu>
);
expect(component.state().showChildren).toEqual(false);
expect(component.text()).not.toContainEqual("EllipsisMenu Children");
component.find("button").simulate("click");
expect(component.state().showChildren).toEqual(true);
expect(component.text()).toContain("EllipsisMenu Children");
});
});

View File

@ -1,26 +0,0 @@
.ellipsis-menu {
user-select: none;
display: inline-block;
position: absolute;
&__btn {
color: $core-fleet-black;
font-size: $medium;
font-weight: $bold;
letter-spacing: -1px;
}
&__triangle {
position: absolute;
top: -22px;
z-index: 1;
&--left {
left: -218px;
}
&--right {
right: -218px;
}
}
}

View File

@ -1,17 +0,0 @@
const TOOLTIP_WIDTH = 300;
const calculateElementDistanceToBrowserRight = (el) => {
const distanceWindowLeftToElementRight = el.getBoundingClientRect().right;
const windowWidth = global.window.innerWidth;
return windowWidth - distanceWindowLeftToElementRight;
};
export const calculateTooltipDirection = (el) => {
const elementDistanceToBrowserRight = calculateElementDistanceToBrowserRight(
el
);
return elementDistanceToBrowserRight < TOOLTIP_WIDTH ? "left" : "right";
};
export default { calculateTooltipDirection };

View File

@ -1,31 +0,0 @@
import { calculateTooltipDirection } from "./helpers";
describe("EllipsisMenu - helpers", () => {
describe("#calculateTooltipDirection", () => {
it('returns "left" if the element does not fit to the right in the browser', () => {
const el = {
getBoundingClientRect: () => {
return {
// test DOM window.innerWidth is 1024px
right: 725,
};
},
};
expect(calculateTooltipDirection(el)).toEqual("left");
});
it('returns "right" if the element fits to the right in the browser', () => {
const el = {
getBoundingClientRect: () => {
return {
// test DOM window.innerWidth is 1024px
right: 724,
};
},
};
expect(calculateTooltipDirection(el)).toEqual("right");
});
});
});

View File

@ -1 +0,0 @@
export { default } from "./EllipsisMenu";

View File

@ -1,25 +0,0 @@
import React from "react";
import PropTypes from "prop-types";
import classnames from "classnames";
import FleetIcon from "components/icons/FleetIcon";
const baseClass = "persistent-flash";
const PersistentFlash = ({ message }) => {
const klass = classnames(baseClass, `${baseClass}--error`);
return (
<div className={klass}>
<div className={`${baseClass}__content`}>
<FleetIcon name="warning-filled" /> <span>{message}</span>
</div>
</div>
);
};
PersistentFlash.propTypes = {
message: PropTypes.string.isRequired,
};
export default PersistentFlash;

View File

@ -1,54 +0,0 @@
.wrapper {
> div {
&.persistent-flash {
height: 50px;
min-height: 50px;
max-height: 50px;
}
}
}
.persistent-flash {
@include position(fixed);
top: 80px;
left: 50%;
transform: translate(-50%, 0);
display: flex;
align-items: center;
justify-content: center;
color: $core-white;
padding: $pad-small $pad-medium;
z-index: 3;
background-color: $ui-warning;
margin: auto;
border: 1px solid $ui-fleet-blue-15;
box-sizing: border-box;
box-shadow: 0px 7px 3px -5px rgba(25, 33, 71, 0.1);
border-radius: 8px;
white-space: nowrap;
a {
font-size: $x-small;
color: $core-vibrant-blue;
font-weight: $bold;
text-decoration: none;
}
&__content {
flex-grow: 1;
text-align: center;
i {
color: $core-fleet-black;
font-size: $small;
}
span {
margin-left: 15px;
margin-right: 15px;
font-size: $x-small;
color: $core-fleet-black;
white-space: nowrap;
}
}
}

View File

@ -1 +0,0 @@
export { default } from "./PersistentFlash";

View File

@ -8,14 +8,14 @@ import Dropdown from "components/forms/fields/Dropdown";
import Form from "components/forms/Form";
import formFieldInterface from "interfaces/form_field";
import enrollSecretInterface from "interfaces/enroll_secret";
import EnrollSecretTable from "components/config/EnrollSecretTable";
import EnrollSecretTable from "components/EnrollSecretTable";
import InputField from "components/forms/fields/InputField";
import OrgLogoIcon from "components/icons/OrgLogoIcon";
import validate from "components/forms/admin/AppConfigForm/validate";
import IconToolTip from "components/IconToolTip";
import InfoBanner from "components/InfoBanner/InfoBanner";
import YamlAce from "components/YamlAce";
import Modal from "components/modals/Modal";
import Modal from "components/Modal";
import OpenNewTabIcon from "../../../../../assets/images/open-new-tab-12x12@2x.png";
const authMethodOptions = [

View File

@ -0,0 +1,27 @@
import React from "react";
import { Meta, Story } from "@storybook/react";
import { noop } from "lodash";
import Checkbox from ".";
import { ICheckboxProps } from "./Checkbox";
import "../../../../index.scss";
export default {
component: Checkbox,
title: "Components/FormFields/Checkbox",
args: {
value: false,
disabled: false,
indeterminate: false,
className: "",
name: "",
wrapperClassName: "",
onChange: noop,
},
} as Meta;
const Template: Story<ICheckboxProps> = (props) => <Checkbox {...props} />;
export const Default = Template.bind({});

View File

@ -7,7 +7,7 @@ import { IFormFieldProps } from "components/forms/FormField/FormField";
const baseClass = "fleet-checkbox";
interface ICheckboxProps {
export interface ICheckboxProps {
children?: JSX.Element | Array<JSX.Element> | string;
className?: string;
disabled?: boolean;

View File

@ -0,0 +1,60 @@
import React from "react";
import { Meta, Story } from "@storybook/react";
import { noop } from "lodash";
import { IDropdownOption } from "interfaces/dropdownOption"; // @ts-ignore
import Dropdown from ".";
import "../../../../index.scss";
interface IDropdownProps {
className?: string;
clearable?: boolean;
searchable?: boolean;
disabled?: boolean;
error?: string;
label?: string | string[];
labelClassName?: string;
multi?: boolean;
name?: string;
options: IDropdownOption[];
placeholder?: string | string[];
value?: string | string[] | number;
wrapperClassName?: string;
onChange: () => void;
onOpen: () => void;
onClose: () => void;
}
const authMethodOptions = [
{ label: "Plain", value: "authmethod_plain" },
{ label: "Cram MD5", value: "authmethod_cram_md5" },
{ label: "Login", value: "authmethod_login" },
];
export default {
component: Dropdown,
title: "Components/FormFields/Dropdown",
args: {
className: "",
clearable: false,
searchable: false,
disabled: false,
error: "",
label: "",
labelClassName: "",
multi: false,
name: "",
options: authMethodOptions,
placeholder: "Choose one...",
value: "",
wrapperClassName: "",
onChange: noop,
onOpen: noop,
onClose: noop,
},
} as Meta;
const Template: Story<IDropdownProps> = (props) => <Dropdown {...props} />;
export const Default = Template.bind({});

View File

@ -0,0 +1,46 @@
import React from "react";
import { Meta, Story } from "@storybook/react";
import { noop } from "lodash";
// @ts-ignore
import InputField from ".";
import "../../../../index.scss";
interface IInputFieldProps {
autofocus?: boolean;
disabled?: boolean;
error?: string;
inputClassName?: string;
inputWrapperClass?: string;
inputOptions?: Record<string, unknown>; // other html input props
name?: string;
placeholder: string;
type?: string;
value: string;
onFocus?: () => void;
onChange?: (value: string) => void;
}
export default {
component: InputField,
title: "Components/FormFields/Input",
args: {
autofocus: false,
disabled: false,
error: "",
inputClassName: "",
inputWrapperClass: "",
inputOptions: "",
name: "",
placeholder: "Type here...",
type: "",
value: "",
onFocus: noop,
onChange: noop,
},
} as Meta;
const Template: Story<IInputFieldProps> = (props) => <InputField {...props} />;
export const Default = Template.bind({});

View File

@ -0,0 +1,145 @@
import React from "react";
import { Meta, Story } from "@storybook/react";
import { noop } from "lodash";
// @ts-ignore
import InputFieldWithIcon from ".";
import "../../../../index.scss";
interface IInputFieldWithIconProps {
autofocus?: boolean;
error?: string;
hint?: string | string[];
iconName?: string;
label?: string;
name?: string;
placeholder?: string;
tabIndex?: number;
type?: string;
className?: string;
disabled?: boolean;
iconPosition?: "start" | "end";
onChange?: () => void;
}
export default {
component: InputFieldWithIcon,
title: "Components/FormFields/InputWithIcon",
argTypes: {
iconPosition: {
options: ["start", "end"],
control: "radio",
},
iconName: {
options: [
"kolide-logo-flat",
"chevrondown",
"chevronleft",
"chevronright",
"chevronup",
"cpu",
"downcarat",
"filter",
"mac",
"memory",
"storage",
"upcarat",
"uptime",
"world",
"osquery",
"join",
"add-button",
"packs",
"help",
"admin",
"config",
"mia",
"success-check",
"offline",
"windows-original",
"centos-original",
"ubuntu-original",
"apple-original",
"search",
"all-hosts",
"alerts",
"logout",
"user-settings",
"clipboard",
"list-select",
"grid-select",
"label",
"docker",
"cloud",
"self-hosted",
"help-solid",
"help-stroke",
"warning-filled",
"delete-cloud",
"pdf",
"credit-card-small",
"billing-card",
"lock-big",
"link-big",
"briefcase",
"name-card",
"kolide-logo",
"business",
"clock",
"host-large",
"single-host",
"username",
"password",
"email",
"hosts",
"query",
"import",
"pencil",
"add-plus",
"x",
"kill-kolide",
"right-arrow",
"camera",
"plus-minus",
"bold-plus",
"linux-original",
"clock2",
"trash",
"laptop-plus",
"wrench-hand",
"external-link",
"fullscreen",
"windowed",
"heroku",
"ubuntu",
"windows",
"centos",
"apple",
"linux",
],
control: "select",
},
},
args: {
autofocus: false,
iconPosition: "start",
iconName: "email",
disabled: false,
label: "Email",
placeholder: "Type here...",
error: "",
hint: "",
name: "",
tabIndex: "",
type: "",
className: "",
onChange: noop,
},
} as Meta;
const Template: Story<IInputFieldWithIconProps> = (props) => (
<InputFieldWithIcon {...props} />
);
export const Default = Template.bind({});

View File

@ -0,0 +1,28 @@
import React from "react";
import { Meta, Story } from "@storybook/react";
import { noop } from "lodash";
// @ts-ignore
import Radio from ".";
import { IRadioProps } from "./Radio";
import "../../../../index.scss";
export default {
component: Radio,
title: "Components/FormFields/Radio",
args: {
checked: true,
disabled: false,
label: "Selected radio",
value: "",
id: "",
name: "",
className: "",
onChange: noop,
},
} as Meta;
const Template: Story<IRadioProps> = (props) => <Radio {...props} />;
export const Default = Template.bind({});

View File

@ -3,7 +3,7 @@ import classnames from "classnames";
const baseClass = "radio";
interface IRadioProps {
export interface IRadioProps {
label: string;
value: string;
id: string;

View File

@ -0,0 +1,40 @@
import React from "react";
import { Meta, Story } from "@storybook/react";
import { noop } from "lodash";
import { ITarget } from "interfaces/target"; // @ts-ignore
import SelectedTargetsDropdown from ".";
import "../../../../index.scss";
interface ISelectedTargetsDropdownProps {
disabled?: boolean;
error?: string;
label?: string;
selectedTargets?: ITarget[];
targetsCount?: number;
queryId?: number;
isPremiumTier?: boolean;
onSelect: () => void;
onFetchTargets?: () => void;
}
export default {
component: SelectedTargetsDropdown,
title: "Components/SelectTargetsDropdown",
args: {
disabled: false,
label: "Select Targets",
selectedTargets: [],
targetsCount: 0,
queryId: 1,
onFetchTargets: noop,
onSelect: noop,
},
} as Meta;
const Template: Story<ISelectedTargetsDropdownProps> = (props) => (
<SelectedTargetsDropdown {...props} />
);
export const Default = Template.bind({});

View File

@ -0,0 +1,30 @@
import React from "react";
import { Meta, Story } from "@storybook/react";
import { noop } from "lodash";
// @ts-ignore
import Slider from ".";
import "../../../../index.scss";
interface ISliderProps {
value: boolean;
inactiveText: string;
activeText: string;
onChange: () => void;
}
export default {
component: Slider,
title: "Components/FormFields/Slider",
args: {
value: false,
inactiveText: "Off",
activeText: "On",
onChange: noop,
},
} as Meta;
const Template: Story<ISliderProps> = (props) => <Slider {...props} />;
export const Default = Template.bind({});

View File

@ -1,9 +0,0 @@
import React from "react";
const baseClass = "kolide-circle-loader";
const Circle = () => {
return <div className={baseClass} />;
};
export default Circle;

View File

@ -1,18 +0,0 @@
$circle-slice: #48c586;
$circle-background: rgba($circle-slice, 0.2);
$circle-size: 60px;
.kolide-circle-loader {
@include size($circle-size);
border-radius: 50%;
border: #{$circle-size / 12} solid $circle-background;
border-left-color: $circle-slice;
transform: rotate(0deg);
animation: spin-circle 1s linear infinite;
}
@keyframes spin-circle {
100% {
transform: rotate(360deg);
}
}

View File

@ -1 +0,0 @@
export { default } from "./Circle";

View File

@ -1,37 +0,0 @@
import React, { Component } from "react";
import PropTypes from "prop-types";
import classnames from "classnames";
import { round } from "lodash";
const baseClass = "progress-bar";
class ProgressBar extends Component {
static propTypes = {
className: PropTypes.string,
error: PropTypes.number.isRequired,
max: PropTypes.number.isRequired,
success: PropTypes.number.isRequired,
};
render() {
const { className, error, max, success } = this.props;
const successPercentComplete = `${round((success / (max || 1)) * 100, 0)}%`;
const errorPercentComplete = `${round((error / (max || 1)) * 100, 0)}%`;
const wrapperClassName = classnames(baseClass, className);
return (
<div className={wrapperClassName}>
<div
className={`${baseClass}__progress ${baseClass}__progress--success`}
style={{ width: successPercentComplete }}
/>
<div
className={`${baseClass}__progress ${baseClass}__progress--error`}
style={{ width: errorPercentComplete }}
/>
</div>
);
}
}
export default ProgressBar;

View File

@ -1,22 +0,0 @@
.progress-bar {
display: flex;
background-color: $ui-fleet-black-25;
height: 10px;
overflow: hidden;
border-radius: 2px;
margin-top: $pad-xsmall;
&__progress {
height: 100%;
overflow: hidden;
transition: width 300ms ease-in;
&--error {
background-color: $ui-error;
}
&--success {
background-color: $ui-success;
}
}
}

View File

@ -1 +0,0 @@
export { default } from "./ProgressBar";

View File

@ -1,22 +0,0 @@
import React, { Component } from "react";
import PropTypes from "prop-types";
import { convertSeconds } from "./helpers";
const baseClass = "kolide-timer";
class Timer extends Component {
static propTypes = {
totalMilliseconds: PropTypes.number,
};
render() {
const { totalMilliseconds } = this.props;
return (
<span className={baseClass}>{convertSeconds(totalMilliseconds)}</span>
);
}
}
export default Timer;

View File

@ -1,23 +0,0 @@
import React from "react";
import { mount } from "enzyme";
import Timer from "./Timer";
describe("Timer - component", () => {
it("renders with proper time", () => {
const timer1 = mount(<Timer totalMilliseconds={1000} />);
const elem1 = timer1.find(".kolide-timer");
expect(elem1.text()).toEqual("00:00:01");
const timer2 = mount(<Timer totalMilliseconds={60000} />);
const elem2 = timer2.find(".kolide-timer");
expect(elem2.text()).toEqual("00:01:00");
const timer3 = mount(<Timer totalMilliseconds={3600000} />);
const elem3 = timer3.find(".kolide-timer");
expect(elem3.text()).toEqual("01:00:00");
});
});

View File

@ -1,4 +0,0 @@
.kolide-timer {
font-size: $x-small;
color: $core-fleet-black;
}

View File

@ -1,7 +0,0 @@
import moment from "moment";
export const convertSeconds = (totalMilliSeconds) => {
return moment.utc(totalMilliSeconds).format("HH:mm:ss");
};
export default { convertSeconds };

View File

@ -1,10 +0,0 @@
import { convertSeconds } from "./helpers";
describe("Timer convertSeconds helper", () => {
it("converts seconds to hh:mm:ss", () => {
expect(convertSeconds(3000)).toEqual("00:00:03");
expect(convertSeconds(30000)).toEqual("00:00:30");
expect(convertSeconds(300000)).toEqual("00:05:00");
expect(convertSeconds(0)).toEqual("00:00:00");
});
});

View File

@ -1 +0,0 @@
export { default } from "./Timer";

View File

@ -1,6 +1,6 @@
import React from "react";
import Modal from "components/modals/Modal";
import Modal from "components/Modal";
import Button from "components/buttons/Button";
const baseClass = "remove-query-modal";

View File

@ -9,8 +9,7 @@ import { isEqual } from "lodash";
import permissionUtils from "utilities/permissions";
import configInterface from "interfaces/config";
import FlashMessage from "components/flash_messages/FlashMessage";
import PersistentFlash from "components/flash_messages/PersistentFlash";
import FlashMessage from "components/FlashMessage";
import SiteTopNav from "components/side_panels/SiteTopNav";
import userInterface from "interfaces/user";
import notificationInterface from "interfaces/notification";
@ -40,10 +39,6 @@ export class CoreLayout extends Component {
user: userInterface,
fullWidthFlash: PropTypes.bool,
notifications: notificationInterface,
persistentFlash: PropTypes.shape({
showFlash: PropTypes.bool.isRequired,
message: PropTypes.string.isRequired,
}).isRequired,
isPremiumTier: PropTypes.bool,
};
@ -141,7 +136,6 @@ export class CoreLayout extends Component {
notifications,
children,
config,
persistentFlash,
user,
isPremiumTier,
} = this.props;
@ -175,9 +169,6 @@ export class CoreLayout extends Component {
/>
</nav>
<div className="core-wrapper">
{persistentFlash.showFlash && (
<PersistentFlash message={persistentFlash.message} />
)}
{isPremiumTier && showExpirationFlashMessage && (
<FlashMessage
fullWidth={fullWidthFlash}
@ -203,7 +194,6 @@ const mapStateToProps = (state) => {
app: { config },
auth: { user },
notifications,
persistentFlash,
} = state;
const isPremiumTier = permissionUtils.isPremiumTier(state.app.config);
@ -214,7 +204,6 @@ const mapStateToProps = (state) => {
config,
fullWidthFlash,
notifications,
persistentFlash,
user,
isPremiumTier,
};

View File

@ -29,10 +29,6 @@ describe("CoreLayout - layouts", () => {
isVisible: true,
message: "nice jerb!",
},
persistentFlash: {
showFlash: false,
message: "",
},
};
const mockStoreWithNotifications = reduxMockStore(storeWithNotifications);
const componentWithFlash = connectedComponent(CoreLayout, {
@ -51,28 +47,4 @@ describe("CoreLayout - layouts", () => {
expect(appWithFlash.find("FlashMessage").html()).toBeTruthy();
expect(appWithoutFlash.find("FlashMessage").html()).toBeFalsy();
});
it("renders the PersistentFlash component when showFlash is true", () => {
const storeWithPersistentFlash = {
...store,
persistentFlash: {
showFlash: true,
message: "This is the flash message",
},
};
const mockStoreWithPersistentFlash = reduxMockStore(
storeWithPersistentFlash
);
const Layout = connectedComponent(CoreLayout, {
mockStore: mockStoreWithPersistentFlash,
});
const MountedLayout = mount(Layout);
expect(MountedLayout.find("PersistentFlash").length).toEqual(
1,
"Expected the Persistent Flash to be on the page"
);
});
});

View File

@ -11,7 +11,7 @@ import { IActivity, ActivityType } from "interfaces/activity";
import Avatar from "components/Avatar";
import Button from "components/buttons/Button";
import Spinner from "components/loaders/Spinner"; // @ts-ignore
import Spinner from "components/Spinner"; // @ts-ignore
import FleetIcon from "components/icons/FleetIcon";
import ErrorIcon from "../../../../../assets/images/icon-error-16x16@2x.png";

View File

@ -5,7 +5,7 @@ import { Tab, Tabs, TabList, TabPanel } from "react-tabs";
import softwareAPI from "services/entities/software";
import { ISoftware } from "interfaces/software";
import Modal from "components/modals/Modal";
import Modal from "components/Modal";
import TabsWrapper from "components/TabsWrapper";
import TableContainer from "components/TableContainer";

View File

@ -10,9 +10,9 @@ import { IHostPolicy } from "interfaces/host_policy";
import hostAPI from "services/entities/hosts"; // @ts-ignore
import { renderFlash } from "redux/nodes/notifications/actions";
import Spinner from "components/loaders/Spinner";
import Spinner from "components/Spinner";
import Button from "components/buttons/Button";
import Modal from "components/modals/Modal";
import Modal from "components/Modal";
import LaptopMac from "../../../../../assets/images/laptop-mac.png";
import LinkArrow from "../../../../../assets/images/icon-arrow-right-vibrant-blue-10x18@2x.png";
import IconDisabled from "../../../../../assets/images/icon-action-disable-red-16x16@2x.png";

View File

@ -17,7 +17,7 @@ import permissionUtils from "utilities/permissions";
import FleetIcon from "components/icons/FleetIcon";
import InputField from "components/forms/fields/InputField";
import { logoutUser, updateUser } from "redux/nodes/auth/actions";
import Modal from "components/modals/Modal";
import Modal from "components/Modal";
import configInterface from "interfaces/config";
import versionInterface from "interfaces/version";
import { renderFlash } from "redux/nodes/notifications/actions";

View File

@ -56,10 +56,7 @@
}
&__avatar {
@include size(120px);
border: solid 1px $ui-fleet-blue-15;
margin: 0 auto;
display: block;
}
&__more-info-detail {

View File

@ -3,7 +3,7 @@ import React, { useCallback, useState } from "react";
import { INewMembersBody, ITeam } from "interfaces/team";
import { IUser } from "interfaces/user";
import endpoints from "fleet/endpoints";
import Modal from "components/modals/Modal";
import Modal from "components/Modal";
import Button from "components/buttons/Button";
import AutocompleteDropdown from "pages/admin/TeamManagementPage/TeamDetailsWrapper/MembersPagePage/components/AutocompleteDropdown";
import { IDropdownOption } from "interfaces/dropdownOption";

View File

@ -1,6 +1,6 @@
import React from "react";
import Modal from "components/modals/Modal";
import Modal from "components/Modal";
import Button from "components/buttons/Button";
const baseClass = "remove-member-modal";

View File

@ -14,7 +14,7 @@ import { AppContext } from "context/app";
// @ts-ignore
import { renderFlash } from "redux/nodes/notifications/actions";
import teamActions from "redux/nodes/entities/teams/actions";
import Spinner from "components/loaders/Spinner";
import Spinner from "components/Spinner";
import Button from "components/buttons/Button";
import TabsWrapper from "components/TabsWrapper";
import TeamsDropdown from "components/TeamsDropdown";

View File

@ -1,6 +1,6 @@
import React from "react";
import Button from "components/buttons/Button";
import Modal from "components/modals/Modal";
import Modal from "components/Modal";
interface IAddHostsModalProps {
onCancel: () => void;

View File

@ -1,6 +1,6 @@
import React, { useState, useCallback } from "react";
import Modal from "components/modals/Modal";
import Modal from "components/Modal";
import Button from "components/buttons/Button";
import InfoBanner from "components/InfoBanner/InfoBanner";
// @ts-ignore

View File

@ -1,6 +1,6 @@
import React from "react";
import Modal from "components/modals/Modal";
import Modal from "components/Modal";
import Button from "components/buttons/Button";
const baseClass = "delete-team-modal";

View File

@ -1,6 +1,6 @@
import React, { useState, useCallback } from "react";
import Modal from "components/modals/Modal";
import Modal from "components/Modal";
// @ts-ignore
import InputFieldWithIcon from "components/forms/fields/InputFieldWithIcon";
import Button from "components/buttons/Button";

View File

@ -20,7 +20,7 @@ import teamActions from "redux/nodes/entities/teams/actions";
import TableContainer from "components/TableContainer";
import TableDataError from "components/TableDataError";
import Modal from "components/modals/Modal";
import Modal from "components/Modal";
import { DEFAULT_CREATE_USER_ERRORS } from "utilities/constants";
import EmptyUsers from "./components/EmptyUsers";
import { generateTableHeaders, combineDataSets } from "./UsersTableConfig";

View File

@ -2,8 +2,8 @@ import React, { useState } from "react";
import { ITeam } from "interfaces/team";
import { IUserFormErrors } from "interfaces/user";
import Modal from "components/modals/Modal";
import Spinner from "components/loaders/Spinner";
import Modal from "components/Modal";
import Spinner from "components/Spinner";
import UserForm from "../UserForm";
import { IFormData } from "../UserForm/UserForm";

View File

@ -2,7 +2,7 @@ import React from "react";
import { ITeam } from "interfaces/team";
import { IUserFormErrors } from "interfaces/user";
import Modal from "components/modals/Modal";
import Modal from "components/Modal";
import UserForm from "../UserForm";
import { IFormData } from "../UserForm/UserForm";

View File

@ -1,6 +1,6 @@
import React from "react";
import { IUser } from "interfaces/user";
import Modal from "components/modals/Modal";
import Modal from "components/Modal";
import Button from "components/buttons/Button";
const baseClass = "reset-password-modal";

View File

@ -1,6 +1,6 @@
import React from "react";
import { IUser } from "interfaces/user";
import Modal from "components/modals/Modal";
import Modal from "components/Modal";
import Button from "components/buttons/Button";
const baseClass = "reset-sessions-modal";

View File

@ -23,9 +23,9 @@ import { renderFlash } from "redux/nodes/notifications/actions";
import permissionUtils from "utilities/permissions";
import ReactTooltip from "react-tooltip";
import Spinner from "components/loaders/Spinner";
import Spinner from "components/Spinner";
import Button from "components/buttons/Button";
import Modal from "components/modals/Modal";
import Modal from "components/Modal";
import SoftwareVulnerabilities from "pages/hosts/HostDetailsPage/SoftwareVulnCount";
import TableContainer from "components/TableContainer";
import InfoBanner from "components/InfoBanner";

View File

@ -1,6 +1,6 @@
import React from "react";
import Button from "components/buttons/Button";
import Modal from "components/modals/Modal";
import Modal from "components/Modal";
import { IHostPolicy } from "interfaces/host_policy";

View File

@ -9,7 +9,7 @@ import queryInterface from "interfaces/query";
import hostInterface from "interfaces/host";
import Button from "components/buttons/Button";
import Modal from "components/modals/Modal";
import Modal from "components/Modal";
import InputField from "components/forms/fields/InputField";
import OpenNewTabIcon from "../../../../../assets/images/open-new-tab-12x12@2x.png";

View File

@ -1,7 +1,7 @@
import React, { useCallback, useState } from "react";
import { Link } from "react-router";
import PATHS from "router/paths";
import Modal from "components/modals/Modal";
import Modal from "components/Modal";
import Button from "components/buttons/Button";
// ignore TS error for now until these are rewritten in ts.
// @ts-ignore

View File

@ -40,7 +40,7 @@ import Button from "components/buttons/Button"; // @ts-ignore
import Dropdown from "components/forms/fields/Dropdown";
import HostSidePanel from "components/side_panels/HostSidePanel"; // @ts-ignore
import LabelForm from "components/forms/LabelForm";
import Modal from "components/modals/Modal";
import Modal from "components/Modal";
import QuerySidePanel from "components/side_panels/QuerySidePanel";
import TableContainer from "components/TableContainer";
import TableDataError from "components/TableDataError";

View File

@ -8,7 +8,7 @@ import configInterface from "interfaces/config";
import teamInterface from "interfaces/team";
import userInterface from "interfaces/user";
import permissionUtils from "utilities/permissions";
import EnrollSecretTable from "components/config/EnrollSecretTable";
import EnrollSecretTable from "components/EnrollSecretTable";
import FleetIcon from "components/icons/FleetIcon";
import Dropdown from "components/forms/fields/Dropdown";
import InputField from "components/forms/fields/InputField";

View File

@ -1,6 +1,6 @@
import React from "react";
import Modal from "components/modals/Modal";
import Modal from "components/Modal";
import Button from "components/buttons/Button";
const baseClass = "delete-host-modal";

View File

@ -1,9 +1,9 @@
import React from "react";
import { useSelector } from "react-redux";
import Modal from "components/modals/Modal";
import Modal from "components/Modal";
import Button from "components/buttons/Button";
// @ts-ignore
import EnrollSecretTable from "components/config/EnrollSecretTable";
import EnrollSecretTable from "components/EnrollSecretTable";
import { ITeam } from "interfaces/team";
import { IEnrollSecret } from "interfaces/enroll_secret";

View File

@ -1,7 +1,7 @@
import React, { useState } from "react";
import Button from "components/buttons/Button";
import Modal from "components/modals/Modal";
import Modal from "components/Modal";
import { ITeam } from "interfaces/team";
import { IEnrollSecret } from "interfaces/enroll_secret";
import PlatformWrapper from "./PlatformWrapper/PlatformWrapper";

View File

@ -1,7 +1,7 @@
import React, { useCallback, useState } from "react";
import { Link } from "react-router";
import PATHS from "router/paths";
import Modal from "components/modals/Modal";
import Modal from "components/Modal";
import Button from "components/buttons/Button";
// ignore TS error for now until these are rewritten in ts.
// @ts-ignore

View File

@ -4,7 +4,7 @@ import React, { useState, useEffect } from "react";
// @ts-ignore
import Fleet from "fleet";
import { pull } from "lodash";
import Modal from "components/modals/Modal";
import Modal from "components/Modal";
import Button from "components/buttons/Button";
// @ts-ignore
import Dropdown from "components/forms/fields/Dropdown";

Some files were not shown because too many files have changed in this diff Show More