From 55bf0cea6aef21daa9e45a091f4d38e19debb5bb Mon Sep 17 00:00:00 2001 From: Gabriel Hernandez Date: Wed, 19 Oct 2022 23:44:27 +0100 Subject: [PATCH] change label filter dropdown icon match status dropdown and extend icon component (#8337) --- .../issue-8105-update-label-filter-chevron | 1 + frontend/components/Icon/Icon.tsx | 19 +++++++++--- frontend/components/Icon/_styles.scss | 6 ++++ frontend/components/icons/ChevronDown.tsx | 20 +++++++++++++ frontend/components/icons/index.ts | 2 ++ frontend/docs/patterns.md | 27 +++++++++++++++++ .../CustomDropdownIndicator.tsx | 29 +++++++++++++++++++ .../CustomDropdownIndicator/index.ts | 1 + .../LabelFilterSelect/LabelFilterSelect.tsx | 10 +++++-- .../components/LabelFilterSelect/_styles.scss | 16 ++++++++++ 10 files changed, 124 insertions(+), 7 deletions(-) create mode 100644 changes/issue-8105-update-label-filter-chevron create mode 100644 frontend/components/icons/ChevronDown.tsx create mode 100644 frontend/pages/hosts/ManageHostsPage/components/CustomDropdownIndicator/CustomDropdownIndicator.tsx create mode 100644 frontend/pages/hosts/ManageHostsPage/components/CustomDropdownIndicator/index.ts diff --git a/changes/issue-8105-update-label-filter-chevron b/changes/issue-8105-update-label-filter-chevron new file mode 100644 index 0000000000..9fc3479145 --- /dev/null +++ b/changes/issue-8105-update-label-filter-chevron @@ -0,0 +1 @@ +- updates label filter chevron icon to match the icon on the status filter dropdown diff --git a/frontend/components/Icon/Icon.tsx b/frontend/components/Icon/Icon.tsx index 23d9c1306a..15c3f437aa 100644 --- a/frontend/components/Icon/Icon.tsx +++ b/frontend/components/Icon/Icon.tsx @@ -1,22 +1,33 @@ -import React from "react"; - +import React, { useMemo } from "react"; import { IconNames, ICON_MAP } from "components/icons"; import classnames from "classnames"; interface IIconProps { name: IconNames; + color?: string; className?: string; } const baseClass = "icon"; -const Icon = ({ name, className }: IIconProps) => { +const Icon = ({ name, color, className }: IIconProps) => { const classsNames = classnames(baseClass, className); + // createPassedProps creates a props object that we pass to the specific icon + // for values that are not null or undefined + const props = useMemo(() => { + const createPassedProps = () => { + return Object.assign({}, color === undefined ? undefined : { color }); + }; + + return createPassedProps(); + }, [color]); + const IconComponent = ICON_MAP[name]; + return (
- +
); }; diff --git a/frontend/components/Icon/_styles.scss b/frontend/components/Icon/_styles.scss index e69de29bb2..2be34ae007 100644 --- a/frontend/components/Icon/_styles.scss +++ b/frontend/components/Icon/_styles.scss @@ -0,0 +1,6 @@ +.icon { + // we want to ensure the icon is always centered with these styles + display: flex; + align-items: center; + justify-content: center; +} diff --git a/frontend/components/icons/ChevronDown.tsx b/frontend/components/icons/ChevronDown.tsx new file mode 100644 index 0000000000..67a6e89854 --- /dev/null +++ b/frontend/components/icons/ChevronDown.tsx @@ -0,0 +1,20 @@ +import React from "react"; + +interface IChevronDownProps { + color?: string; +} + +const ChevronDown = ({ color = "#192147" }: IChevronDownProps) => { + return ( + + + + ); +}; + +export default ChevronDown; diff --git a/frontend/components/icons/index.ts b/frontend/components/icons/index.ts index 9647a68921..1a23fb3afa 100644 --- a/frontend/components/icons/index.ts +++ b/frontend/components/icons/index.ts @@ -7,6 +7,7 @@ import LowDiskSpaceHosts from "./LowDiskSpaceHosts"; import ApplePurple from "./ApplePurple"; import LinuxGreen from "./LinuxGreen"; import WindowsBlue from "./WindowsBlue"; +import ChevronDown from "./ChevronDown"; // a mapping of the usable names of icons to the icon source. export const ICON_MAP = { @@ -22,6 +23,7 @@ export const ICON_MAP = { "linux-green": LinuxGreen, "missing-hosts": MissingHosts, "low-disk-space-hosts": LowDiskSpaceHosts, + "chevron-down": ChevronDown, }; export type IconNames = keyof typeof ICON_MAP; diff --git a/frontend/docs/patterns.md b/frontend/docs/patterns.md index 4b7c35d74a..c1ad1b9974 100644 --- a/frontend/docs/patterns.md +++ b/frontend/docs/patterns.md @@ -15,6 +15,7 @@ should be discussed within the team and documented before merged. - [Fleet API Calls](#fleet-api-calls) - [Page Routing](#page-routing) - [Styles](#styles) + - [Icons and Images](#icons) - [Other](#other) ## Typing @@ -260,6 +261,8 @@ const PageOrComponent = ({ Below are a few need-to-knows about what's available in Fleet's CSS: + + ### Modals 1) When creating a modal with a form inside, the action buttons (cancel, save, delete, etc.) should @@ -281,6 +284,30 @@ suffice as long as we do not go more than two levels deep. Otherwise, if states to be used across multiple unrelated components or 3+ levels from a parent, then the [app's context](#react-context) should be used. +## Icons and Images + +### Adding Icons + +To add a new icon: + +1. create a React component for the icon in `frontend/components/icons` directory. We will add the + SVG here. +2. download the icon source from Figma as an SVG file +3. run the downloaded file through an SVG optimizer such as + [SVGOMG](https://jakearchibald.github.io/svgomg/) or [SVG Optimizer](https://svgoptimizer.com/) +4. download the optimized SVG and place it in created file from step 1. +5. import the new icon in the `frontend/components/icons/index.ts` and add it the the `ICON_MAP` + object. The key will be the name the icon is accessible under. + +The icon should now be available to use with the `Icon` component from the given key name. + +```tsx +// using a new icon with the given key name 'chevron` + +``` + + + ### File size The recommend line limit per page/component is 500 lines. This is only a recommendation. diff --git a/frontend/pages/hosts/ManageHostsPage/components/CustomDropdownIndicator/CustomDropdownIndicator.tsx b/frontend/pages/hosts/ManageHostsPage/components/CustomDropdownIndicator/CustomDropdownIndicator.tsx new file mode 100644 index 0000000000..b89b642cd0 --- /dev/null +++ b/frontend/pages/hosts/ManageHostsPage/components/CustomDropdownIndicator/CustomDropdownIndicator.tsx @@ -0,0 +1,29 @@ +import React from "react"; +import { DropdownIndicatorProps, components } from "react-select-5"; + +import { ILabel } from "interfaces/label"; +import Icon from "components/Icon"; + +import { IEmptyOption, IGroupOption } from "../LabelFilterSelect/helpers"; + +const baseClass = "custom-dropdown-indicator"; + +const CustomDropdownIndicator = ( + props: DropdownIndicatorProps +) => { + const { isFocused, selectProps } = props; + // no access to hover state here from react-select so that is done in the scss + // file of LabelFilterSelect. + const color = isFocused || selectProps.menuIsOpen ? "#6a67fe" : undefined; + return ( + + + + ); +}; + +export default CustomDropdownIndicator; diff --git a/frontend/pages/hosts/ManageHostsPage/components/CustomDropdownIndicator/index.ts b/frontend/pages/hosts/ManageHostsPage/components/CustomDropdownIndicator/index.ts new file mode 100644 index 0000000000..da85947c73 --- /dev/null +++ b/frontend/pages/hosts/ManageHostsPage/components/CustomDropdownIndicator/index.ts @@ -0,0 +1 @@ +export { default } from "./CustomDropdownIndicator"; diff --git a/frontend/pages/hosts/ManageHostsPage/components/LabelFilterSelect/LabelFilterSelect.tsx b/frontend/pages/hosts/ManageHostsPage/components/LabelFilterSelect/LabelFilterSelect.tsx index 1b07f59fdd..d56444fee1 100644 --- a/frontend/pages/hosts/ManageHostsPage/components/LabelFilterSelect/LabelFilterSelect.tsx +++ b/frontend/pages/hosts/ManageHostsPage/components/LabelFilterSelect/LabelFilterSelect.tsx @@ -8,6 +8,7 @@ import { PLATFORM_LABEL_DISPLAY_NAMES } from "utilities/constants"; import CustomLabelGroupHeading from "../CustomLabelGroupHeading"; import { PLATFORM_TYPE_ICONS } from "./constants"; import { createDropdownOptions, IEmptyOption, IGroupOption } from "./helpers"; +import CustomDropdownIndicator from "../CustomDropdownIndicator"; // Extending the react-select module to add custom props we need for our custom // group heading. More info here: @@ -27,6 +28,8 @@ declare module "react-select-5/dist/declarations/src/Select" { } } +const baseClass = "label-filter-select"; + /** A custom option label to show in the dropdown. Only used in this dropdown * component. You will find focus and blur handlers in this component to help * solve the problem of changing focus between the select dropdown and the @@ -53,8 +56,6 @@ const OptionLabel = (data: ILabel | IEmptyOption) => { ); }; -const baseClass = "label-filter-select"; - interface ILabelFilterSelectProps { labels: ILabel[]; selectedLabel: ILabel | null; @@ -158,7 +159,10 @@ const LabelFilterSelect = ({ isSearchable={false} getOptionLabel={getOptionLabel} getOptionValue={getOptionValue} - components={{ GroupHeading: CustomLabelGroupHeading }} + components={{ + GroupHeading: CustomLabelGroupHeading, + DropdownIndicator: CustomDropdownIndicator, + }} labelQuery={labelQuery} canAddNewLabels={canAddNewLabels} onChange={handleChange} diff --git a/frontend/pages/hosts/ManageHostsPage/components/LabelFilterSelect/_styles.scss b/frontend/pages/hosts/ManageHostsPage/components/LabelFilterSelect/_styles.scss index 853f08dc64..6891b4073a 100644 --- a/frontend/pages/hosts/ManageHostsPage/components/LabelFilterSelect/_styles.scss +++ b/frontend/pages/hosts/ManageHostsPage/components/LabelFilterSelect/_styles.scss @@ -1,6 +1,16 @@ .label-filter-select { width: 175px; + &:hover { + + // no access to hover state from react-select in JS so this is done here. + .custom-dropdown-indicator { + & path { + fill: $core-vibrant-blue + } + } + } + .label-filter-select__control { border: 1px solid $ui-fleet-blue-15; background-color: $ui-light-grey; @@ -108,4 +118,10 @@ margin-right: $pad-small; } } + + &__custom-dropdown-indicator { + &:hover { + cursor: pointer; + } + } }