From 55a89042100205568cc005df8a4bb2322273821f Mon Sep 17 00:00:00 2001 From: Adrian Wozniak Date: Tue, 31 Mar 2020 08:56:46 +0200 Subject: [PATCH] Feature(Project board): Add text filter input --- jirs-client/js/css/app.css | 10 +- jirs-client/js/css/project.css | 50 +++++ jirs-client/js/css/shared.css | 59 +++++- jirs-client/js/styles.css | 1 + jirs-client/src/lib.rs | 3 + jirs-client/src/model.rs | 14 +- jirs-client/src/project.rs | 62 ++++++- jirs-client/src/project_settings.rs | 7 +- jirs-client/src/shared/aside.rs | 66 +++++++ jirs-client/src/shared/mod.rs | 171 +----------------- jirs-client/src/shared/navbar_left.rs | 101 +++++++++++ jirs-client/src/shared/styled_input.rs | 55 ++++++ .../src/Project/Board/Header/index.jsx | 14 +- react-client/src/Project/Board/index.jsx | 14 +- react-client/src/Project/NavbarLeft/index.jsx | 22 +-- react-client/src/Project/index.jsx | 22 +-- .../shared/components/Breadcrumbs/index.jsx | 22 +-- .../src/shared/components/Button/index.jsx | 52 +++--- 18 files changed, 486 insertions(+), 259 deletions(-) create mode 100644 jirs-client/js/css/project.css create mode 100644 jirs-client/src/shared/aside.rs create mode 100644 jirs-client/src/shared/navbar_left.rs create mode 100644 jirs-client/src/shared/styled_input.rs diff --git a/jirs-client/js/css/app.css b/jirs-client/js/css/app.css index 2c20a8cd..afc86c50 100644 --- a/jirs-client/js/css/app.css +++ b/jirs-client/js/css/app.css @@ -10,13 +10,13 @@ main { min-height: 100%; } -article#inner-layout { +article.inner-layout { width: 100%; } @media (min-width: 1240px) { - article#inner-layout { - display: flex; - justify-content: start; - } + /*article.inner-layout {*/ + /* display: flex;*/ + /* justify-content: start;*/ + /*}*/ } diff --git a/jirs-client/js/css/project.css b/jirs-client/js/css/project.css new file mode 100644 index 00000000..d7eb20ef --- /dev/null +++ b/jirs-client/js/css/project.css @@ -0,0 +1,50 @@ +#projectPage { + padding: 25px 32px 50px calc(var(--appNavBarLeftWidth) + var(--secondarySideBarWidth) + 40px); +} + +#projectPage > .breadcrumbsContainer { + color: var(--textMedium); + font-size: 15px; +} + +#projectPage > .breadcrumbsContainer > .breadcrumbsDivider { + position: relative; + top: 2px; + margin: 0 10px; + font-size: 18px; +} + +#projectPage > #projectBoardHeader { + margin-top: 6px; + display: flex; + justify-content: space-between; +} + +#projectPage > #projectBoardHeader > #boardName { + font-size: 24px; + font-family: var(--font-medium); + font-weight: normal; +} + +#projectPage > #projectBoardFilters { + display: flex; + align-items: center; + margin-top: 24px; +} + +#projectPage > #projectBoardFilters > #searchInput { + margin-right: 18px; + width: 160px; +} + +@media (max-width: 1100px) { + #projectPage { + padding: 25px 20px 50px calc(var(--appNavBarLeftWidth) + var(--secondarySideBarWidth) + 20px); + } +} + +@media (max-width: 999px) { + #projectPage { + padding-left: calc(var(--appNavBarLeftWidth) + var(--secondarySideBarWidth) + 20px - var(--secondarySideBarWidth)); + } +} diff --git a/jirs-client/js/css/shared.css b/jirs-client/js/css/shared.css index e90fe8f8..8de8728d 100644 --- a/jirs-client/js/css/shared.css +++ b/jirs-client/js/css/shared.css @@ -37,7 +37,7 @@ padding: 0 9px; } -.styledButton.primary { +.styledButton.primary, .styledButton.primary > i { color: #fff; background: var(--primary); font-family: var(--font-medium); @@ -55,17 +55,17 @@ filter: brightness(110%); } -.styledButton.success { +.styledButton.success, .styledButton.success > i { color: #fff; background: var(--success); } -.styledButton.danger { +.styledButton.danger, .styledButton.danger > i { color: #fff; background: var(--danger); } -.styledButton.secondary { +.styledButton.secondary, .styledButton.secondary > i { color: var(--textDark); background: var(--secondary); font-family: var(--font-regular); @@ -85,7 +85,7 @@ background: var(--backgroundLightPrimary); } -.styledButton.empty { +.styledButton.empty, .styledButton.empty > i { background: #fff; color: var(--textDark); font-family: var(--font-regular); @@ -104,3 +104,52 @@ color: var(--primary); background: var(--backgroundLightPrimary); } + +.styledInput { + position: relative; + display: inline-block; + height: 32px; + width: 100%; +} + +.styledInput > .inputElement { + height: 100%; + width: 100%; + padding: 0 7px; + border-radius: 3px; + border: 1px solid var(--borderLightest); + color: var(--textDarkest); + background: var(--backgroundLightest); + transition: background 0.1s; + font-family: var(--font-regular); + font-size: 15px; +} + +.styledInput > .inputElement.withIcon { + padding-left: 32px; +} + +.styledInput > i.styledIcon { + font-size: 15px; + position: absolute; + top: 8px; + left: 8px; + pointer-events: none; + color: #5E6C84; +} + +.styledInput > .inputElement:hover { + background: var(--backgroundLight); +} + +.styledInput > .inputElement:focus { + background: #fff; + border: 1px solid var(--borderInputFocus); + box-shadow: 0 0 0 1px var(--borderInputFocus); +} + +.styledInput.invalid, +.styledInput.invalid:focus { + border: 1px solid var(--danger); + box-shadow: none; +} diff --git a/jirs-client/js/styles.css b/jirs-client/js/styles.css index 96f72504..22363157 100644 --- a/jirs-client/js/styles.css +++ b/jirs-client/js/styles.css @@ -8,3 +8,4 @@ @import "css/shared.css"; @import "css/styledTooltip.css"; @import "css/app.css"; +@import "css/project.css"; diff --git a/jirs-client/src/lib.rs b/jirs-client/src/lib.rs index dbc12fc1..9867aba0 100644 --- a/jirs-client/src/lib.rs +++ b/jirs-client/src/lib.rs @@ -18,6 +18,9 @@ pub enum Msg { CurrentUserResult(FetchObject), InternalFailure(String), ToggleAboutTooltip, + + // project + ProjectTextFilterChanged(String), } fn update(msg: Msg, model: &mut model::Model, orders: &mut impl Orders) { diff --git a/jirs-client/src/model.rs b/jirs-client/src/model.rs index d347fa7f..acd8d506 100644 --- a/jirs-client/src/model.rs +++ b/jirs-client/src/model.rs @@ -47,6 +47,12 @@ pub struct UpdateProjectForm { pub fields: UpdateProjectPayload, } +#[derive(Serialize, Deserialize, Debug)] +pub struct ProjectPage { + pub about_tooltip_visible: bool, + pub text_filter: String, +} + #[derive(Serialize, Deserialize, Debug)] pub struct Model { pub access_token: Option, @@ -59,8 +65,7 @@ pub struct Model { pub comments_by_project_id: HashMap>, pub page: Page, pub host_url: String, - // - pub about_tooltip_visible: bool, + pub project_page: ProjectPage, } impl Default for Model { @@ -77,7 +82,10 @@ impl Default for Model { comments_by_project_id: Default::default(), page: Page::Project, host_url, - about_tooltip_visible: false, + project_page: ProjectPage { + about_tooltip_visible: false, + text_filter: "".to_string(), + }, } } } diff --git a/jirs-client/src/project.rs b/jirs-client/src/project.rs index fb37ee81..b7f38c74 100644 --- a/jirs-client/src/project.rs +++ b/jirs-client/src/project.rs @@ -1,6 +1,8 @@ use seed::{prelude::*, *}; -use crate::model::Page; +use crate::model::{Icon, Model, Page}; +use crate::shared::styled_button::{StyledButton, Variant}; +use crate::shared::styled_input::StyledInput; use crate::shared::{host_client, inner_layout}; use crate::Msg; @@ -15,14 +17,64 @@ pub fn update(msg: Msg, model: &mut crate::model::Model, orders: &mut impl Order .perform_cmd(crate::api::fetch_current_user(model.host_url.clone())); } Msg::ToggleAboutTooltip => { - model.about_tooltip_visible = !model.about_tooltip_visible; + model.project_page.about_tooltip_visible = !model.project_page.about_tooltip_visible; + } + Msg::ProjectTextFilterChanged(text) => { + model.project_page.text_filter = text; } _ => (), } } -pub fn view(model: &crate::model::Model) -> Node { - let project_section = section![id!["project-section"],]; +pub fn view(model: &Model) -> Node { + let project_section = vec![breadcrumbs(model), header(), project_board_filters(model)]; - inner_layout(model, project_section) + inner_layout(model, "projectPage", project_section) +} + +fn breadcrumbs(model: &Model) -> Node { + let project_name = model + .project + .as_ref() + .map(|p| p.name.clone()) + .unwrap_or_default(); + div![ + attrs![At::Class => "breadcrumbsContainer"], + span!["Projects"], + span![attrs![At::Class => "breadcrumbsDivider"], "/"], + span![project_name], + span![attrs![At::Class => "breadcrumbsDivider"], "/"], + span!["Kanban Board"] + ] +} + +fn header() -> Node { + let button = StyledButton { + variant: Variant::Secondary, + icon_only: false, + disabled: false, + active: false, + text: Some("Github Repo".to_string()), + icon: Some(Icon::Github), + } + .into_node(); + div![ + id!["projectBoardHeader"], + div![id!["boardName"], "Kanban board"], + a![ + attrs![At::Href => "https://gitlab.com/adrian.wozniak/jirs", At::Target => "__blank", At::Rel => "noreferrer noopener"], + button + ] + ] +} + +fn project_board_filters(_model: &Model) -> Node { + let search_input = StyledInput { + icon: Some(Icon::Search), + id: Some("searchInput".to_string()), + valid: true, + on_change: input_ev(Ev::Change, |value| Msg::ProjectTextFilterChanged(value)), + } + .into_node(); + div![id!["projectBoardFilters"], search_input] } diff --git a/jirs-client/src/project_settings.rs b/jirs-client/src/project_settings.rs index 58949a60..a1fcec11 100644 --- a/jirs-client/src/project_settings.rs +++ b/jirs-client/src/project_settings.rs @@ -1,11 +1,12 @@ +use seed::{prelude::*, *}; + use crate::shared::inner_layout; use crate::{model, Msg}; -use seed::{prelude::*, *}; pub fn update(_msg: Msg, _model: &mut model::Model, _orders: &mut impl Orders) {} pub fn view(model: &model::Model) -> Node { - let project_section = section![id!["project-settings-section"],]; + let project_section = vec![]; - inner_layout(model, project_section) + inner_layout(model, "projectSettings", project_section) } diff --git a/jirs-client/src/shared/aside.rs b/jirs-client/src/shared/aside.rs new file mode 100644 index 00000000..5689ff28 --- /dev/null +++ b/jirs-client/src/shared/aside.rs @@ -0,0 +1,66 @@ +use seed::{prelude::*, *}; + +use crate::model::{Icon, Model, Page}; +use crate::shared::{divider, styled_icon}; +use crate::Msg; + +pub fn render(model: &Model) -> Node { + let project_icon = Node::from_html(include_str!("../../static/project-avatar.svg")); + let project_info = match model.project.as_ref() { + Some(project) => li![ + id!["projectInfo"], + project_icon, + div![ + attrs![At::Class => "projectTexts";], + div![attrs![At::Class => "projectName";], project.name], + div![attrs![At::Class => "projectCategory";], project.category] + ], + ], + _ => li![ + id!["projectInfo"], + div![ + attrs![At::Class => "projectTexts";], + div![attrs![At::Class => "projectName";], ""], + div![attrs![At::Class => "projectCategory";], ""] + ], + ], + }; + nav![ + id!["sidebar"], + ul![ + project_info, + sidebar_link_item(model, "Kanban Board", Icon::Board, Some(Page::Project)), + sidebar_link_item( + model, + "Project settings", + Icon::Settings, + Some(Page::ProjectSettings) + ), + li![divider()], + sidebar_link_item(model, "Releases", Icon::Shipping, None), + sidebar_link_item(model, "Issue and Filters", Icon::Issues, None), + sidebar_link_item(model, "Pages", Icon::Page, None), + sidebar_link_item(model, "Reports", Icon::Reports, None), + sidebar_link_item(model, "Components", Icon::Component, None), + ] + ] +} + +fn sidebar_link_item(model: &Model, name: &str, icon: Icon, page: Option) -> Node { + let path = page.map(|ref p| p.to_path()).unwrap_or_default(); + let mut class_list = vec!["linkItem".to_string(), icon.to_string()]; + if let None = page { + class_list.push("notAllowed".to_string()); + }; + if Some(model.page) == page { + class_list.push("active".to_string()); + } + li![ + attrs![At::Class => class_list.join(" ")], + a![ + attrs![At::Href => path], + styled_icon(icon), + div![attrs![At::Class => "linkText"], name], + ] + ] +} diff --git a/jirs-client/src/shared/mod.rs b/jirs-client/src/shared/mod.rs index 93ea6dad..8027d12c 100644 --- a/jirs-client/src/shared/mod.rs +++ b/jirs-client/src/shared/mod.rs @@ -2,170 +2,16 @@ use seed::fetch::{FetchObject, ResponseWithDataResult}; use seed::{prelude::*, *}; use jirs_data::FullProjectResponse; -use styled_button::*; -use crate::model::{Icon, Model, Page}; +use crate::model::{Icon, Model}; use crate::Msg; +pub mod aside; +pub mod navbar_left; pub mod styled_button; +pub mod styled_input; pub mod styled_tooltip; -pub fn navbar_left(model: &Model) -> Vec> { - let logo_svg = Node::from_html(include_str!("../../static/logo.svg")); - - vec![ - about_tooltip_popup(model), - aside![ - id!["navbar-left"], - a![ - attrs![At::Class => "logoLink", At::Href => "/"], - div![attrs![At::Class => "styledLogo"], logo_svg] - ], - navbar_left_item(model, "Search issues", Icon::Search), - navbar_left_item(model, "Create Issue", Icon::Plus), - div![ - attrs![At::Class => "bottom"], - about_tooltip(model, navbar_left_item(model, "About", Icon::Help)), - ], - ], - ] -} - -fn navbar_left_item(_model: &Model, text: &str, logo: Icon) -> Node { - div![ - attrs![At::Class => "item"], - i![attrs![At::Class => format!("styledIcon {}", logo)]], - span![attrs![At::Class => "itemText"], text] - ] -} - -pub fn about_tooltip(model: &Model, children: Node) -> Node { - div![ - attrs![At::Class => "aboutTooltip"], - ev(Ev::Click, |_| Msg::ToggleAboutTooltip), - children - ] -} - -fn about_tooltip_popup(model: &Model) -> Node { - styled_tooltip::StyledTooltip { - visible: model.about_tooltip_visible, - class_name: "aboutTooltipPopup".to_string(), - children: div![ - ev(Ev::Click, |_| Msg::ToggleAboutTooltip), - attrs![At::Class => "feedbackDropdown"], - div![ - attrs![At::Class => "feedbackImageCont"], - img![attrs![At::Src => "/feedback.png", At::Class => "feedbackImage"]] - ], - div![ - attrs![At::Class => "feedbackParagraph"], - "This simplified Jira clone is built with Seed.rs on the front-end and Actix-Web on the back-end." - ], - div![ - attrs![At::Class => "feedbackParagraph"], - "Read more on my website or reach out via ", - a![ - attrs![At::Href => "mailto:adrian.wozniak@ita-prog.pl"], - strong!["adrian.wozniak@ita-prog.pl"] - ] - ], - a![ - attrs![ - At::Href => "https://gitlab.com/adrian.wozniak/jirs", - At::Target => "_blank", - At::Rel => "noreferrer noopener", - ], - StyledButton { - text: Some("Visit Website".to_string()), - variant: Variant::Primary, - disabled: false, - active: false, - icon_only: false, - icon: None, - }.into_node(), - ], - a![ - id!["about-github-button"], - attrs![ - At::Href => "https://gitlab.com/adrian.wozniak/jirs", - At::Target => "_blank", - At::Rel => "noreferrer noopener", - ], - StyledButton { - text: Some("Github Repo".to_string()), - variant: Variant::Secondary, - disabled: false, - active: false, - icon_only: false, - icon: Some(Icon::Github), - }.into_node() - ] - ], - }.into_node() -} - -pub fn sidebar(model: &Model) -> Node { - let project_icon = Node::from_html(include_str!("../../static/project-avatar.svg")); - let project_info = match model.project.as_ref() { - Some(project) => li![ - id!["projectInfo"], - project_icon, - div![ - attrs![At::Class => "projectTexts";], - div![attrs![At::Class => "projectName";], project.name], - div![attrs![At::Class => "projectCategory";], project.category] - ], - ], - _ => li![ - id!["projectInfo"], - div![ - attrs![At::Class => "projectTexts";], - div![attrs![At::Class => "projectName";], ""], - div![attrs![At::Class => "projectCategory";], ""] - ], - ], - }; - nav![ - id!["sidebar"], - ul![ - project_info, - sidebar_link_item(model, "Kanban Board", Icon::Board, Some(Page::Project)), - sidebar_link_item( - model, - "Project settings", - Icon::Settings, - Some(Page::ProjectSettings) - ), - li![divider()], - sidebar_link_item(model, "Releases", Icon::Shipping, None), - sidebar_link_item(model, "Issue and Filters", Icon::Issues, None), - sidebar_link_item(model, "Pages", Icon::Page, None), - sidebar_link_item(model, "Reports", Icon::Reports, None), - sidebar_link_item(model, "Components", Icon::Component, None), - ] - ] -} - -fn sidebar_link_item(model: &Model, name: &str, icon: Icon, page: Option) -> Node { - let path = page.map(|ref p| p.to_path()).unwrap_or_default(); - let mut class_list = vec!["linkItem".to_string(), icon.to_string()]; - let item_class = if let None = page { - class_list.push("notAllowed".to_string()) - }; - if Some(model.page) == page { - class_list.push("active".to_string()); - } - li![ - attrs![At::Class => class_list.join(" ")], - a![ - attrs![At::Href => path], - styled_icon(icon), - div![attrs![At::Class => "linkText"], name], - ] - ] -} - pub fn styled_icon(icon: Icon) -> Node { i![attrs![At::Class => format!("styledIcon {}", icon)], ""] } @@ -174,11 +20,12 @@ pub fn divider() -> Node { div![attrs![At::Class => "divider"], ""] } -pub fn inner_layout(model: &Model, children: Node) -> Node { +pub fn inner_layout(model: &Model, page_name: &str, children: Vec>) -> Node { article![ - id!["inner-layout"], - navbar_left(model), - sidebar(model), + attrs![At::Class => "inner-layout"], + id![page_name], + navbar_left::render(model), + aside::render(model), children, ] } diff --git a/jirs-client/src/shared/navbar_left.rs b/jirs-client/src/shared/navbar_left.rs new file mode 100644 index 00000000..aeb14419 --- /dev/null +++ b/jirs-client/src/shared/navbar_left.rs @@ -0,0 +1,101 @@ +use seed::{prelude::*, *}; + +use crate::model::{Icon, Model}; +use crate::shared::styled_button::{StyledButton, Variant}; +use crate::shared::styled_tooltip; +use crate::Msg; + +pub fn render(model: &Model) -> Vec> { + let logo_svg = Node::from_html(include_str!("../../static/logo.svg")); + + vec![ + about_tooltip_popup(model), + aside![ + id!["navbar-left"], + a![ + attrs![At::Class => "logoLink", At::Href => "/"], + div![attrs![At::Class => "styledLogo"], logo_svg] + ], + navbar_left_item(model, "Search issues", Icon::Search), + navbar_left_item(model, "Create Issue", Icon::Plus), + div![ + attrs![At::Class => "bottom"], + about_tooltip(model, navbar_left_item(model, "About", Icon::Help)), + ], + ], + ] +} + +fn navbar_left_item(_model: &Model, text: &str, logo: Icon) -> Node { + div![ + attrs![At::Class => "item"], + i![attrs![At::Class => format!("styledIcon {}", logo)]], + span![attrs![At::Class => "itemText"], text] + ] +} + +pub fn about_tooltip(_model: &Model, children: Node) -> Node { + div![ + attrs![At::Class => "aboutTooltip"], + ev(Ev::Click, |_| Msg::ToggleAboutTooltip), + children + ] +} + +fn about_tooltip_popup(model: &Model) -> Node { + styled_tooltip::StyledTooltip { + visible: model.project_page.about_tooltip_visible, + class_name: "aboutTooltipPopup".to_string(), + children: div![ + ev(Ev::Click, |_| Msg::ToggleAboutTooltip), + attrs![At::Class => "feedbackDropdown"], + div![ + attrs![At::Class => "feedbackImageCont"], + img![attrs![At::Src => "/feedback.png", At::Class => "feedbackImage"]] + ], + div![ + attrs![At::Class => "feedbackParagraph"], + "This simplified Jira clone is built with Seed.rs on the front-end and Actix-Web on the back-end." + ], + div![ + attrs![At::Class => "feedbackParagraph"], + "Read more on my website or reach out via ", + a![ + attrs![At::Href => "mailto:adrian.wozniak@ita-prog.pl"], + strong!["adrian.wozniak@ita-prog.pl"] + ] + ], + a![ + attrs![ + At::Href => "https://gitlab.com/adrian.wozniak/jirs", + At::Target => "_blank", + At::Rel => "noreferrer noopener", + ], + StyledButton { + text: Some("Visit Website".to_string()), + variant: Variant::Primary, + disabled: false, + active: false, + icon_only: false, + icon: None, + }.into_node(), + ], + a![ + id!["about-github-button"], + attrs![ + At::Href => "https://gitlab.com/adrian.wozniak/jirs", + At::Target => "_blank", + At::Rel => "noreferrer noopener", + ], + StyledButton { + text: Some("Github Repo".to_string()), + variant: Variant::Secondary, + disabled: false, + active: false, + icon_only: false, + icon: Some(Icon::Github), + }.into_node() + ] + ], + }.into_node() +} diff --git a/jirs-client/src/shared/styled_input.rs b/jirs-client/src/shared/styled_input.rs new file mode 100644 index 00000000..a78577c7 --- /dev/null +++ b/jirs-client/src/shared/styled_input.rs @@ -0,0 +1,55 @@ +use seed::{prelude::*, *}; + +use crate::model::Icon; +use crate::shared::styled_icon; +use crate::Msg; + +pub struct StyledInput { + pub id: Option, + pub icon: Option, + pub valid: bool, + pub on_change: EventHandler, +} + +impl Into> for StyledInput { + fn into(self) -> Node { + render(self) + } +} + +impl StyledInput { + pub fn into_node(self) -> Node { + self.into() + } +} + +pub fn render(values: StyledInput) -> Node { + let StyledInput { + id, + icon, + valid, + on_change, + } = values; + + let mut wrapper_class_list = vec!["styledInput"]; + if !valid { + wrapper_class_list.push("invalid"); + } + + let mut input_class_list = vec!["inputElement"]; + if icon.is_some() { + input_class_list.push("withIcon"); + } + + let icon = match icon { + Some(icon) => vec![styled_icon(icon)], + _ => vec![], + }; + + div![ + id![id.unwrap_or_default()], + attrs!(At::Class => wrapper_class_list.join(" ")), + icon, + input![attrs![At::Class => input_class_list.join(" ")], on_change] + ] +} diff --git a/react-client/src/Project/Board/Header/index.jsx b/react-client/src/Project/Board/Header/index.jsx index 907d41ec..231df521 100644 --- a/react-client/src/Project/Board/Header/index.jsx +++ b/react-client/src/Project/Board/Header/index.jsx @@ -2,15 +2,15 @@ import React from 'react'; import { Button } from 'shared/components'; -import { Header, BoardName } from './Styles'; +import { BoardName, Header } from './Styles'; const ProjectBoardHeader = () => ( -
- Kanban board - - - -
+
+ Kanban board + + + +
); export default ProjectBoardHeader; diff --git a/react-client/src/Project/Board/index.jsx b/react-client/src/Project/Board/index.jsx index 31e8da7c..de01145b 100644 --- a/react-client/src/Project/Board/index.jsx +++ b/react-client/src/Project/Board/index.jsx @@ -1,6 +1,6 @@ import React, { Fragment } from 'react'; import PropTypes from 'prop-types'; -import { Route, useRouteMatch, useHistory } from 'react-router-dom'; +import { Route, useHistory, useRouteMatch } from 'react-router-dom'; import useMergeState from 'shared/hooks/mergeState'; import { Breadcrumbs, Modal } from 'shared/components'; @@ -10,12 +10,6 @@ import Filters from './Filters'; import Lists from './Lists'; import IssueDetails from './IssueDetails'; -const propTypes = { - project: PropTypes.object.isRequired, - fetchProject: PropTypes.func.isRequired, - updateLocalProjectIssues: PropTypes.func.isRequired, -}; - const defaultFilters = { searchTerm: '', userIds: [], @@ -69,6 +63,10 @@ const ProjectBoard = ({ project, fetchProject, updateLocalProjectIssues }) => { ); }; -ProjectBoard.propTypes = propTypes; +ProjectBoard.propTypes = { + project: PropTypes.object.isRequired, + fetchProject: PropTypes.func.isRequired, + updateLocalProjectIssues: PropTypes.func.isRequired, +}; export default ProjectBoard; diff --git a/react-client/src/Project/NavbarLeft/index.jsx b/react-client/src/Project/NavbarLeft/index.jsx index 9067c927..4a6c520f 100644 --- a/react-client/src/Project/NavbarLeft/index.jsx +++ b/react-client/src/Project/NavbarLeft/index.jsx @@ -1,9 +1,9 @@ import React from 'react'; import PropTypes from 'prop-types'; -import { Icon, AboutTooltip } from 'shared/components'; +import { AboutTooltip, Icon } from 'shared/components'; -import { NavLeft, LogoLink, StyledLogo, Bottom, Item, ItemText } from './Styles'; +import { Bottom, Item, ItemText, LogoLink, NavLeft, StyledLogo } from './Styles'; const propTypes = { issueSearchModalOpen: PropTypes.func.isRequired, @@ -11,17 +11,17 @@ const propTypes = { }; const ProjectNavbarLeft = ({ issueSearchModalOpen, issueCreateModalOpen }) => ( - - - - + + + + - - - Search issues - + + + Search issues + - + Create Issue diff --git a/react-client/src/Project/index.jsx b/react-client/src/Project/index.jsx index 8733b79f..834aefe5 100644 --- a/react-client/src/Project/index.jsx +++ b/react-client/src/Project/index.jsx @@ -1,10 +1,10 @@ import React from 'react'; -import { Route, Redirect, useRouteMatch, useHistory } from 'react-router-dom'; +import { Redirect, Route, useHistory, useRouteMatch } from 'react-router-dom'; import useApi from 'shared/hooks/api'; import { updateArrayItemById } from 'shared/utils/javascript'; import { createQueryParamModalHelpers } from 'shared/utils/queryParamModal'; -import { PageLoader, PageError, Modal } from 'shared/components'; +import { Modal, PageError, PageLoader } from 'shared/components'; import NavbarLeft from './NavbarLeft'; import Sidebar from './Sidebar'; @@ -38,17 +38,17 @@ const Project = () => { }; return ( - - + + - + - {issueSearchModalHelpers.isOpen() && ( - ( - - {items.map((item, index) => ( - - {index !== 0 && /} - {item} - - ))} + + { items.map((item, index) => ( + + { index !== 0 && / } + { item } + + )) } ); -Breadcrumbs.propTypes = propTypes; +Breadcrumbs.propTypes = { + items: PropTypes.array.isRequired, +}; export default Breadcrumbs; diff --git a/react-client/src/shared/components/Button/index.jsx b/react-client/src/shared/components/Button/index.jsx index 2a305377..947d89d8 100644 --- a/react-client/src/shared/components/Button/index.jsx +++ b/react-client/src/shared/components/Button/index.jsx @@ -6,28 +6,6 @@ import Icon from 'shared/components/Icon'; import { StyledButton, StyledSpinner, Text } from './Styles'; -const propTypes = { - className: PropTypes.string, - children: PropTypes.node, - variant: PropTypes.oneOf(['primary', 'success', 'danger', 'secondary', 'empty']), - icon: PropTypes.oneOfType([PropTypes.string, PropTypes.node]), - iconSize: PropTypes.number, - disabled: PropTypes.bool, - isWorking: PropTypes.bool, - onClick: PropTypes.func, -}; - -const defaultProps = { - className: undefined, - children: undefined, - variant: 'secondary', - icon: undefined, - iconSize: 18, - disabled: false, - isWorking: false, - onClick: () => {}, -}; - const Button = forwardRef( ({ children, variant, icon, iconSize, disabled, isWorking, onClick, ...buttonProps }, ref) => { const handleClick = () => { @@ -52,17 +30,37 @@ const Button = forwardRef( ) : ( icon - )} - {children && {children}} + ) } + { children && { children } } ); }, ); const getIconColor = variant => - ['secondary', 'empty'].includes(variant) ? color.textDark : '#fff'; + [ 'secondary', 'empty' ].includes(variant) ? color.textDark : '#fff'; -Button.propTypes = propTypes; -Button.defaultProps = defaultProps; +Button.propTypes = { + className: PropTypes.string, + children: PropTypes.node, + variant: PropTypes.oneOf([ 'primary', 'success', 'danger', 'secondary', 'empty' ]), + icon: PropTypes.oneOfType([ PropTypes.string, PropTypes.node ]), + iconSize: PropTypes.number, + disabled: PropTypes.bool, + isWorking: PropTypes.bool, + onClick: PropTypes.func, +}; + +Button.defaultProps = { + className: undefined, + children: undefined, + variant: 'secondary', + icon: undefined, + iconSize: 18, + disabled: false, + isWorking: false, + onClick: () => { + }, +}; export default Button;