<repo_name>Zanluca/challenge-bossabox<file_sep>/src/components/Modal/ModalAddTool.js
import React, {useState, useEffect} from 'react'
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'

import { ModalContainer, ModalTitle, ModalBody, ModalFooter, LabelInput} from './style'
import {Button} from '../Button'
import {InputText, InputTextLarge} from '../InputText'
import BaseModal from './BaseModal'
import close from '../../assets/Close.svg'

const defaultTool = {
    description : '',
    link : '',
    tags: [],
    title : '',
}

function ModalAddTool(props) {
    const [tool, setTool] = useState(defaultTool)
    const [tagsStr, setTagsStr] = useState('')
    
    useEffect(() => {        
        setTool(defaultTool)
        setTagsStr('')
    }, [props.show])

    function handleClose() {
        setTool(defaultTool)
        setTagsStr('')
        props.close()
    }

    function handleTitleType(e) {
        setTool({
            ...tool, 
            title : e.target.value
        })
    }

    function handleLinkType(e) {
        setTool({
            ...tool, 
            link : e.target.value
        })
    }

    function handleDescriptionType(e) {
        setTool({
            ...tool, 
            description : e.target.value
        })
    }

    function handleTagsType(e) {
        const tags = e.target.value.split(' ')
        setTagsStr(e.target.value)
        setTool({
            ...tool, 
            tags
        })
    }

    return (
        <BaseModal testeID='modal-add' onClose={props.close} show={props.show}>
            <ModalContainer>
                <ModalTitle>
                    <span><FontAwesomeIcon icon='plus'/> Add new tool</span>
                    <img src={close} alt='close' onClick={handleClose} />
                </ModalTitle>
                <ModalBody>
                    <LabelInput>Tool Name</LabelInput>
                    <InputText aria-label='tool-name' value={tool.title} onChange={handleTitleType}/>

                    <LabelInput>Tool Link</LabelInput>
                    <InputText aria-label='tool-link' value={tool.link} onChange={handleLinkType}/>

                    <LabelInput>Tool Description</LabelInput>
                    <InputTextLarge aria-label='tool-description' rows="6" cols="54" value={tool.description} onChange={handleDescriptionType}/>

                    <LabelInput>Tool Tags</LabelInput>
                    <InputText aria-label='tool-tags' value={tagsStr} onChange={handleTagsType}/>
                </ModalBody>
                <ModalFooter>
                <Button aria-label='button-add-tool' primary neutral onClick={() => props.addTool(tool)}>Add tool</Button>
                </ModalFooter>
            </ModalContainer>
        </BaseModal>
    )
}

export default ModalAddTool<file_sep>/src/components/App/style.js
import styled, {createGlobalStyle} from "styled-components";

const Container = styled.div`
    margin-top: 5em;
    display: flex;
    flex-direction: column;
    align-items: center;
    justify-content : center;
    flex-wrap: wrap;
`

const AppContent = styled.div`
    h1 {
        font-size: 4em;
    }

    h2 {
        font-size: 3.17em;
    }

    input[type=checkbox] {
        position: absolute;
        opacity: 0;
    }

    input[type=checkbox] + label {
        position: relative;
        cursor: pointer;
        padding: 0;    
    }

    input[type=checkbox] + label::before {
        content: '';
        margin-right: 5px;
        display: inline-block;
        vertical-align: middle;
        width: 15px;
        height: 15px;
        background: #F5F4F6;
        border: 1px solid #DEDCE1;
    }

    input[type=checkbox]:checked + label::before {    
        background: #365DF0;
        border: 1px solid #365DF0;
    }

    input[type=checkbox]:checked + label::after {    
        content: '';
        position: absolute;
        background: white;
        transform: rotate(45deg);
        left: 3px;
        top: 10px;
        width: 2px;
        height: 2px;
        box-shadow: 
            2px 0 0 white,
            4px 0 0 white,
            4px -2px 0 white,
            4px -4px 0 white,
            4px -6px 0 white,
            4px -8px 0 white;
    }

`

const Grid = styled.div`
    margin-top : 60px;
    display: grid;
    grid-template-columns: 2.3fr 1fr 0fr;
    grid-gap : 10px;

    span{
        margin-top: 15px;
    }
`

const GlobalStyle = createGlobalStyle`
    * {
        margin: 0;
        padding: 0;
        outline: 0;
        box-sizing: border-box;
    }

    html, body, #root {
        height: 100%;
    }

    body, input, button {
        font-family: 'Source Sans Pro', Semibold;
    }
`

export {
    Container,
    AppContent,
    GlobalStyle,
    Grid
}<file_sep>/src/components/Card/style.js
import styled from "styled-components";

const CardContainer = styled.div`
    background-color: #FFFFFF;
    border: 1px solid #EBEAED;
    padding: 16px;
    box-shadow: 0 10px 10px rgba(0, 0, 0, .05);
    text-align: left;
    outline: 1px solid #ddd;
    width : 100%;
    max-width: 950px;
    margin-bottom: 1.5em;


    *{
        font-size : 18px;
    }

    a{
        font-size : 22px;
    }
`

const CardTitleContainer = styled.div`    
    margin-bottom : 1em;
    display: grid;
    grid-template-columns: 10fr 1fr;
`

const CardBody = styled.div`
    margin-bottom: 2.5em;
`

const CardFooter = styled.div`
    font-weight: bold;
    margin-bottom: 1em;

    mark {
        padding: 0;
        background-color: #ffd703;
    }
`

export {
    CardContainer,
    CardTitleContainer,
    CardBody,
    CardFooter
}<file_sep>/src/__test__/ModalRemove.test.js
import React from 'react';
import ReactDOM from 'react-dom';

import ModalRemove from '../components/Modal/ModalRemove'

import { cleanup } from "@testing-library/react";

afterEach(cleanup);

test('renders without crashing ModalRemove', () => {
    const div = document.createElement('div');
    ReactDOM.render(<ModalRemove />, div);
    ReactDOM.unmountComponentAtNode(div);
});<file_sep>/src/components/App/App.js
import React, { useState, useEffect, Fragment } from 'react'

import { library } from '@fortawesome/fontawesome-svg-core'
import { fab } from '@fortawesome/free-brands-svg-icons'
import { faPlus, faTimes } from '@fortawesome/free-solid-svg-icons'
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'

import { getTool, addTool, deleteTool } from '../../service/serviceTools'
import useDebounce from '../../utils/use-debounce'

import { Container, AppContent, GlobalStyle, Grid } from './style'
import { Button } from '../Button/'
import { InputText } from '../InputText'
import CardList from '../../components/CardList'
import ModalRemove from '../../components/Modal/ModalRemove'
import ModalAddTool from '../../components/Modal/ModalAddTool'

library.add(fab, faPlus, faTimes)

function App() {
  const [listTools, setListTools] = useState([])
  const [showRemove, setShowRemove] = useState(false)
  const [showAdd, setShowAdd] = useState(false)
  const [selectedTool, setSelectedTool] = useState(null)
  const [searchString, setSearchString] = useState('')
  const [isSearchOnlyTags, setIsSearchOnlyTags] = useState(false)
  const debounceSearchTerm = useDebounce(searchString, 500)

  /** 
   * Responsável por fazer a busca da lista de ferramentas quando a página é carregada.
   */
  useEffect(() => {
    async function getData() {
      const tools = await getTool()
      if (tools)
        setListTools(tools)
    }
    getData()

  }, [])

  /** 
   * Responsável por fazer a busca da lista de ferramentas filtrada somente pelo texto ou pelas tags. 
   */
  useEffect(() => {
    async function getData() {
      let tools
      if ((isSearchOnlyTags) && (debounceSearchTerm.trim() !== ''))
        tools = await getTool(
          {queryTag : debounceSearchTerm}
        )
      else if (debounceSearchTerm.trim() !== '')
        tools = await getTool(
          {queryString : debounceSearchTerm}        
        )  
      else
        tools = await getTool()   
      if (tools)
        setListTools(tools)
    }

    getData()

  }, [debounceSearchTerm, isSearchOnlyTags])

  function handleShowRemove(tool) {
    setShowRemove(true)
    setSelectedTool(tool)
  }

  function handleCloseRemove() {
    setShowRemove(false)
    setSelectedTool(null)
  }

  async function handleRemoveTool() {
    setShowRemove(false)
    const deletedOK = await deleteTool(selectedTool.id)
    setSelectedTool(null)
    if (deletedOK)
      setListTools(listTools.filter(tool => tool.id !== selectedTool.id))
  }

  async function handleAddTool(tool) {
    const data = await addTool(tool)
    setShowAdd(false)
    if (data)
      setListTools([...listTools, data])
  }

  function handleCloseAddTool() {
    setShowAdd(false)
  }

  function handleShowAddTool() {
    setShowAdd(true)
  }

  return (
    <Fragment>
      <GlobalStyle />
      <Container>
        <AppContent>
          <h1 data-testid='title'>VUTTR</h1>
          <h3>Very Useful Tools to Remember</h3>
          <Grid>
            <InputText value={searchString} type='text' placeholder='&#xF002; search' onChange={e => setSearchString(e.target.value)} />
            <span>
              <input id='search-by-tag' type='checkbox' defaultChecked={isSearchOnlyTags} onChange={e => setIsSearchOnlyTags(e.target.checked)} />
              <label htmlFor='search-by-tag'>search in tags only</label>
            </span>
            <Button data-testid='button-add' primary neutral onClick={handleShowAddTool}><FontAwesomeIcon icon='plus' /> Add</Button>
          </Grid>
          <CardList tools={listTools} handleShowRemove={handleShowRemove} highlight = {debounceSearchTerm} />
        </AppContent>
        {selectedTool &&
          <ModalRemove
            show={showRemove}
            close={handleCloseRemove}
            name={selectedTool.title}
            remove={handleRemoveTool}
          />
        }
        <ModalAddTool show={showAdd} addTool={handleAddTool} close={handleCloseAddTool} />
      </Container>
    </Fragment>

  );
}

export default App;
<file_sep>/src/components/CardList/index.js
import React from 'react'
import List from './style'
import Card from '../Card'

function CardList({
    tools = [], 
    handleShowRemove = () => {},
    highlight = ''
}) {


    return (
        <List data-testid='card-list'>
            {tools.map((tool) => (
                <Card data-testid='card' key={tool.id}
                  tool={tool}
                  showRemove={handleShowRemove}
                  highlight={highlight} />
              ))}
        </List>
    )
}

export default CardList<file_sep>/src/__test__/Input.test.js
import React from 'react';
import ReactDOM from 'react-dom';

import { InputText } from '../components/InputText'

import { cleanup } from "@testing-library/react";

afterEach(cleanup);

test('renders without crashing InputText', () => {
    const div = document.createElement('div');
    ReactDOM.render(<InputText />, div);
    ReactDOM.unmountComponentAtNode(div);
});<file_sep>/src/service/serviceTools.js
import axios from 'axios'
const API_PATH = 'http://localhost:3000/'

const getTool = async ({
    queryString = '',
    queryTag = ''
} = {}) => {
    let query = ''

    if (queryString)
        query = `?q=${queryString}`
    else if (queryTag)
        query = `?tags_like=${queryTag}`

    try {
        const response = await axios.get(API_PATH + `tools${query}`)
        if (response.status === 200)
            return response.data
        else {
            return undefined
        }
    }
    catch (error) {
        return undefined
    }
}

const addTool = async (tool)  =>{
    try{
        const response = await axios.post(API_PATH + 'tools', 
            tool
        , {
            headers : {
                'Content-Type': 'application/json'
            }
        })
        if (response.status === 201) {
            return response.data
        } 
        else {
            return undefined   
        }
    }
    catch(error) {
        return undefined
    }
}

const deleteTool = async (toolID) => {
    try {
        const response = await axios.delete(API_PATH + `tools/${toolID}`)

        if (response.status === 200)
            return true
        else {
            return undefined     
        }
    }
    catch(error) {
        return undefined
    }
}

export {
    getTool,
    addTool,
    deleteTool
} <file_sep>/README.md
# VUTTR (Very Useful Tools to Remember)
> Desafio front-end BossaBox.

O desafio consistia em implementar um VUTTR (Very Useful Tools to Remember). Para isso deveria seguir as seguintes instruções: [https://www.notion.so/Dev-Front-End-7e048c1d54274a15b26e50f2a4d52d6c](https://www.notion.so/Dev-Front-End-7e048c1d54274a15b26e50f2a4d52d6c)

## Instalação

Para instalar o projeto use uma das opções
```sh
npm install
```
ou
```sh
yarn
```

## Exemplo de uso
Para utilizar a aplicação precisa-se da seguinte [API](https://gitlab.com/bossabox/challenge-fake-api/tree/master) ela é o back-end da aplicação

Para iniciar use um dos comandos:
```sh
npm start
```
ou
```sh
yarn start
```

A aplicação deve rodar numa porta diferente da 3000 pois essa já é utilizada pela API. Ao rodar um dos comandos acima ele irá sugerir um porta disponível.
Para executar os testes basta rodar um dos comandos:
```sh
npm test
```
ou
```sh
yarn test
```
<file_sep>/src/components/InputText/index.js
import styled from "styled-components";

export const InputText = styled.input`    
    width: 100%;
    height: 50px;

    background: #F5F4F6;
    border: 1px solid #EBEAED;
    border-radius: 5px;
    opacity: 1;

    text-align: left;
    font: 20px/26px Source Sans Pro, FontAwesome ;
    letter-spacing: 0.4px;
    font-size: 20px;
    padding: 13px 22.1px 12px;
    color: #170C3A;
    margin-right : 0.5em;
    

    ::placeholder {
        color: #B1ADB9;
    }

    :focus {
        background: #EBEAED;
        border: 1px solid #DEDCE1;

        letter-spacing: 0;
    }

    :focus::placeholder {
        color: #8F8A9B;
    }
`

export const InputTextLarge = styled.textarea`
    resize : none;
    width: 100%;

    background: #F5F4F6;
    border: 1px solid #EBEAED;
    border-radius: 5px;
    opacity: 1;

    text-align: left;
    font: 20px/26px Source Sans Pro, FontAwesome ;
    letter-spacing: 0.4px;
    font-size: 20px;
    padding: 12.48px 22.1px;
    color: #170C3A;
    margin-right : 0.5em;
    

    ::placeholder {
        color: #B1ADB9;
    }

    :focus {
        background: #EBEAED;
        border: 1px solid #DEDCE1;

        letter-spacing: 0;
    }

    :focus::placeholder {
        color: #8F8A9B;
    }
`<file_sep>/src/__mocks__/axios.js

export default {
    get: jest.fn().mockResolvedValue({
        data: {},
      }),

    post : jest.fn().mockResolvedValue({
        status : 201,
        data: {}
    }),

    delete : jest.fn().mockResolvedValue({
      status : 200,
      data : {}
    }) 
  };<file_sep>/src/components/Modal/style.js
import styled from "styled-components"

const ModaBackground = styled.div`
    position: fixed;
    top : 0;
    left: 0;
    bottom: 0;
    right: 0;
    display: ${props => props.show ? 'flex' : 'none'};
    flex-direction: column;
    justify-content: center;
    align-items: center;
    background: rgba(0,0,0, 0.8);
    overflow : hidden;
`

const ModalContainer = styled.div`
    width: 600px;
    /* height: 332px; */
    background: #FFFFFF;
    box-shadow: 0px 20px 25px #000000;
    border-radius: 5px;
    padding: 30px 30.5px;
`

const ModalTitle = styled.div`
    display: grid;
    grid-template-columns: 16fr 0fr;

    span {
        font-size : 1.5em;
    }

    img {
        cursor: pointer;
    }
`

const ModalBody = styled.div`
    text-align: left;
    font: 20px/26px Source Sans Pro;
    letter-spacing: 0.4px;
    color: #8F8A9B;
    opacity: 1;
    margin-top: 30px
`

const ModalFooter = styled.div`
    display: flex;
    flex-direction: row-reverse;
    margin-top: 30px;

    button{
        margin-left : 1em
    }
`

const LabelInput = styled.label`
    display: block;
    margin-top: 20px;
    margin-bottom: 10px;
`

export {
    ModaBackground,
    ModalContainer,
    ModalTitle,
    ModalBody,
    ModalFooter,
    LabelInput
}<file_sep>/src/__test__/App.test.js
import React from 'react';
import ReactDOM from 'react-dom';

import '@testing-library/jest-dom/extend-expect'
import {
  render,
  fireEvent,
  cleanup,
  within,
  waitForElement,
} from "@testing-library/react";

import App from '../components/App/App';

import axiosMock from 'axios'
import result from './data/sample-data.json'
import { act } from 'react-dom/test-utils';

afterEach(cleanup);

test('renders without crashing App', () => {
  const div = document.createElement('div');
  ReactDOM.render(<App />, div);
  ReactDOM.unmountComponentAtNode(div);
});

test('fetches and display data (cards)', async () => {
  axiosMock.get.mockResolvedValueOnce({
    status: 200,
    data: result,
  })

  await act(async () => {
    const { getByTestId } = render(<App />)

    const cardList = getByTestId('card-list')

    const cards = await waitForElement(() =>
      within(cardList).getAllByTestId('card')
    )

    expect(cards.length).toBe(6)
  })
})

test('add new tool', async () => {  
  axiosMock.get.mockResolvedValueOnce({
    status: 200,
    data: result,
  })
  
  const newTool = {
    description: 'Promise based HTTP client for the browser and node.js',
    link: 'https://github.com/axios/axios',
    tags: ['node', 'http', 'promisse'],
    title: 'axios',
    id: 9999
  }
  axiosMock.post.mockResolvedValueOnce({
    status: 201,
    data: newTool
  })

  await act(async () => {
    const { getByTestId } = render(<App />)

    const btnAddNewTool = getByTestId('button-add')

    fireEvent.click(btnAddNewTool)

    const modalAdd = await waitForElement(() => getByTestId('modal-add'))

    expect(modalAdd).toHaveStyle('display : flex')

    const inputName = within(modalAdd).getByLabelText('tool-name')
    fireEvent.change(inputName, { target: { value: newTool.title } })

    const inputLink = within(modalAdd).getByLabelText('tool-link')
    fireEvent.change(inputLink, { target: { value: newTool.link } })

    const inputDescription = within(modalAdd).getByLabelText('tool-description')
    fireEvent.change(inputDescription, { target: { value: newTool.description } })

    const inputTags = within(modalAdd).getByLabelText('tool-tags')
    const tagsStr = newTool.tags.reduce(((tags, tag) => `${tags} ${tag}`))
    fireEvent.change(inputTags, { target: { value: tagsStr } })

    const btnAddTool = within(modalAdd).getByLabelText('button-add-tool')
    fireEvent.click(btnAddTool)

    const cardList = await waitForElement(() =>
      getByTestId('card-list'))

    const cards = await waitForElement(() =>
      within(cardList).getAllByLabelText('tool-name')
    )
    const allNames = cards.map(name => name.textContent)

    expect(allNames).toContain(newTool.title)
  })
})

test('remove tool', async () => {
  axiosMock.get.mockResolvedValueOnce({
    status: 200,
    data: result,
  })

  await act(async () => {
    const { getByTestId } = render(<App />)
    const cardList = getByTestId('card-list')

    const cards = await waitForElement(() =>
      within(cardList).getAllByTestId('card')
    )

    const nameTool = within(cards[0]).getByLabelText('tool-name')
    const removeButton = within(cards[0]).getByLabelText('button-remove')
    fireEvent.click(removeButton)

    const modalRemove = await waitForElement(() =>
      getByTestId('modal-remove')
    )
    expect(modalRemove).toHaveStyle('display : flex')

    const btnRemove = within(modalRemove).getByLabelText('button-yes-remove')
    fireEvent.click(btnRemove)

    const modalRemoveAfterRemove = await waitForElement(() =>
      getByTestId('modal-remove')
    )
    expect(modalRemoveAfterRemove).toHaveStyle('display : none')

    const cardListNew = await waitForElement(() =>
      getByTestId('card-list')
    )
    const cardsNames = await waitForElement(() =>
      within(cardListNew).getAllByLabelText('tool-name')
    )

    const allNames = cardsNames.map(name => name.textContent)
    expect(allNames).not.toContain(nameTool.textContent)
  })
})

test('cancel remove tool ', async () => {
  axiosMock.get.mockResolvedValueOnce({
    status: 200,
    data: result,
  })

  await act(async () => {
    const { getByTestId, queryByText } = render(<App />)
    const cardList = getByTestId('card-list')
    
    const cards = await waitForElement(() =>
      within(cardList).getAllByTestId('card')
    )

    const cardsNames = await waitForElement(() =>
      within(cardList).getAllByLabelText('tool-name')
    )

    const allNames = cardsNames.map(name => name.textContent)

    const removeButton = within(cards[0]).getByLabelText('button-remove')
    fireEvent.click(removeButton)

    const modalRemove = await waitForElement(() =>
      getByTestId('modal-remove')
    )
    expect(modalRemove).toHaveStyle('display : flex')

    const btnCancelRemove = within(modalRemove).getByLabelText('button-cancel-remove')
    fireEvent.click(btnCancelRemove)
    
    expect(queryByText('Remove tool')).not.toBeInTheDocument()

    const cardListNew = await waitForElement(() =>
      getByTestId('card-list')
    )
    const cardsNamesNew = await waitForElement(() =>
      within(cardListNew).getAllByLabelText('tool-name')
    )

    const allNamesNew = cardsNamesNew.map(name => name.textContent)
    expect(allNames).toEqual(allNamesNew)
  })
})<file_sep>/src/components/Button/index.js
import styled from "styled-components";

const Button = styled.button`
    display: inline-block;  
    border-radius: 5px;
    padding: ${props => ((props.primary) || (props.secondary)) ? '14px 26px 13px 26px' : '0'};
    height: ${
        props => {
            if ((props.primary) || (props.secondary))
                return '50px'
            else if (props.terciary)
                return '35px'
            else
                return '0'
        }
    };
    ${props => (!props.quartiary ? 'width: 174px' : '')} ;    
    font-size: 18px;
    border: 1px solid transparent;
    cursor: pointer; 
    background-color: ${
        props => {
            if (props.primary) {
                if (props.danger) 
                    return '#F95E5A'
                else if (props.success)
                    return '#0DCB7D'    
                else if (props.neutral)
                    return '#365DF0'
                else
                    return 'transparent'
            }
            else if ((props.secondary) || (props.terciary)) {
                if (props.danger) 
                    return '#FEEFEE'
                else if (props.success)
                    return '#E7FBF3'    
                else if (props.neutral)
                    return '#E1E7FD'
                else
                    return 'transparent'
            }
            else
                return 'transparent'
        }
    };    
    color: ${
        props => {
            if (props.primary) {
                return 'white'
            }
            else if ((props.secondary) || (props.terciary) || (props.quartiary)) {
                if (props.danger) 
                    return '#F95E5A'
                else if (props.success)
                    return '#12DB89'    
                else if (props.neutral)
                    return '#365DF0'
                else 
                    return 'black'  
            }
            else
                return 'black'
        }
    };

    :hover {
        background-color : ${
            props => {
                if (props.primary) {
                    if (props.danger) 
                        return '#CC4C4C'
                    else if (props.success)
                        return '#10B26C'    
                    else if (props.neutral)
                        return '#2F55CC'
                    else
                        return 'transparent'
                }
                else if ((props.secondary) || (props.terciary)) {
                    if (props.danger) 
                        return '#FCD7D6'
                    else if (props.success)
                        return '#CFF9E6'    
                    else if (props.neutral)
                        return '#CAD6FC'
                    else
                        return 'transparent'
                }
                else
                    return 'transparent'
            }
        }
    }

    :active {
        background-color : ${
            props => {
                if (props.primary) {
                    if (props.danger) 
                        return '#A53F3F'
                    else if (props.success)
                        return '#0E995D'    
                    else if (props.neutral)
                        return '#244AA8'
                    else
                        return 'transparent'
                }
                else if ((props.secondary) || (props.terciary)) {
                    if (props.danger) 
                        return '#FCC6C5'
                    else if (props.success)
                        return '#B7F7D8'    
                    else if (props.neutral)
                        return '#B9C6FA'
                    else
                        return 'transparent'
                }
                else
                    return 'transparent'
            }
        };
    }

    :disabled {
        ${
            props => {
                if (props.primary) {
                    if (props.danger) 
                        return 'background: #FCAEAC; ' +
                                'color : #FEEFEE;'
                    else if (props.success)
                        return 'background: #88EDC4; ' +
                                'color : #E7FBF3;'    
                    else if (props.neutral)
                        return 'background: #B9C6FA; ' +
                                'color : #E1E7FD;'
                    else
                        return 'transparent'
                }
                else if ((props.secondary) || (props.terciary)) {
                    if (props.danger)  
                        return (props.quartiary ? 'background: #FEEFEE; ' : '') +
                                'color : #FCAEAC;'
                    else if (props.success)
                        return (props.quartiary ? 'background: #E7FBF3; ' : '') +
                                'color : #88EDC4;'    
                    else if (props.neutral)
                        return (props.quartiary ? 'background: #E1E7FD; ' : '') +
                                'color : #B9C6FA;'
                    else
                        return 'transparent'
                }
                else
                    return 'transparent'
            }
        };
    }
`

export {
    Button    
}<file_sep>/src/components/Modal/BaseModal.js
import React, {useRef, useEffect } from 'react'
import { ModaBackground } from './style'

function BaseModal({
    onClose = () => { },
    show = false,
    children = undefined,
    testeID = ''
}) {
    const refBackground = useRef(null)

    useEffect(() => {
        const body = document.body
        if (show) {
            body.style.height = '100vh';
            body.style.overflowY = 'hidden';
        }
        else {            
            body.style.overflowY = 'auto';
        }

        return () => {
            const body = document.body
            body.style.overflowY = 'auto';
        };
    }, [show])

    function handleBackgroungClick(e) {
        if (e.target === refBackground.current) {
            onClose()
        }
    }

    return (
        <ModaBackground data-testid={testeID} ref={refBackground} onClick={(e) => handleBackgroungClick(e)} show={show}> 
            {children}
        </ModaBackground>
    )
}

export default BaseModal<file_sep>/src/components/Card/index.js
import React from 'react'
import { CardContainer, CardTitleContainer, CardBody, CardFooter} from './style'
import {Button} from '../Button/'
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'

const defaultTool = {
  description : '',
  link : '',
  tags: [],
  title : '',
}

function Card({
  tool = defaultTool,
  showRemove = () => {},
  highlight = ''
}) {

  /**
   *Destaca a tag que corresponte exatamento com o termo que está sendo buscado.
   */
  function highlightTag(tag, key) {
    if (tag === highlight)
      return <span key={key}><mark>#{tag}</mark> </span>
    else
      return <span key={key}>#{tag} </span>
  }

  return (
    <CardContainer data-testid='card'>
      <CardTitleContainer>
        <span aria-label='tool-name'><a href={tool.link}>{tool.title}</a></span>        
        <span><Button aria-label='button-remove' quartiary danger onClick={() => showRemove(tool)}><FontAwesomeIcon icon = 'times'/> remove</Button></span>    
      </CardTitleContainer>
      <CardBody aria-label='tool-description'>
        {tool.description} 
      </CardBody>
      <CardFooter aria-label='tool-tags'>
        {tool.tags.map((tag, index) => highlightTag(tag, index))}
      </CardFooter>
    </CardContainer>
  )
}

export default Card