From 1c310f746e674d90223ecc1f544b2f318274097b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jes=C3=BAs=20Espino?= Date: Sat, 24 Oct 2020 09:39:01 +0200 Subject: [PATCH] Extracting the view header in its own component --- webapp/src/components/boardComponent.tsx | 257 +----------------- webapp/src/components/tableComponent.tsx | 243 +---------------- webapp/src/components/viewHeader.tsx | 318 +++++++++++++++++++++++ 3 files changed, 333 insertions(+), 485 deletions(-) create mode 100644 webapp/src/components/viewHeader.tsx diff --git a/webapp/src/components/boardComponent.tsx b/webapp/src/components/boardComponent.tsx index 2105fa66c..d30143ec4 100644 --- a/webapp/src/components/boardComponent.tsx +++ b/webapp/src/components/boardComponent.tsx @@ -2,17 +2,12 @@ // See LICENSE.txt for license information. /* eslint-disable max-lines */ import React from 'react' -import {FormattedMessage} from 'react-intl' -import {Archiver} from '../archiver' -import {ISortOption} from '../blocks/boardView' import {BlockIcons} from '../blockIcons' import {IPropertyOption, IPropertyTemplate} from '../blocks/board' import {Card, MutableCard} from '../blocks/card' import {BoardTree, BoardTreeGroup} from '../viewModel/boardTree' -import {CsvExporter} from '../csvExporter' import {CardFilter} from '../cardFilter' -import ViewMenu from '../components/viewMenu' import {Constants} from '../constants' import mutator from '../mutator' import {Utils} from '../utils' @@ -25,7 +20,7 @@ import Button from './button' import {CardDialog} from './cardDialog' import {Editable} from './editable' import RootPortal from './rootPortal' -import {FilterComponent} from './filterComponent' +import ViewHeader from './viewHeader' type Props = { boardTree?: BoardTree @@ -102,11 +97,8 @@ class BoardComponent extends React.Component { const propertyValues = boardTree.groupByProperty?.options || [] Utils.log(`${propertyValues.length} propertyValues`) - const groupByStyle = {color: '#000000'} const {board, activeView} = boardTree const visiblePropertyTemplates = board.cardProperties.filter((template) => activeView.visiblePropertyIds.includes(template.id)) - const hasFilter = activeView.filter && activeView.filter.filters?.length > 0 - const hasSort = activeView.sortOptions.length > 0 const visibleGroups = boardTree.groups.filter((group) => !group.isHidden) const hiddenGroups = boardTree.groups.filter((group) => group.isHidden) @@ -175,194 +167,13 @@ class BoardComponent extends React.Component {
-
- { - mutator.changeTitle(activeView, text) - }} - /> - -
-
-
- - -
- -
- -
- - {boardTree.board.cardProperties.map((option) => ( - { - const property = boardTree.board.cardProperties.find((o) => o.id === propertyId) - Utils.assertValue(property) - Utils.log(`Toggle property ${property.name}`) - - let newVisiblePropertyIds = [] - if (activeView.visiblePropertyIds.includes(propertyId)) { - newVisiblePropertyIds = activeView.visiblePropertyIds.filter((o) => o !== propertyId) - } else { - newVisiblePropertyIds = [...activeView.visiblePropertyIds, propertyId] - } - mutator.changeViewVisibleProperties(activeView, newVisiblePropertyIds) - }} - /> - ))} - -
- -
- Group by {boardTree.groupByProperty?.name} -
- - {boardTree.board.cardProperties.filter((o) => o.type === 'select').map((option) => ( - { - if (boardTree.activeView.groupById === id) { - return - } - - mutator.changeViewGroupById(boardTree.activeView, id) - }} - /> - ))} - -
-
- - {this.state.showFilter && - } -
- -
- -
- - {boardTree.board.cardProperties.map((option) => ( - { - let newSortOptions: ISortOption[] = [] - if (activeView.sortOptions[0] && activeView.sortOptions[0].propertyId === propertyId) { - // Already sorting by name, so reverse it - newSortOptions = [ - {propertyId, reversed: !activeView.sortOptions[0].reversed}, - ] - } else { - newSortOptions = [ - {propertyId, reversed: false}, - ] - } - mutator.changeViewSortOptions(activeView, newSortOptions) - }} - /> - ))} - -
- {this.state.isSearching ? - { - this.searchChanged(text) - }} - onKeyDown={(e) => { - this.onSearchKeyDown(e) - }} - /> : -
{ - this.setState({...this.state, isSearching: true}) - }} - >Search
- } - -
- - CsvExporter.exportTableCsv(boardTree)} - /> - Archiver.exportBoardTree(boardTree)} - /> - this.testAddCards(100)} - /> - this.testAddCards(1000)} - /> - this.testRandomizeIcons()} - /> - - -
{ - this.addCard(undefined) - }} - >New
-
- - {/* Headers */} - + this.addCard()} + withGroupBy={true} + />
{ await mutator.changePropertyOptionValue(boardTree, boardTree.groupByProperty, option, text) } - private filterClicked = () => { - this.setState({showFilter: true}) - } - - private hideFilter = () => { - this.setState({showFilter: false}) - } - - private async testAddCards(count: number) { - const {boardTree} = this.props - const {board, activeView} = boardTree - - const startCount = boardTree?.cards?.length - let optionIndex = 0 - - for (let i = 0; i < count; i++) { - const card = new MutableCard() - card.parentId = boardTree.board.id - card.properties = CardFilter.propertiesThatMeetFilterGroup(activeView.filter, board.cardProperties) - if (boardTree.groupByProperty && boardTree.groupByProperty.options.length > 0) { - // Cycle through options - const option = boardTree.groupByProperty.options[optionIndex] - optionIndex = (optionIndex + 1) % boardTree.groupByProperty.options.length - card.properties[boardTree.groupByProperty.id] = option.id - card.title = `Test Card ${startCount + i + 1}` - card.icon = BlockIcons.shared.randomIcon() - } - await mutator.insertBlock(card, 'test add card') - } - } - - private async testRandomizeIcons() { - const {boardTree} = this.props - - for (const card of boardTree.cards) { - mutator.changeIcon(card, BlockIcons.shared.randomIcon(), 'randomize icon') - } - } - private cardClicked(e: React.MouseEvent, card: Card): void { if (e.shiftKey) { // Shift+Click = add to selection @@ -754,19 +526,6 @@ class BoardComponent extends React.Component { await mutator.changePropertyOptionOrder(board, boardTree.groupByProperty, draggedHeaderOption, destIndex) } } - - private onSearchKeyDown(e: React.KeyboardEvent) { - if (e.keyCode === 27) { // ESC: Clear search - this.searchFieldRef.current.text = '' - this.setState({isSearching: false}) - this.props.setSearchText(undefined) - e.preventDefault() - } - } - - private searchChanged(text?: string) { - this.props.setSearchText(text) - } } export {BoardComponent} diff --git a/webapp/src/components/tableComponent.tsx b/webapp/src/components/tableComponent.tsx index 14988e980..4ace73648 100644 --- a/webapp/src/components/tableComponent.tsx +++ b/webapp/src/components/tableComponent.tsx @@ -3,15 +3,10 @@ import React from 'react' import {FormattedMessage} from 'react-intl' -import {Archiver} from '../archiver' -import {ISortOption} from '../blocks/boardView' import {BlockIcons} from '../blockIcons' import {IPropertyTemplate} from '../blocks/board' import {Card, MutableCard} from '../blocks/card' import {BoardTree} from '../viewModel/boardTree' -import ViewMenu from '../components/viewMenu' -import {CsvExporter} from '../csvExporter' -import {CardFilter} from '../cardFilter' import {Menu as OldMenu} from '../menu' import mutator from '../mutator' import {Utils} from '../utils' @@ -23,7 +18,7 @@ import {CardDialog} from './cardDialog' import {Editable} from './editable' import RootPortal from './rootPortal' import {TableRow} from './tableRow' -import {FilterComponent} from './filterComponent' +import ViewHeader from './viewHeader' type Props = { boardTree?: BoardTree @@ -76,9 +71,6 @@ class TableComponent extends React.Component { const {board, cards, activeView} = boardTree - const hasFilter = activeView.filter && activeView.filter.filters?.length > 0 - const hasSort = activeView.sortOptions.length > 0 - this.cardIdToRowMap.clear() return ( @@ -164,181 +156,12 @@ class TableComponent extends React.Component {
-
- { - mutator.changeTitle(activeView, text) - }} - /> - -
-
-
- - -
- -
- -
- - {boardTree.board.cardProperties.map((option) => ( - { - const property = boardTree.board.cardProperties.find((o) => o.id === propertyId) - Utils.assertValue(property) - Utils.log(`Toggle property ${property.name}`) - - let newVisiblePropertyIds = [] - if (activeView.visiblePropertyIds.includes(propertyId)) { - newVisiblePropertyIds = activeView.visiblePropertyIds.filter((o) => o !== propertyId) - } else { - newVisiblePropertyIds = [...activeView.visiblePropertyIds, propertyId] - } - mutator.changeViewVisibleProperties(activeView, newVisiblePropertyIds) - }} - /> - ))} - -
-
- - {this.state.showFilter && - } -
- -
- -
- - {boardTree.board.cardProperties.map((option) => ( - { - let newSortOptions: ISortOption[] = [] - if (activeView.sortOptions[0] && activeView.sortOptions[0].propertyId === propertyId) { - // Already sorting by name, so reverse it - newSortOptions = [ - {propertyId, reversed: !activeView.sortOptions[0].reversed}, - ] - } else { - newSortOptions = [ - {propertyId, reversed: false}, - ] - } - mutator.changeViewSortOptions(activeView, newSortOptions) - }} - /> - ))} - -
- {this.state.isSearching && - - {(placeholder: string) => ( - { - this.searchChanged(text) - }} - onKeyDown={(e) => { - this.onSearchKeyDown(e) - }} - /> - )} - } - {!this.state.isSearching && -
{ - this.setState({...this.state, isSearching: true}) - }} - > - -
} - -
- - CsvExporter.exportTableCsv(boardTree)} - /> - Archiver.exportBoardTree(boardTree)} - /> - this.testAddCards(100)} - /> - this.testAddCards(1000)} - /> - this.testRandomizeIcons()} - /> - - -
{ - this.addCard(true) - }} - > - -
-
+ {/* Main content */} @@ -462,14 +285,6 @@ class TableComponent extends React.Component { ) } - private filterClicked = () => { - this.setState({showFilter: true}) - } - - private hideFilter = () => { - this.setState({showFilter: false}) - } - private async headerClicked(e: React.MouseEvent, templateId: string) { const {boardTree} = this.props const {board} = boardTree @@ -589,50 +404,6 @@ class TableComponent extends React.Component { const destIndex = template ? board.cardProperties.indexOf(template) : 0 await mutator.changePropertyTemplateOrder(board, draggedHeaderTemplate, destIndex) } - - private onSearchKeyDown(e: React.KeyboardEvent) { - if (e.keyCode === 27) { // ESC: Clear search - this.searchFieldRef.current.text = '' - this.setState({...this.state, isSearching: false}) - this.props.setSearchText(undefined) - e.preventDefault() - } - } - - private searchChanged(text?: string) { - this.props.setSearchText(text) - } - - private async testAddCards(count: number) { - const {boardTree} = this.props - const {board, activeView} = boardTree - - const startCount = boardTree?.cards?.length - let optionIndex = 0 - - for (let i = 0; i < count; i++) { - const card = new MutableCard() - card.parentId = boardTree.board.id - card.properties = CardFilter.propertiesThatMeetFilterGroup(activeView.filter, board.cardProperties) - if (boardTree.groupByProperty && boardTree.groupByProperty.options.length > 0) { - // Cycle through options - const option = boardTree.groupByProperty.options[optionIndex] - optionIndex = (optionIndex + 1) % boardTree.groupByProperty.options.length - card.properties[boardTree.groupByProperty.id] = option.id - card.title = `Test Card ${startCount + i + 1}` - card.icon = BlockIcons.shared.randomIcon() - } - await mutator.insertBlock(card, 'test add card') - } - } - - private async testRandomizeIcons() { - const {boardTree} = this.props - - for (const card of boardTree.cards) { - mutator.changeIcon(card, BlockIcons.shared.randomIcon(), 'randomize icon') - } - } } export {TableComponent} diff --git a/webapp/src/components/viewHeader.tsx b/webapp/src/components/viewHeader.tsx new file mode 100644 index 000000000..dc0215dc5 --- /dev/null +++ b/webapp/src/components/viewHeader.tsx @@ -0,0 +1,318 @@ +// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved. +// See LICENSE.txt for license information. +import React from 'react' +import {FormattedMessage} from 'react-intl' + +import {Archiver} from '../archiver' +import {ISortOption} from '../blocks/boardView' +import {BlockIcons} from '../blockIcons' +import {MutableCard} from '../blocks/card' +import {BoardTree} from '../viewModel/boardTree' +import ViewMenu from '../components/viewMenu' +import {CsvExporter} from '../csvExporter' +import {CardFilter} from '../cardFilter' +import mutator from '../mutator' +import {Utils} from '../utils' +import Menu from '../widgets/menu' +import MenuWrapper from '../widgets/menuWrapper' + +import {Editable} from './editable' +import {FilterComponent} from './filterComponent' + +type Props = { + boardTree?: BoardTree + showView: (id: string) => void + setSearchText: (text: string) => void + addCard: (show: boolean) => void + withGroupBy?: boolean +} + +type State = { + isSearching: boolean + showFilter: boolean +} + +export default class ViewHeader extends React.Component { + private searchFieldRef = React.createRef() + + shouldComponentUpdate(): boolean { + return true + } + + constructor(props: Props) { + super(props) + this.state = {isSearching: Boolean(this.props.boardTree?.getSearchText()), showFilter: false} + } + + componentDidUpdate(prevPros: Props, prevState: State): void { + if (this.state.isSearching && !prevState.isSearching) { + this.searchFieldRef.current.focus() + } + } + + private filterClicked = () => { + this.setState({showFilter: true}) + } + + private hideFilter = () => { + this.setState({showFilter: false}) + } + + private onSearchKeyDown = (e: React.KeyboardEvent) => { + if (e.keyCode === 27) { // ESC: Clear search + this.searchFieldRef.current.text = '' + this.setState({...this.state, isSearching: false}) + this.props.setSearchText(undefined) + e.preventDefault() + } + } + + private searchChanged(text?: string) { + this.props.setSearchText(text) + } + + private async testAddCards(count: number) { + const {boardTree} = this.props + const {board, activeView} = boardTree + + const startCount = boardTree?.cards?.length + let optionIndex = 0 + + for (let i = 0; i < count; i++) { + const card = new MutableCard() + card.parentId = boardTree.board.id + card.properties = CardFilter.propertiesThatMeetFilterGroup(activeView.filter, board.cardProperties) + if (boardTree.groupByProperty && boardTree.groupByProperty.options.length > 0) { + // Cycle through options + const option = boardTree.groupByProperty.options[optionIndex] + optionIndex = (optionIndex + 1) % boardTree.groupByProperty.options.length + card.properties[boardTree.groupByProperty.id] = option.id + card.title = `Test Card ${startCount + i + 1}` + card.icon = BlockIcons.shared.randomIcon() + } + await mutator.insertBlock(card, 'test add card') + } + } + + private async testRandomizeIcons() { + const {boardTree} = this.props + + for (const card of boardTree.cards) { + mutator.changeIcon(card, BlockIcons.shared.randomIcon(), 'randomize icon') + } + } + + render(): JSX.Element { + const {boardTree, showView, withGroupBy} = this.props + const {board, activeView} = boardTree + + const hasFilter = activeView.filter && activeView.filter.filters?.length > 0 + const hasSort = activeView.sortOptions.length > 0 + + return ( +
+ { + mutator.changeTitle(activeView, text) + }} + /> + +
+
+
+ + +
+ +
+ +
+ + {boardTree.board.cardProperties.map((option) => ( + { + const property = boardTree.board.cardProperties.find((o) => o.id === propertyId) + Utils.assertValue(property) + Utils.log(`Toggle property ${property.name}`) + + let newVisiblePropertyIds = [] + if (activeView.visiblePropertyIds.includes(propertyId)) { + newVisiblePropertyIds = activeView.visiblePropertyIds.filter((o) => o !== propertyId) + } else { + newVisiblePropertyIds = [...activeView.visiblePropertyIds, propertyId] + } + mutator.changeViewVisibleProperties(activeView, newVisiblePropertyIds) + }} + /> + ))} + +
+ {withGroupBy && + +
+ Group by {boardTree.groupByProperty?.name} +
+ + {boardTree.board.cardProperties.filter((o) => o.type === 'select').map((option) => ( + { + if (boardTree.activeView.groupById === id) { + return + } + + mutator.changeViewGroupById(boardTree.activeView, id) + }} + /> + ))} + +
} +
+ + {this.state.showFilter && + } +
+ +
+ +
+ + {boardTree.board.cardProperties.map((option) => ( + { + let newSortOptions: ISortOption[] = [] + if (activeView.sortOptions[0] && activeView.sortOptions[0].propertyId === propertyId) { + // Already sorting by name, so reverse it + newSortOptions = [ + {propertyId, reversed: !activeView.sortOptions[0].reversed}, + ] + } else { + newSortOptions = [ + {propertyId, reversed: false}, + ] + } + mutator.changeViewSortOptions(activeView, newSortOptions) + }} + /> + ))} + +
+ {this.state.isSearching && + + {(placeholder: string) => ( + { + this.searchChanged(text) + }} + onKeyDown={(e) => { + this.onSearchKeyDown(e) + }} + /> + )} + } + {!this.state.isSearching && +
{ + this.setState({...this.state, isSearching: true}) + }} + > + +
} + +
+ + CsvExporter.exportTableCsv(boardTree)} + /> + Archiver.exportBoardTree(boardTree)} + /> + this.testAddCards(100)} + /> + this.testAddCards(1000)} + /> + this.testRandomizeIcons()} + /> + + +
{ + this.props.addCard(true) + }} + > + +
+
+ ) + } +}