mirror of
https://github.com/empayre/fleet.git
synced 2024-11-06 00:45:19 +00:00
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:
parent
c945485b81
commit
bcfac603f0
43
.storybook/main.js
Normal file
43
.storybook/main.js
Normal 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
9
.storybook/preview.js
Normal file
@ -0,0 +1,9 @@
|
||||
export const parameters = {
|
||||
actions: { argTypesRegex: "^on[A-Z].*" },
|
||||
controls: {
|
||||
matchers: {
|
||||
color: /(background|color)$/i,
|
||||
date: /Date$/,
|
||||
},
|
||||
},
|
||||
}
|
@ -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();
|
||||
|
||||
|
@ -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
|
||||
|
21
frontend/components/Avatar/Avatar.stories.tsx
Normal file
21
frontend/components/Avatar/Avatar.stories.tsx
Normal 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" />
|
||||
);
|
@ -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" : ""}`}
|
||||
|
@ -1,4 +1,7 @@
|
||||
.avatar {
|
||||
@include size(120px);
|
||||
border: solid 1px $ui-fleet-blue-15;
|
||||
display: block;
|
||||
background-size: cover;
|
||||
border-radius: 50%;
|
||||
|
||||
|
@ -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";
|
||||
|
@ -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 = {
|
36
frontend/components/FlashMessage/FlashMessage.stories.tsx
Normal file
36
frontend/components/FlashMessage/FlashMessage.stories.tsx
Normal 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({});
|
@ -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 =
|
33
frontend/components/FleetAce/FleetAce.stories.tsx
Normal file
33
frontend/components/FleetAce/FleetAce.stories.tsx
Normal 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({});
|
@ -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;
|
||||
|
24
frontend/components/IconToolTip/IconToolTip.stories.tsx
Normal file
24
frontend/components/IconToolTip/IconToolTip.stories.tsx
Normal 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({});
|
@ -1,7 +1,7 @@
|
||||
import React from "react";
|
||||
import ReactTooltip from "react-tooltip";
|
||||
|
||||
interface IIconToolTipProps {
|
||||
export interface IIconToolTipProps {
|
||||
text: string;
|
||||
isHtml?: boolean;
|
||||
issue?: boolean;
|
||||
|
21
frontend/components/InfoBanner/InfoBanner.stories.tsx
Normal file
21
frontend/components/InfoBanner/InfoBanner.stories.tsx
Normal 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({});
|
@ -3,7 +3,7 @@ import classNames from "classnames";
|
||||
|
||||
const baseClass = "info-banner";
|
||||
|
||||
interface IInfoBannerProps {
|
||||
export interface IInfoBannerProps {
|
||||
children: React.ReactNode;
|
||||
className?: string;
|
||||
}
|
||||
|
27
frontend/components/Modal/Modal.stories.tsx
Normal file
27
frontend/components/Modal/Modal.stories.tsx
Normal 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({});
|
@ -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;
|
@ -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;
|
@ -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;
|
||||
}
|
@ -1 +0,0 @@
|
||||
export { default } from "./NumberPill";
|
30
frontend/components/Pagination/Pagination.stories.tsx
Normal file
30
frontend/components/Pagination/Pagination.stories.tsx
Normal 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({});
|
20
frontend/components/Spinner/Spinner.stories.tsx
Normal file
20
frontend/components/Spinner/Spinner.stories.tsx
Normal 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({});
|
@ -1,6 +1,6 @@
|
||||
import React from "react";
|
||||
|
||||
interface ISpinnerProps {
|
||||
export interface ISpinnerProps {
|
||||
isInButton?: boolean;
|
||||
}
|
||||
|
@ -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";
|
||||
|
||||
|
59
frontend/components/buttons/Button/Button.stories.tsx
Normal file
59
frontend/components/buttons/Button/Button.stories.tsx
Normal 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 };
|
@ -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,
|
||||
}
|
||||
|
@ -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 };
|
@ -51,6 +51,10 @@
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
&.story {
|
||||
padding-left: 100px;
|
||||
}
|
||||
}
|
||||
|
||||
.downcarat {
|
||||
|
@ -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`}
|
||||
>
|
||||
• • •
|
||||
</button>
|
||||
{renderChildren()}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
export default ClickOutside(EllipsisMenu, {
|
||||
getDOMNode: (component) => {
|
||||
return component.DOMNode;
|
||||
},
|
||||
onOutsideClick: (component) => {
|
||||
return () => {
|
||||
component.setState({ showChildren: false });
|
||||
|
||||
return false;
|
||||
};
|
||||
},
|
||||
});
|
@ -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");
|
||||
});
|
||||
});
|
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
@ -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 };
|
@ -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");
|
||||
});
|
||||
});
|
||||
});
|
@ -1 +0,0 @@
|
||||
export { default } from "./EllipsisMenu";
|
@ -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;
|
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
@ -1 +0,0 @@
|
||||
export { default } from "./PersistentFlash";
|
@ -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 = [
|
||||
|
@ -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({});
|
@ -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;
|
||||
|
@ -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({});
|
@ -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({});
|
@ -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({});
|
28
frontend/components/forms/fields/Radio/Radio.stories.tsx
Normal file
28
frontend/components/forms/fields/Radio/Radio.stories.tsx
Normal 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({});
|
@ -3,7 +3,7 @@ import classnames from "classnames";
|
||||
|
||||
const baseClass = "radio";
|
||||
|
||||
interface IRadioProps {
|
||||
export interface IRadioProps {
|
||||
label: string;
|
||||
value: string;
|
||||
id: string;
|
||||
|
@ -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({});
|
30
frontend/components/forms/fields/Slider/Slider.stories.tsx
Normal file
30
frontend/components/forms/fields/Slider/Slider.stories.tsx
Normal 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({});
|
@ -1,9 +0,0 @@
|
||||
import React from "react";
|
||||
|
||||
const baseClass = "kolide-circle-loader";
|
||||
|
||||
const Circle = () => {
|
||||
return <div className={baseClass} />;
|
||||
};
|
||||
|
||||
export default Circle;
|
@ -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);
|
||||
}
|
||||
}
|
@ -1 +0,0 @@
|
||||
export { default } from "./Circle";
|
@ -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;
|
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
@ -1 +0,0 @@
|
||||
export { default } from "./ProgressBar";
|
@ -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;
|
@ -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");
|
||||
});
|
||||
});
|
@ -1,4 +0,0 @@
|
||||
.kolide-timer {
|
||||
font-size: $x-small;
|
||||
color: $core-fleet-black;
|
||||
}
|
@ -1,7 +0,0 @@
|
||||
import moment from "moment";
|
||||
|
||||
export const convertSeconds = (totalMilliSeconds) => {
|
||||
return moment.utc(totalMilliSeconds).format("HH:mm:ss");
|
||||
};
|
||||
|
||||
export default { convertSeconds };
|
@ -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");
|
||||
});
|
||||
});
|
@ -1 +0,0 @@
|
||||
export { default } from "./Timer";
|
@ -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";
|
||||
|
@ -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,
|
||||
};
|
||||
|
@ -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"
|
||||
);
|
||||
});
|
||||
});
|
||||
|
@ -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";
|
||||
|
@ -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";
|
||||
|
||||
|
@ -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";
|
||||
|
@ -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";
|
||||
|
@ -56,10 +56,7 @@
|
||||
}
|
||||
|
||||
&__avatar {
|
||||
@include size(120px);
|
||||
border: solid 1px $ui-fleet-blue-15;
|
||||
margin: 0 auto;
|
||||
display: block;
|
||||
}
|
||||
|
||||
&__more-info-detail {
|
||||
|
@ -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";
|
||||
|
@ -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";
|
||||
|
@ -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";
|
||||
|
@ -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;
|
||||
|
@ -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
|
||||
|
@ -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";
|
||||
|
@ -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";
|
||||
|
@ -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";
|
||||
|
@ -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";
|
||||
|
||||
|
@ -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";
|
||||
|
||||
|
@ -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";
|
||||
|
@ -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";
|
||||
|
@ -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";
|
||||
|
@ -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";
|
||||
|
||||
|
@ -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";
|
||||
|
@ -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
|
||||
|
@ -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";
|
||||
|
@ -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";
|
||||
|
@ -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";
|
||||
|
@ -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";
|
||||
|
||||
|
@ -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";
|
||||
|
@ -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
|
||||
|
@ -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
Loading…
Reference in New Issue
Block a user